diff --git a/CMakeLists.txt b/CMakeLists.txt index d7508741..bc125b7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,12 +49,12 @@ else() endif() # Determine if build mode -if(CMAKE_BUILD_TYPE MATCHES "(Debug)+") +if(${CMAKE_BUILD_TYPE} MATCHES "(Debug)+") add_compile_options(-g) endif() # Enable position independent code -if(UNIX) +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() diff --git a/module/CMakeLists.txt b/module/CMakeLists.txt index b1393edb..a4b04090 100644 --- a/module/CMakeLists.txt +++ b/module/CMakeLists.txt @@ -64,6 +64,7 @@ add_library(SqModule MODULE SqBase.hpp Main.cpp Library/IO/Buffer.cpp Library/IO/Buffer.hpp Library/IO/File.cpp Library/IO/File.hpp Library/IO/INI.cpp Library/IO/INI.hpp + Library/MMDB.cpp Library/MMDB.hpp Library/Numeric.cpp Library/Numeric.hpp Library/Numeric/Long.cpp Library/Numeric/Long.hpp Library/Numeric/Math.cpp Library/Numeric/Math.hpp @@ -105,7 +106,7 @@ if(WIN32 OR MINGW) target_link_libraries(SqModule wsock32 ws2_32 shlwapi) endif() # Link to base libraries -target_link_libraries(SqModule Squirrel FmtLib SimpleINI TinyDir ConcurrentQueue cpr::cpr) +target_link_libraries(SqModule Squirrel FmtLib SimpleINI TinyDir ConcurrentQueue cpr::cpr maxminddb::maxminddb) # Link to POCO libraries target_link_libraries(SqModule Poco::Foundation Poco::Encodings Poco::Crypto Poco::Util Poco::Data Poco::Net Poco::JSON Poco::XML Poco::Zip Poco::JWT Poco::Redis Poco::MongoDB) # Does POCO have SQLite support? @@ -132,7 +133,7 @@ if(POSTGRESQL_FOUND) target_compile_definitions(SqModule PRIVATE SQMOD_POCO_HAS_POSTGRESQL=1) endif() # Determine if build mode -if(CMAKE_BUILD_TYPE MATCHES "(Release)+") +if(${CMAKE_BUILD_TYPE} MATCHES "(Release)+") target_compile_definitions(SqModule PRIVATE NDEBUG=1) else() target_compile_definitions(SqModule PRIVATE _DEBUG=1 SQMOD_EXCEPTLOC=1) diff --git a/module/Library/MMDB.cpp b/module/Library/MMDB.cpp new file mode 100644 index 00000000..b92440cf --- /dev/null +++ b/module/Library/MMDB.cpp @@ -0,0 +1,1611 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/MMDB.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +// N[0] - contains least significant bits, N[3] - most significant +static SQChar * Bin128ToDec(const uint32_t N[4]) +{ + // log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322 + static SQChar s[128 / 3 + 1 + 1]; + uint32_t n[4]; + SQChar * p = s; + int i; + + std::memset(s, '0', sizeof(s) - 1); + s[sizeof(s) - 1] = '\0'; + + std::memcpy(n, N, sizeof(n)); + + for (i = 0; i < 128; i++) + { + int j, carry; + + carry = (n[3] >= 0x80000000); + // Shift n[] left, doubling it + n[3] = ((n[3] << 1u) & 0xFFFFFFFFu) + (n[2] >= 0x80000000u); + n[2] = ((n[2] << 1u) & 0xFFFFFFFFu) + (n[1] >= 0x80000000u); + n[1] = ((n[1] << 1u) & 0xFFFFFFFFu) + (n[0] >= 0x80000000u); + n[0] = ((n[0] << 1u) & 0xFFFFFFFFu); + + // Add s[] to itself in decimal, doubling it + for (j = sizeof(s) - 2; j >= 0; j--) + { + s[j] += s[j] - '0' + carry; + + carry = (s[j] > '9'); + + if (carry) + { + s[j] -= 10; + } + } + } + + while ((p[0] == '0') && (p < &s[sizeof(s) - 2])) + { + p++; + } + + return p; +} + +// ------------------------------------------------------------------------------------------------ +const SQChar * AsTypeStr(uint32_t id) +{ + switch (id) + { + case MMDB_DATA_TYPE_EXTENDED: return _SC("extended"); + case MMDB_DATA_TYPE_POINTER: return _SC("pointer"); + case MMDB_DATA_TYPE_UTF8_STRING: return _SC("string"); + case MMDB_DATA_TYPE_DOUBLE: return _SC("double"); + case MMDB_DATA_TYPE_BYTES: return _SC("bytes"); + case MMDB_DATA_TYPE_UINT16: return _SC("uint16"); + case MMDB_DATA_TYPE_UINT32: return _SC("uint32"); + case MMDB_DATA_TYPE_MAP: return _SC("map"); + case MMDB_DATA_TYPE_INT32: return _SC("int32"); + case MMDB_DATA_TYPE_UINT64: return _SC("uint64"); + case MMDB_DATA_TYPE_UINT128: return _SC("uint128"); + case MMDB_DATA_TYPE_ARRAY: return _SC("array"); + case MMDB_DATA_TYPE_CONTAINER: return _SC("container"); + case MMDB_DATA_TYPE_END_MARKER: return _SC("endmarker"); + case MMDB_DATA_TYPE_BOOLEAN: return _SC("boolean"); + case MMDB_DATA_TYPE_FLOAT: return _SC("float"); + default: return _SC("unknown"); + } +} + +// ------------------------------------------------------------------------------------------------ +bool GetEntryAsBool(const MMDB_entry_data_s & ed) +{ + bool value = false; + // Identify the type of entry data + switch (ed.type) + { + case MMDB_DATA_TYPE_POINTER: { + value = ed.pointer > 0; + } break; + case MMDB_DATA_TYPE_UTF8_STRING: { + if (ed.data_size > 0) + { + value = ConvTo< bool >::From(reinterpret_cast< const SQChar * >(ed.utf8_string)); + } + } break; + case MMDB_DATA_TYPE_DOUBLE: { + value = ConvTo< bool >::From(ed.double_value); + } break; + case MMDB_DATA_TYPE_BYTES: { + for (uint32_t i = 0; i < ed.data_size; ++i) + { + if (ed.bytes[i] != 0) + { + value = true; + // Found something that isn't 0 + break; + } + } + } break; + case MMDB_DATA_TYPE_UINT16: { + value = ConvTo< bool >::From(ed.uint16); + } break; + case MMDB_DATA_TYPE_UINT32: { + value = ConvTo< bool >::From(ed.uint16); + } break; + case MMDB_DATA_TYPE_INT32: { + value = ConvTo< bool >::From(ed.uint16); + } break; + case MMDB_DATA_TYPE_UINT64: { + value = ConvTo< bool >::From(ed.uint16); + } break; + case MMDB_DATA_TYPE_UINT128: { +#if defined(MMDB_UINT128_IS_BYTE_ARRAY) && (MMDB_UINT128_IS_BYTE_ARRAY == 1) + for (uint32_t i = 0; i < sizeof(ed.uint128); ++i) + { + if (ed.uint128[i] != 0) + { + value = true; + // Found somethinf that isn't 0 + break; + } + } +#else + value = ed.uint128 > 0; +#endif // MMDB_UINT128_IS_BYTE_ARRAY + + } break; + case MMDB_DATA_TYPE_BOOLEAN: { + value = ed.boolean; + } break; + case MMDB_DATA_TYPE_FLOAT: { + value = ConvTo< bool >::From(ed.float_value); + } break; + default: + STHROWF("Unsupported conversion from (%s) to (boolean)", AsTypeStr(ed.type)); + } + // Return the extracted value + return value; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger GetEntryAsInteger(const MMDB_entry_data_s & ed) +{ + SQInteger value = 0; + // Identify the type of entry data + switch (ed.type) + { + case MMDB_DATA_TYPE_POINTER: { + value = static_cast< SQInteger >(ed.pointer); + } break; + case MMDB_DATA_TYPE_UTF8_STRING: { + if (ed.data_size > 0) + { + value = ConvTo< SQInteger >::From(reinterpret_cast< const SQChar * >(ed.utf8_string)); + } + } break; + case MMDB_DATA_TYPE_DOUBLE: { + value = ConvTo< SQInteger >::From(ed.double_value); + } break; + case MMDB_DATA_TYPE_BYTES: { + std::memcpy(&value, ed.bytes, Clamp(ed.data_size, 0U, sizeof(value))); + } break; + case MMDB_DATA_TYPE_UINT16: { + value = ConvTo< SQInteger >::From(ed.uint16); + } break; + case MMDB_DATA_TYPE_UINT32: { + value = ConvTo< SQInteger >::From(ed.uint32); + } break; + case MMDB_DATA_TYPE_INT32: { + value = ConvTo< SQInteger >::From(ed.int32); + } break; + case MMDB_DATA_TYPE_UINT64: { + value = ConvTo< SQInteger >::From(ed.uint64); + } break; + case MMDB_DATA_TYPE_UINT128: { +#if defined(MMDB_UINT128_IS_BYTE_ARRAY) && (MMDB_UINT128_IS_BYTE_ARRAY == 1) + std::memcpy(&value, ed.uint128, sizeof(value)); +#else + std::memcpy(&value, &ed.uint128, sizeof(value)); +#endif // MMDB_UINT128_IS_BYTE_ARRAY + } break; + case MMDB_DATA_TYPE_BOOLEAN: { + value = ed.boolean ? 1 : 0; + } break; + case MMDB_DATA_TYPE_FLOAT: { + value = ConvTo< SQInteger >::From(ed.float_value); + } break; + default: + STHROWF("Unsupported conversion from (%s) to (integer)", AsTypeStr(ed.type)); + } + // Return the extracted value + return value; +} + +// ------------------------------------------------------------------------------------------------ +SQFloat GetEntryAsFloat(const MMDB_entry_data_s & ed) +{ + SQFloat value = 0.0; + // Identify the type of entry data + switch (ed.type) + { + case MMDB_DATA_TYPE_POINTER: { + value = ConvTo< SQFloat >::From(static_cast< SQInteger >(ed.pointer)); + } break; + case MMDB_DATA_TYPE_UTF8_STRING: { + if (ed.data_size > 0) + { + value = ConvTo< SQFloat >::From(reinterpret_cast< const SQChar * >(ed.utf8_string)); + } + } break; + case MMDB_DATA_TYPE_DOUBLE: { + value = ConvTo< SQFloat >::From(ed.double_value); + } break; + case MMDB_DATA_TYPE_BYTES: { + // Not our problem if the result will be junk! + std::memcpy(&value, ed.bytes, Clamp(ed.data_size, 0U, sizeof(value))); + } break; + case MMDB_DATA_TYPE_UINT16: { + value = ConvTo< SQFloat >::From(ed.uint16); + } break; + case MMDB_DATA_TYPE_UINT32: { + value = ConvTo< SQFloat >::From(ed.uint32); + } break; + case MMDB_DATA_TYPE_INT32: { + value = ConvTo< SQFloat >::From(ed.int32); + } break; + case MMDB_DATA_TYPE_UINT64: { + value = ConvTo< SQFloat >::From(ed.uint64); + } break; + case MMDB_DATA_TYPE_UINT128: { + SQInteger num; + // Convert to integer first +#if defined(MMDB_UINT128_IS_BYTE_ARRAY) && (MMDB_UINT128_IS_BYTE_ARRAY == 1) + std::memcpy(&num, ed.uint128, sizeof(num)); +#else + std::memcpy(&num, &ed.uint128, sizeof(num)); +#endif // MMDB_UINT128_IS_BYTE_ARRAY + // Now convert to float + value = ConvTo< SQFloat >::From(num); + } break; + case MMDB_DATA_TYPE_BOOLEAN: { + value = ed.boolean ? 1.0 : 0.0; + } break; + case MMDB_DATA_TYPE_FLOAT: { + value = ConvTo< SQFloat >::From(ed.float_value); + } break; + default: + STHROWF("Unsupported conversion from (%s) to (float)", AsTypeStr(ed.type)); + } + // Return the extracted value + return value; +} + +// ------------------------------------------------------------------------------------------------ +LightObj GetEntryAsLong(const MMDB_entry_data_s & ed) +{ + uint64_t value = 0; + // Identify the type of entry data + switch (ed.type) + { + case MMDB_DATA_TYPE_POINTER: { + value = static_cast< uint64_t >(ed.pointer); + } break; + case MMDB_DATA_TYPE_UTF8_STRING: { + if (ed.data_size > 0) + { + value = ConvTo< uint64_t >::From(reinterpret_cast< const SQChar * >(ed.utf8_string)); + } + } break; + case MMDB_DATA_TYPE_DOUBLE: { + value = ConvTo< uint64_t >::From(ed.double_value); + } break; + case MMDB_DATA_TYPE_BYTES: { + std::memcpy(&value, ed.bytes, Clamp(ed.data_size, 0U, sizeof(value))); + } break; + case MMDB_DATA_TYPE_UINT16: { + value = ConvTo< uint64_t >::From(ed.uint16); + } break; + case MMDB_DATA_TYPE_UINT32: { + value = ConvTo< uint64_t >::From(ed.uint32); + } break; + case MMDB_DATA_TYPE_INT32: { + value = ConvTo< uint64_t >::From(ed.int32); + } break; + case MMDB_DATA_TYPE_UINT64: { + value = ConvTo< uint64_t >::From(ed.uint64); + } break; + case MMDB_DATA_TYPE_UINT128: { +#if defined(MMDB_UINT128_IS_BYTE_ARRAY) && (MMDB_UINT128_IS_BYTE_ARRAY == 1) + std::memcpy(&value, ed.uint128, sizeof(value)); +#else + std::memcpy(&value, &ed.uint128, sizeof(value)); +#endif // MMDB_UINT128_IS_BYTE_ARRAY + } break; + case MMDB_DATA_TYPE_BOOLEAN: { + value = ed.boolean ? 1 : 0; + } break; + case MMDB_DATA_TYPE_FLOAT: { + value = ConvTo< uint64_t >::From(ed.float_value); + } break; + default: + STHROWF("Unsupported conversion from (%s) to (long)", AsTypeStr(ed.type)); + } + // Return a long integer instance with the requested value + return LightObj(SqTypeIdentity< ULongInt >{}, SqVM(), value); +} + +// ------------------------------------------------------------------------------------------------ +LightObj GetEntryAsString(const MMDB_entry_data_s & ed) +{ + // Obtain the initial stack size + const StackGuard sg; + // The default vm + HSQUIRRELVM vm = SqVM(); + // Identify the type of entry data + switch (ed.type) + { + case MMDB_DATA_TYPE_POINTER: { + sq_pushstring(vm, fmt::format("{:p}", ed.pointer).c_str(), -1); + } break; + case MMDB_DATA_TYPE_UTF8_STRING: { + sq_pushstring(vm, ed.utf8_string, ed.data_size); + } break; + case MMDB_DATA_TYPE_DOUBLE: { + sq_pushstring(vm, fmt::format("{}", ed.double_value).c_str(), -1); + } break; + case MMDB_DATA_TYPE_BYTES: { + sq_pushstring(vm, reinterpret_cast< const SQChar * >(ed.bytes), ed.data_size / sizeof(SQChar)); + } break; + case MMDB_DATA_TYPE_UINT16: { + sq_pushstring(vm, fmt::format("{}", ed.uint16).c_str(), -1); + } break; + case MMDB_DATA_TYPE_UINT32: { + sq_pushstring(vm, fmt::format("{}", ed.uint32).c_str(), -1); + } break; + case MMDB_DATA_TYPE_INT32: { + sq_pushstring(vm, fmt::format("{}", ed.int32).c_str(), -1); + } break; + case MMDB_DATA_TYPE_UINT64: { + sq_pushstring(vm, fmt::format("{}", ed.uint64).c_str(), -1); + } break; + case MMDB_DATA_TYPE_UINT128: { +#if defined(MMDB_UINT128_IS_BYTE_ARRAY) && (MMDB_UINT128_IS_BYTE_ARRAY == 1) + sq_pushstring(vm, Bin128ToDec(reinterpret_cast< const uint32_t * >(ed.uint128)), -1); +#else + sq_pushstring(vm, Bin128ToDec(reinterpret_cast< const uint32_t * >(&ed.uint128)), -1); +#endif // MMDB_UINT128_IS_BYTE_ARRAY + } break; + case MMDB_DATA_TYPE_BOOLEAN: { + sq_pushstring(vm, ed.boolean ? _SC("true") : _SC("false"), -1); + } break; + case MMDB_DATA_TYPE_FLOAT: { + sq_pushstring(vm, fmt::format("{}", ed.float_value).c_str(), -1); + } break; + default: + STHROWF("Unsupported conversion from (%s) to (string)", AsTypeStr(ed.type)); + } + // Obtain the object from the stack and return it + return LightObj(-1); +} + +// ------------------------------------------------------------------------------------------------ +LightObj GetEntryAsBytes(const MMDB_entry_data_s & ed) +{ + // Identify the type of entry data + switch (ed.type) + { + case MMDB_DATA_TYPE_POINTER: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.pointer), sizeof(ed.pointer), 0); + } + case MMDB_DATA_TYPE_UTF8_STRING: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(ed.utf8_string), ed.data_size, 0); + } + case MMDB_DATA_TYPE_DOUBLE: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.double_value), sizeof(ed.double_value), 0); + } + case MMDB_DATA_TYPE_BYTES: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(ed.bytes), ed.data_size, 0); + } + case MMDB_DATA_TYPE_UINT16: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.uint16), sizeof(ed.uint16), 0); + } + case MMDB_DATA_TYPE_UINT32: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.uint32), sizeof(ed.uint32), 0); + } + case MMDB_DATA_TYPE_INT32: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.int32), sizeof(ed.int32), 0); + } + case MMDB_DATA_TYPE_UINT64: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.uint64), sizeof(ed.uint64), 0); + } + case MMDB_DATA_TYPE_UINT128: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.uint128), sizeof(ed.uint128), 0); + } + case MMDB_DATA_TYPE_BOOLEAN: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.boolean), sizeof(ed.boolean), 0); + } + case MMDB_DATA_TYPE_FLOAT: { + return LightObj(SqTypeIdentity< SqBuffer >{}, SqVM(), + reinterpret_cast< const char * >(&ed.float_value), + sizeof(ed.float_value), 0); + } + default: + STHROWF("Unsupported conversion from (%s) to (buffer)", AsTypeStr(ed.type)); + } + // Return a null object (shouldn't reach here) + return LightObj{}; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger SockAddr::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMSockAddr"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void SockAddr::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid sockaddr structure handle =>[%s:%d]", file, line); + } +} +#else +void SockAddr::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid sockaddr structure handle"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +SockAddr::Pointer SockAddr::GetValid(CCStr file, int32_t line) const +{ + Validate(file, line); + return m_Handle; +} +#else +SockAddr::Pointer SockAddr::GetValid() const +{ + Validate(); + return m_Handle; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +SockAddr::SockAddr(const SQChar * addr) + : m_Handle(nullptr) +{ + struct addrinfo hints{}; + // Configure the hints structure + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + // We set ai_socktype so that we only get one result back + hints.ai_socktype = SOCK_STREAM; + // Attempt to obtain information about the specified address + int32_t status = getaddrinfo(addr, nullptr, &hints, &m_Handle); + // Validate the success of the operation + if (!status) + { + // See if we must free any handles (just in case) + if (m_Handle) + { + freeaddrinfo(m_Handle); + } + // Now it's safe to throw the error + STHROWF("Unable to query the specified address for information [%s]", gai_strerrorA(status)); + } + // Save the specified string address + m_Addres.assign(addr ? addr : _SC("")); +} + +// ------------------------------------------------------------------------------------------------ +SockAddr::~SockAddr() +{ + if (m_Handle) + { + freeaddrinfo(m_Handle); + } +} + +// ------------------------------------------------------------------------------------------------ +DbHnd::DbHnd(const SQChar * filepath, uint32_t flags) + : mDb() +{ + // Validate the specified file path + if (!filepath || *filepath == '\0') + { + STHROWF("Invalid database file path"); + } + // Let's attempt to open the specified database + const int32_t status = MMDB_open(filepath, flags, &mDb); + // Validate the result of the operation + if (status != MMDB_SUCCESS) + { + STHROWF("Unable to open the specified database [%s]", MMDB_strerror(status)); + } +} + +// ------------------------------------------------------------------------------------------------ +DbHnd::~DbHnd() +{ + // We don't need the database handle anymore + MMDB_close(&mDb); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger Database::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMDatabase"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void Database::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference =>[%s:%d]", file, line); + } +} +#else +void Database::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +const DbRef & Database::GetValid(CCStr file, int32_t line) const +{ + Validate(file, line); + return m_Handle; +} +#else +const DbRef & Database::GetValid() const +{ + Validate(); + return m_Handle; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +Metadata Database::GetMetadata() const +{ + return Metadata(m_Handle, &(SQMOD_GET_VALID(*this)->mDb).metadata); +} + +// ------------------------------------------------------------------------------------------------ +LightObj Database::GetMetadataAsEntryDataList() const +{ + MMDB_entry_data_list_s * entry_data_list = nullptr; + // Attempt to retrieve the database meta-data as an entry data list + const int status = MMDB_get_metadata_as_entry_data_list(&(SQMOD_GET_VALID(*this)->mDb), &entry_data_list); + // Validate the status code + if (status != MMDB_SUCCESS) + { + STHROWF("Unable to get meta-data entry data list [%s]", MMDB_strerror(status)); + } + // Return the resulted list + return LightObj(SqTypeIdentity< EntryDataList >{}, SqVM(), m_Handle, entry_data_list); +} + +// ------------------------------------------------------------------------------------------------ +LookupResult Database::LookupString(const SQChar * addr) +{ + // Validate the database handle + SQMOD_VALIDATE(*this); + // Validate the specified string + if (!addr || *addr == '\0') + { + STHROWF("Invalid address string"); + } + // Dummy variables to obtain the status codes + int gai_error, mmdb_error; + // Attempt to perform the actual lookup + MMDB_lookup_result_s result = MMDB_lookup_string(&m_Handle->mDb, addr, &gai_error, &mmdb_error); + // Validate the result of the getaddrinfo() function call + if (gai_error != 0) + { + STHROWF("Unable to resolve address (%s) because [%s]", addr, gai_strerrorA(gai_error)); + } + // Validate the lookup status code + else if (mmdb_error != MMDB_SUCCESS) + { + STHROWF("Unable to lookup address (%s) because [%s]", addr, MMDB_strerror(mmdb_error)); + } + // Now it's safe to return the lookup result + return LookupResult(m_Handle, result); +} + +// ------------------------------------------------------------------------------------------------ +LookupResult Database::LookupSockAddr(SockAddr & addr) +{ + // Validate the database handle + SQMOD_VALIDATE(*this); + // Validate the specified socket address + if (!addr.IsValid()) + { + STHROWF("Invalid address instance"); + } + // Dummy variable to obtain the status codes + int mmdb_error; + // Attempt to perform the actual lookup + MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&m_Handle->mDb, addr.GetHandle()->ai_addr, &mmdb_error); + // Validate the lookup status code + if (mmdb_error != MMDB_SUCCESS) + { + STHROWF("Unable to lookup address (%s) because [%s]", addr.GetAddress(), MMDB_strerror(mmdb_error)); + } + // Now it's safe to return the lookup result + return LookupResult(m_Handle, result); +} + +// ------------------------------------------------------------------------------------------------ +SearchNode Database::ReadNode(uint32_t node) const +{ + // Validate the database handle + SQMOD_VALIDATE(*this); + // Prepare a temporary search node + MMDB_search_node_s search_node; + // Attempt to retrieve the requested node from the database + const int status = MMDB_read_node(&(SQMOD_GET_VALID(*this)->mDb), node, &search_node); + // Validate the status code + if (status != MMDB_SUCCESS) + { + STHROWF("Unable to get node [%s]", MMDB_strerror(status)); + } + // Return the resulted list + return SearchNode(m_Handle, search_node); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger Description::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMDescription"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void Description::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference =>[%s:%d]", file, line); + } +} +#else +void Description::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +Description::Pointer Description::GetValid(CCStr file, int32_t line) const +{ + Validate(file, line); + // Validate the referenced description + if (!m_Description) + { + SqThrowF("Invalid Maxmind meta-data description reference =>[%s:%d]", file, line); + } + // Return the description pointer + return m_Description; +} +#else +Description::Pointer Description::GetValid() const +{ + Validate(); + // Validate the referenced description + if (!m_Description) + { + SqThrowF("Invalid Maxmind meta-data description reference"); + } + // Return the description pointer + return m_Description; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +Database Description::GetDatabase() const +{ + return Database(m_Handle); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger EntryData::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMEntryData"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void EntryData::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference =>[%s:%d]", file, line); + } +} +#else +void EntryData::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +EntryData::ConstRef EntryData::GetValid(CCStr file, int32_t line) const +{ + Validate(file, line); + // See if the entry has any data + if (!m_Entry.has_data) + { + SqThrowF("The referenced entry has no data =>[%s:%d]", file, line); + } + // Return the entry + return m_Entry; +} +#else +EntryData::ConstRef EntryData::GetValid() const +{ + Validate(); + // See if the entry has any data + if (!m_Entry.has_data) + { + SqThrowF("The referenced entry has no data"); + } + // Return the entry + return m_Entry; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +EntryData::EntryData() + : m_Handle(), m_Entry() +{ + std::memset(&m_Entry, 0, sizeof(Type)); +} + +// ------------------------------------------------------------------------------------------------ +void EntryData::Release() +{ + std::memset(&m_Entry, 0, sizeof(Type)); + m_Handle.Reset(); +} + +// ------------------------------------------------------------------------------------------------ +Database EntryData::GetDatabase() const +{ + return Database(m_Handle); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger EntryDataList::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMEntryDataList"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void EntryDataList::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference =>[%s:%d]", file, line); + } +} +#else +void EntryDataList::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +EntryDataList::Pointer EntryDataList::GetValid(CCStr file, int32_t line) const +{ + Validate(file, line); + // Validate the managed list + if (!m_List) + { + SqThrowF("Invalid Maxmind entry data list reference =>[%s:%d]", file, line); + } + // return the list + return m_List; +} +#else +EntryDataList::Pointer EntryDataList::GetValid() const +{ + Validate(); + // Validate the managed list + if (!m_List) + { + SqThrowF("Invalid Maxmind entry data list reference"); + } + // return the list + return m_List; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +EntryDataList::Pointer EntryDataList::GetValidElem(CCStr file, int32_t line) const +{ + Validate(file, line); + // Validate the current element + if (!m_List) + { + SqThrowF("Invalid Maxmind entry data element reference =>[%s:%d]", file, line); + } + // return the element + return m_Elem; +} +#else +EntryDataList::Pointer EntryDataList::GetValidElem() const +{ + Validate(); + // Validate the current element + if (!m_List) + { + SqThrowF("Invalid Maxmind entry data element reference"); + } + // return the element + return m_Elem; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +Database EntryDataList::GetDatabase() const +{ + return Database(m_Handle); +} + +// ------------------------------------------------------------------------------------------------ +uint32_t EntryDataList::GetCount() const +{ + // Prepare a counter + uint32_t count = 0; + // Do we even have a list? + if (m_List) + { + for (Pointer elem = m_List; elem; elem = elem->next) + { + ++count; + } + } + // Return the counter + return count; +} + +// ------------------------------------------------------------------------------------------------ +bool EntryDataList::Next() +{ + // Attempt to fetch the next element + m_Elem = SQMOD_GET_VALID(*this) ? m_Elem->next : nullptr; + // Return whether we have a valid element + return (m_Elem != nullptr); +} + +// ------------------------------------------------------------------------------------------------ +bool EntryDataList::Advance(SQInteger n) +{ + // Validate the database handle + SQMOD_VALIDATE(*this); + // Attempt to skip as many elements as possible + while (n && m_Elem) + { + // Fetch the next element + m_Elem = m_Elem->next; + // Decrease the counter + --n; + } + // Return whether we have a valid element + return m_Elem; +} + +// ------------------------------------------------------------------------------------------------ +void EntryDataList::DumpTo(const SQChar * filepath, int32_t indent) const +{ + // Validate the database and list handle + Pointer ptr = SQMOD_GET_VALID(*this); + // Validate the specified file path + if (!filepath || *filepath == '\0') + { + STHROWF("Invalid file path"); + } + // Attempt to open the specified file + FILE * fp = fopen(filepath, "w"); + // Validate the file handle + if (!fp) + { + STHROWF("Unable to open file %s", filepath); + } + // Attempt to dump the entry data list + int32_t status = MMDB_dump_entry_data_list(fp, ptr, indent); + // Close the file handle + fclose(fp); + // Validate the result of the operation + if (status != MMDB_SUCCESS) + { + STHROWF("Unable to dump the list [%s]", MMDB_strerror(status)); + } +} + +// ------------------------------------------------------------------------------------------------ +SQInteger LookupResult::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMLookupResult"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void LookupResult::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference =>[%s:%d]", file, line); + } +} +#else +void LookupResult::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +const DbRef & LookupResult::GetValid(CCStr file, int32_t line) const +{ + Validate(file, line); + return m_Handle; +} +#else +const DbRef & LookupResult::GetValid() const +{ + Validate(); + return m_Handle; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +LookupResult::LookupResult() + : m_Handle(), m_Result() +{ + std::memset(&m_Result, 0, sizeof(Type)); +} + +// ------------------------------------------------------------------------------------------------ +void LookupResult::Release() +{ + std::memset(&m_Result, 0, sizeof(Type)); + m_Handle.Reset(); +} + +// ------------------------------------------------------------------------------------------------ +Database LookupResult::GetDatabase() const +{ + return Database(m_Handle); +} + +// ------------------------------------------------------------------------------------------------ +Object LookupResult::GetEntryDataList() +{ + // See if there's an entry + if (!m_Result.found_entry) + { + STHROWF("Result does not have an entry"); + } + + MMDB_entry_data_list_s * entry_data_list = nullptr; + // Attempt to retrieve the entire entry data list at once + const int status = MMDB_get_entry_data_list(&m_Result.entry, &entry_data_list); + // Validate the status code + if (status != MMDB_SUCCESS) + { + STHROWF("Unable to get entry data list [%s]", MMDB_strerror(status)); + } + // Return the resulted list + return Object(new EntryDataList(m_Handle, entry_data_list)); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger LookupResult::GetValue(HSQUIRRELVM vm) +{ + const int32_t top = sq_gettop(vm); + // The lookup result instance + LookupResult * lookup; + // Attempt to extract the lookup result instance + try + { + lookup = Var< LookupResult * >(vm, 1).value; + } + catch (const std::exception & e) + { + return sq_throwerror(vm, e.what()); + } + // Do we have a valid lookup result instance? + if (!lookup) + { + return sq_throwerror(vm, "Invalid lookup result instance"); + } + // See if there's a handle + else if (!lookup->m_Handle) + { + return sq_throwerror(vm, "Invalid Maxmind database reference"); + } + // See if there's an entry + else if (!(lookup->m_Result.found_entry)) + { + return sq_throwerror(vm, "Result does not have an entry"); + } + + typedef std::vector< StackStrF > ArgList; + // The list of extracted arguments + ArgList arglist; + // Extract each argument as a string + for (SQInteger i = 2; i <= top; ++i) + { + arglist.emplace_back(vm, i); + // Did we fail to extract the argument value? + if (SQ_FAILED(arglist.back().Proc(false))) + { + return arglist.back().mRes; // Propagate the error + } + } + + typedef std::vector< const SQChar * > PtrList; + // The list of pointers to path segments + PtrList ptrlist; + // Grab the pointers to argument values + for (const auto & a : arglist) + { + ptrlist.push_back(a.mPtr); + } + // Push null to specify the end of the list + ptrlist.push_back(nullptr); + + MMDB_entry_data_s entry_data; + // Attempt to retrieve the specified entry data + const int status = MMDB_aget_value(&(lookup->m_Result.entry), &entry_data, ptrlist.data()); + // Validate the status code + if (status != MMDB_SUCCESS) + { + return sq_throwerrorf(vm, "Unable to get entry data [%s]", MMDB_strerror(status)); + } + // Push the resulted list object onto the stack + try + { + ClassType< EntryData >::PushInstance(vm, new EntryData(lookup->m_Handle, entry_data)); + } + catch (const std::exception & e) + { + return sq_throwerror(vm, e.what()); + } + // Specify that we returned a value + return 1; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger Metadata::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMMetadata"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void Metadata::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference =>[%s:%d]", file, line); + } +} +#else +void Metadata::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +Metadata::Pointer Metadata::GetValid(CCStr file, int32_t line) const +{ + Validate(file, line); + // Validate the referenced meta-data + if (!m_Metadata) + { + SqThrowF("Invalid Maxmind meta-data reference =>[%s:%d]", file, line); + } + // Return the meta-data pointer + return m_Metadata; +} +#else +Metadata::Pointer Metadata::GetValid() const +{ + Validate(); + // Validate the referenced meta-data + if (!m_Metadata) + { + SqThrowF("Invalid Maxmind meta-data reference"); + } + // Return the meta-data pointer + return m_Metadata; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +Database Metadata::GetDatabase() const +{ + return Database(m_Handle); +} + +// ------------------------------------------------------------------------------------------------ +Description Metadata::GetDescriptionHandle(uint32_t idx) const +{ + // Validate the specified index + if (idx > SQMOD_GET_VALID(*this)->description.count) + { + STHROWF("The specified description index is out of range: %u > %u", idx, m_Metadata->description.count); + } + // Return the requested description + return Description(m_Handle, m_Metadata->description.descriptions[idx]); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger SearchNode::Typename(HSQUIRRELVM vm) +{ + static const SQChar name[] = _SC("SqMMSearchNode"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +void SearchNode::Validate(CCStr file, int32_t line) const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference =>[%s:%d]", file, line); + } +} +#else +void SearchNode::Validate() const +{ + if (!m_Handle) + { + SqThrowF("Invalid Maxmind database reference"); + } +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) +SearchNode::Reference SearchNode::GetValid(CCStr file, int32_t line) +{ + Validate(file, line); + return m_Node; +} +#else +SearchNode::Reference SearchNode::GetValid() +{ + Validate(); + return m_Node; +} +#endif // _DEBUG + +// ------------------------------------------------------------------------------------------------ +SearchNode::SearchNode() + : m_Handle(), m_Node() +{ + std::memset(&m_Node, 0, sizeof(Type)); +} + +// ------------------------------------------------------------------------------------------------ +void SearchNode::Release() +{ + std::memset(&m_Node, 0, sizeof(Type)); + m_Handle.Reset(); +} + +// ------------------------------------------------------------------------------------------------ +Database SearchNode::GetDatabase() const +{ + return Database(m_Handle); +} + +// ------------------------------------------------------------------------------------------------ +Object SearchNode::GetLeftRecordEntryDataList() +{ + // Prepare a temporary entry data list pointer + MMDB_entry_data_list_s * entry_data_list = nullptr; + // Attempt to retrieve the entire entry data list at once + const int status = MMDB_get_entry_data_list(&(SQMOD_GET_VALID(*this).left_record_entry), &entry_data_list); + // Validate the status code + if (status != MMDB_SUCCESS) + { + STHROWF("Unable to get entry data list [%s]", MMDB_strerror(status)); + } + // Return the resulted list + return Object(new EntryDataList(m_Handle, entry_data_list)); +} + +// ------------------------------------------------------------------------------------------------ +Object SearchNode::GetRightRecordEntryDataList() +{ + // Prepare a temporary entry data list pointer + MMDB_entry_data_list_s * entry_data_list = nullptr; + // Attempt to retrieve the entire entry data list at once + const int status = MMDB_get_entry_data_list(&(SQMOD_GET_VALID(*this).right_record_entry), &entry_data_list); + // Validate the status code + if (status != MMDB_SUCCESS) + { + STHROWF("Unable to get entry data list [%s]", MMDB_strerror(status)); + } + // Return the resulted list + return Object(new EntryDataList(m_Handle, entry_data_list)); +} + +// ------------------------------------------------------------------------------------------------ +SQInteger SearchNode::GetRecordEntryData(HSQUIRRELVM vm, bool right) +{ + const int32_t top = sq_gettop(vm); + // The search node result instance + SearchNode * node; + // Attempt to extract the search node result instance + try + { + node = Var< SearchNode * >(vm, 1).value; + } + catch (const std::exception & e) + { + return sq_throwerror(vm, e.what()); + } + // Do we have a valid search node result instance? + if (!node) + { + return sq_throwerror(vm, "Invalid search node result instance"); + } + // See if there's a handle + else if (!node->m_Handle) + { + return sq_throwerror(vm, "Invalid Maxmind database reference"); + } + + typedef std::vector< StackStrF > ArgList; + // The list of extracted arguments + ArgList arglist; + // Extract each argument as a string + for (SQInteger i = 2; i <= top; ++i) + { + arglist.emplace_back(vm, i); + // Did we fail to extract the argument value? + if (SQ_FAILED(arglist.back().Proc(false))) + { + return arglist.back().mRes; // Propagate the error + } + } + + typedef std::vector< const SQChar * > PtrList; + // The list of pointers to path segments + PtrList ptrlist; + // Grab the pointers to argument values + for (const auto & a : arglist) + { + ptrlist.push_back(a.mPtr); + } + // Push null to specify the end of the list + ptrlist.push_back(nullptr); + + // Grab the requested entry + MMDB_entry_s * entry = &(right ? node->m_Node.right_record_entry : node->m_Node.left_record_entry); + + MMDB_entry_data_s entry_data; + // Attempt to retrieve the specified entry data + const int status = MMDB_aget_value(entry, &entry_data, ptrlist.data()); + // Validate the status code + if (status != MMDB_SUCCESS) + { + return sq_throwerrorf(vm, "Unable to get entry data [%s]", MMDB_strerror(status)); + } + // Push the resulted list object onto the stack + try + { + ClassType< EntryData >::PushInstance(vm, new EntryData(node->m_Handle, entry_data)); + } + catch (const std::exception & e) + { + return sq_throwerror(vm, e.what()); + } + + // Specify that we returned a value + return 1; +} + +// ================================================================================================ +void Register_MMDB(HSQUIRRELVM vm) +{ + Table mmns(vm); + + mmns.Bind(_SC("SockAddr"), + Class< SockAddr, NoCopy< SockAddr > >(mmns.GetVM(), _SC("SqMMSockAddr")) + // Constructors + .Ctor() + .Ctor< const SQChar * >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SockAddr::Typename) + .Func(_SC("_tostring"), &SockAddr::ToString) + // Properties + .Prop(_SC("IsValid"), &SockAddr::IsValid) + .Prop(_SC("Address"), &SockAddr::GetAddress) + ); + + mmns.Bind(_SC("Database"), + Class< Database >(mmns.GetVM(), _SC("SqMMDatabase")) + // Constructors + .Ctor() + .Ctor< StackStrF & >() + .Ctor< uint32_t, StackStrF & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &Database::Typename) + .Func(_SC("_tostring"), &Database::ToString) + // Properties + .Prop(_SC("IsValid"), &Database::IsValid) + .Prop(_SC("References"), &Database::GetRefCount) + .Prop(_SC("Metadata"), &Database::GetMetadata) + .Prop(_SC("MetadataAsEntryDataList"), &Database::GetMetadataAsEntryDataList) + // Member methods + .Func(_SC("Release"), &Database::Release) + .Func(_SC("LookupString"), &Database::LookupString) + .Func(_SC("LookupSockAddr"), &Database::LookupSockAddr) + .Func(_SC("ReadNode"), &Database::ReadNode) + // Member overloads + .Overload< void (Database::*)(const SQChar *) >(_SC("Open"), &Database::Open) + .Overload< void (Database::*)(const SQChar *, uint32_t) >(_SC("Open"), &Database::Open) + ); + + mmns.Bind(_SC("Description"), + Class< Description >(mmns.GetVM(), _SC("SqMMDescription")) + // Constructors + .Ctor() + .Ctor< const Description & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &Description::Typename) + .Func(_SC("_tostring"), &Description::ToString) + // Properties + .Prop(_SC("IsValid"), &Description::IsValid) + .Prop(_SC("Database"), &Description::GetDatabase) + .Prop(_SC("References"), &Description::GetRefCount) + .Prop(_SC("Value"), &Description::GetDescriptionValue) + .Prop(_SC("Language"), &Description::GetDescriptionLanguage) + // Member methods + .Func(_SC("Release"), &Description::Release) + ); + + mmns.Bind(_SC("EntryData"), + Class< EntryData >(mmns.GetVM(), _SC("SqMMEntryData")) + // Constructors + .Ctor() + .Ctor< const EntryData & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &EntryData::Typename) + .Func(_SC("_tostring"), &EntryData::ToString) + // Properties + .Prop(_SC("IsValid"), &EntryData::IsValid) + .Prop(_SC("Database"), &EntryData::GetDatabase) + .Prop(_SC("References"), &EntryData::GetRefCount) + .Prop(_SC("TypeName"), &EntryData::TypeName) + .Prop(_SC("HasData"), &EntryData::HasData) + .Prop(_SC("Type"), &EntryData::GetType) + .Prop(_SC("Offset"), &EntryData::GetOffset) + .Prop(_SC("DataSize"), &EntryData::DataSize) + .Prop(_SC("String"), &EntryData::GetString) + .Prop(_SC("Integer"), &EntryData::GetInteger) + .Prop(_SC("Float"), &EntryData::GetFloat) + .Prop(_SC("Long"), &EntryData::GetLong) + .Prop(_SC("Bool"), &EntryData::GetBool) + .Prop(_SC("Bytes"), &EntryData::GetBytes) + // Member methods + .Func(_SC("Release"), &EntryData::Release) + ); + + mmns.Bind(_SC("EntryDataList"), + Class< EntryDataList, NoCopy< EntryDataList > >(mmns.GetVM(), _SC("SqMMEntryDataList")) + // Constructors + .Ctor() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &EntryDataList::Typename) + .Func(_SC("_tostring"), &EntryDataList::ToString) + // Properties + .Prop(_SC("IsValid"), &EntryDataList::IsValid) + .Prop(_SC("Database"), &EntryDataList::GetDatabase) + .Prop(_SC("References"), &EntryDataList::GetRefCount) + .Prop(_SC("HaveElement"), &EntryDataList::HaveElement) + .Prop(_SC("TypeName"), &EntryDataList::TypeName) + .Prop(_SC("Count"), &EntryDataList::GetCount) + .Prop(_SC("HasData"), &EntryDataList::HasData) + .Prop(_SC("Type"), &EntryDataList::GetType) + .Prop(_SC("Offset"), &EntryDataList::GetOffset) + .Prop(_SC("DataSize"), &EntryDataList::DataSize) + .Prop(_SC("String"), &EntryDataList::GetString) + .Prop(_SC("Integer"), &EntryDataList::GetInteger) + .Prop(_SC("Float"), &EntryDataList::GetFloat) + .Prop(_SC("Long"), &EntryDataList::GetLong) + .Prop(_SC("Bool"), &EntryDataList::GetBool) + .Prop(_SC("Bytes"), &EntryDataList::GetBytes) + // Member methods + .Func(_SC("Release"), &EntryDataList::Release) + .Func(_SC("Next"), &EntryDataList::Next) + .Func(_SC("Advance"), &EntryDataList::Advance) + .Func(_SC("Reset"), &EntryDataList::Reset) + // Member Overloads + .Overload< void (EntryDataList::*)(const SQChar *) const >(_SC("DumpTo"), &EntryDataList::DumpTo) + .Overload< void (EntryDataList::*)(const SQChar *, int32_t) const >(_SC("DumpTo"), &EntryDataList::DumpTo) + ); + + mmns.Bind(_SC("LookupResult"), + Class< LookupResult >(mmns.GetVM(), _SC("SqMMLookupResult")) + // Constructors + .Ctor() + .Ctor< const LookupResult & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &LookupResult::Typename) + .Func(_SC("_tostring"), &LookupResult::ToString) + // Properties + .Prop(_SC("IsValid"), &LookupResult::IsValid) + .Prop(_SC("Database"), &LookupResult::GetDatabase) + .Prop(_SC("References"), &LookupResult::GetRefCount) + .Prop(_SC("FoundEntry"), &LookupResult::FoundEntry) + .Prop(_SC("NetMask"), &LookupResult::GetNetMask) + .Prop(_SC("EntryDataList"), &LookupResult::GetEntryDataList) + // Member methods + .Func(_SC("Release"), &LookupResult::Release) + // Squirrel functions + .SquirrelFunc(_SC("GetValue"), &LookupResult::GetValue) + ); + + mmns.Bind(_SC("Metadata"), + Class< Metadata >(mmns.GetVM(), _SC("SqMMMetadata")) + // Constructors + .Ctor() + .Ctor< const Metadata & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &Metadata::Typename) + .Func(_SC("_tostring"), &Metadata::ToString) + // Properties + .Prop(_SC("IsValid"), &Metadata::IsValid) + .Prop(_SC("Database"), &Metadata::GetDatabase) + .Prop(_SC("References"), &Metadata::GetRefCount) + .Prop(_SC("NodeCount"), &Metadata::GetNodeCount) + .Prop(_SC("RecordSize"), &Metadata::GetRecordSize) + .Prop(_SC("IpVersion"), &Metadata::GetIpVersion) + .Prop(_SC("DatabaseType"), &Metadata::GetDatabaseType) + .Prop(_SC("LanguageCount"), &Metadata::GetLanguageCount) + .Prop(_SC("BinaryFormatMajorVersion"), &Metadata::GetBinaryFormatMajorVersion) + .Prop(_SC("BinaryFormatMinorVersion"), &Metadata::GetBinaryFormatMinorVersion) + .Prop(_SC("BuildEpoch"), &Metadata::GetBuildEpoch) + .Prop(_SC("DescriptionCount"), &Metadata::GetDescriptionCount) + // Member methods + .Func(_SC("Release"), &Metadata::Release) + .Func(_SC("GetLanguageName"), &Metadata::GetLanguageName) + .Func(_SC("GetDescriptionHandle"), &Metadata::GetDescriptionHandle) + .Func(_SC("GetDescriptionValue"), &Metadata::GetDescriptionValue) + .Func(_SC("GetDescriptionLanguage"), &Metadata::GetDescriptionLanguage) + ); + + mmns.Bind(_SC("SearchNode"), + Class< SearchNode >(mmns.GetVM(), _SC("SqMMSearchNode")) + // Constructors + .Ctor() + .Ctor< const SearchNode & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SearchNode::Typename) + .Func(_SC("_tostring"), &SearchNode::ToString) + // Properties + .Prop(_SC("IsValid"), &SearchNode::IsValid) + .Prop(_SC("Database"), &SearchNode::GetDatabase) + .Prop(_SC("References"), &SearchNode::GetRefCount) + .Prop(_SC("LeftRecord"), &SearchNode::GetLeftRecord) + .Prop(_SC("RightRecord"), &SearchNode::GetRightRecord) + .Prop(_SC("LeftRecordType"), &SearchNode::GetLeftRecordType) + .Prop(_SC("RightRecordType"), &SearchNode::GetRightRecordType) + .Prop(_SC("LeftRecordEntryDataList"), &SearchNode::GetLeftRecordEntryDataList) + .Prop(_SC("RightRecordEntryDataList"), &SearchNode::GetRightRecordEntryDataList) + // Member methods + .Func(_SC("Release"), &SearchNode::Release) + // Squirrel methods + .SquirrelFunc(_SC("GetLeftRecordValue"), &SearchNode::GetLeftRecordEntryData) + .SquirrelFunc(_SC("GetRightRecordValue"), &SearchNode::GetRightRecordEntryData) + ); + + mmns.Func(_SC("StrError"), MMDB_strerror); + mmns.Func(_SC("LibVersion"), MMDB_lib_version); + mmns.Func(_SC("TypeStr"), AsTypeStr); + + RootTable(vm).Bind(_SC("SqMMDB"), mmns); + + ConstTable(vm).Enum(_SC("SqMMDataType"), Enumeration(vm) + .Const(_SC("Extended"), MMDB_DATA_TYPE_EXTENDED) + .Const(_SC("Pointer"), MMDB_DATA_TYPE_POINTER) + .Const(_SC("Utf8String"), MMDB_DATA_TYPE_UTF8_STRING) + .Const(_SC("Double"), MMDB_DATA_TYPE_DOUBLE) + .Const(_SC("Bytes"), MMDB_DATA_TYPE_BYTES) + .Const(_SC("Uint16"), MMDB_DATA_TYPE_UINT16) + .Const(_SC("Uint32"), MMDB_DATA_TYPE_UINT32) + .Const(_SC("Map"), MMDB_DATA_TYPE_MAP) + .Const(_SC("Int32"), MMDB_DATA_TYPE_INT32) + .Const(_SC("Uint64"), MMDB_DATA_TYPE_UINT64) + .Const(_SC("Uint128"), MMDB_DATA_TYPE_UINT128) + .Const(_SC("Array"), MMDB_DATA_TYPE_ARRAY) + .Const(_SC("Container"), MMDB_DATA_TYPE_CONTAINER) + .Const(_SC("EndMarker"), MMDB_DATA_TYPE_END_MARKER) + .Const(_SC("Boolean"), MMDB_DATA_TYPE_BOOLEAN) + .Const(_SC("Float"), MMDB_DATA_TYPE_FLOAT) + ); + + ConstTable(vm).Enum(_SC("SqMMRecordType"), Enumeration(vm) + .Const(_SC("SearchNode"), MMDB_RECORD_TYPE_SEARCH_NODE) + .Const(_SC("Empty"), MMDB_RECORD_TYPE_EMPTY) + .Const(_SC("Data"), MMDB_RECORD_TYPE_DATA) + .Const(_SC("Invalid"), MMDB_RECORD_TYPE_INVALID) + ); + + ConstTable(vm).Enum(_SC("SqMMErrCode"), Enumeration(vm) + .Const(_SC("Success"), MMDB_SUCCESS) + .Const(_SC("FileOpenError"), MMDB_FILE_OPEN_ERROR) + .Const(_SC("CorruptSearchTreeError"), MMDB_CORRUPT_SEARCH_TREE_ERROR) + .Const(_SC("InvalidMetadataError"), MMDB_INVALID_METADATA_ERROR) + .Const(_SC("IOError"), MMDB_IO_ERROR) + .Const(_SC("OutOfMemoryError"), MMDB_OUT_OF_MEMORY_ERROR) + .Const(_SC("UnknownDatabaseFormatError"), MMDB_UNKNOWN_DATABASE_FORMAT_ERROR) + .Const(_SC("InvalidDataError"), MMDB_INVALID_DATA_ERROR) + .Const(_SC("InvalidLookupPathError"), MMDB_INVALID_LOOKUP_PATH_ERROR) + .Const(_SC("LookupPathDoesNotMatchDataError"), MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) + .Const(_SC("InvalidNodeNumberError"), MMDB_INVALID_NODE_NUMBER_ERROR) + .Const(_SC("Ipv6LookupInIpv4DatabaseError"), MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR) + ); + + Sqrat::ConstTable(vm) + .Const(_SC("MMDB_MODE_MMAP"), MMDB_MODE_MMAP) + .Const(_SC("MMDB_MODE_MASK"), MMDB_MODE_MASK); + +} + +} // Namespace:: SqMod diff --git a/module/Library/MMDB.hpp b/module/Library/MMDB.hpp new file mode 100644 index 00000000..1bf8a901 --- /dev/null +++ b/module/Library/MMDB.hpp @@ -0,0 +1,1728 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Common.hpp" + +// ------------------------------------------------------------------------------------------------ +#include "Library/IO/Buffer.hpp" +#include "Library/Numeric/Long.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Handle validation. +*/ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + #define SQMOD_VALIDATE(x) (x).Validate(__FILE__, __LINE__) + #define SQMOD_GET_VALID(x) (x).GetValid(__FILE__, __LINE__) + #define SQMOD_GET_VALID_ELEM(x) (x).GetValidElem(__FILE__, __LINE__) + #define SQMOD_GET_VALID_DATA(x) (x).GetValidData(__FILE__, __LINE__) +#else + #define SQMOD_VALIDATE(x) (x).Validate() + #define SQMOD_GET_VALID(x) (x).GetValid() + #define SQMOD_GET_VALID_ELEM(x) (x).GetValidElem() + #define SQMOD_GET_VALID_DATA(x) (x).GetValidData() +#endif // _DEBUG + +/* ------------------------------------------------------------------------------------------------ + * Forward declarations. +*/ +class Database; +class Metadata; +class Description; +class SockAddr; +class EntryData; +class EntryDataList; +class LookupResult; +class SearchNode; + +/* ------------------------------------------------------------------------------------------------ + * Forward handle declarations. +*/ +struct DbHnd; + +/* ------------------------------------------------------------------------------------------------ + * Common typedefs. +*/ +typedef SharedPtr< DbHnd > DbRef; + +/* ------------------------------------------------------------------------------------------------ + * Used to retrieve the string representation of the specified type identifier. +*/ +const SQChar * AsTypeStr(uint32_t id); + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the value from the specified entry data as a boolean. +*/ +bool GetEntryAsBool(const MMDB_entry_data_s & ed); + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the value from the specified entry data as a native integer. +*/ +SQInteger GetEntryAsInteger(const MMDB_entry_data_s & ed); + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the value from the specified entry data as a floating point. +*/ +SQFloat GetEntryAsFloat(const MMDB_entry_data_s & ed); + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the value from the specified entry data as a long integer. +*/ +LightObj GetEntryAsLong(const MMDB_entry_data_s & ed); + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the value from the specified entry data as a string. +*/ +LightObj GetEntryAsString(const MMDB_entry_data_s & ed); + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the value from the specified entry data as a stream of bytes. +*/ +LightObj GetEntryAsBytes(const MMDB_entry_data_s & ed); + +/* ------------------------------------------------------------------------------------------------ + * Class that can obtain information from string addresses and be used repeatedly thereafter. +*/ +class SockAddr +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef struct addrinfo Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed sockaddr pointer and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed sockaddr pointer and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD Pointer GetValid(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD Pointer GetValid() const; +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + Pointer m_Handle; // The managed sockaddr structure. + String m_Addres; // The address that was queried for information. + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + SockAddr() + : m_Handle(nullptr) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Base constructor. + */ + explicit SockAddr(const SQChar * addr); + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. (disabled) + */ + SockAddr(const SockAddr & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + SockAddr(SockAddr && o) noexcept + : m_Handle(o.m_Handle) + , m_Addres(std::move(o.m_Addres)) + { + o.m_Handle = nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~SockAddr(); + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. (disabled) + */ + SockAddr & operator = (const SockAddr & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + SockAddr & operator = (SockAddr && o) noexcept + { + if (m_Handle != o.m_Handle) + { + m_Handle = o.m_Handle; + m_Addres = o.m_Addres; + o.m_Handle = nullptr; + } + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal addrinfo structure pointer. + */ + Pointer GetHandle() + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal addrinfo structure pointer. + */ + SQMOD_NODISCARD Pointer GetHandle() const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD const String & ToString() const + { + return m_Addres; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid addrinfo structure. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated string address. + */ + SQMOD_NODISCARD const String & GetAddress() const + { + return m_Addres; + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Manages a reference counted database instance. +*/ +class DbHnd +{ + // -------------------------------------------------------------------------------------------- + friend class Database; + +public: + + // -------------------------------------------------------------------------------------------- + typedef MMDB_s Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +public: + + // -------------------------------------------------------------------------------------------- + MMDB_s mDb; // The managed database handle. + + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DbHnd(const SQChar * filepath, uint32_t flags); + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. (disabled) + */ + DbHnd(const DbHnd & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. (disabled) + */ + DbHnd(DbHnd && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DbHnd(); + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. (disabled) + */ + DbHnd & operator = (const DbHnd & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. (disabled) + */ + DbHnd & operator = (DbHnd && o) = delete; +}; + +/* ------------------------------------------------------------------------------------------------ + * Class that can be used to open and query information from MaxMind database files. +*/ +class Database +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef DbHnd::Type Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD const DbRef & GetValid(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD const DbRef & GetValid() const; +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + DbRef m_Handle; // The managed database handle. + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. (null) + */ + Database() + : m_Handle() + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Base constructor. (default flags) + */ + explicit Database(StackStrF & filepath) + : m_Handle(new DbHnd(filepath.mPtr, 0)) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Base constructor. + */ + Database(uint32_t flags, StackStrF & filepath) + : m_Handle(new DbHnd(filepath.mPtr, flags)) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Explicit handle constructor. + */ + explicit Database(const DbRef & db) // NOLINT(modernize-pass-by-value) + : m_Handle(db) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + Database(const Database & o) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + Database(Database && o) = default; + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + Database & operator = (const Database & o) = default; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + Database & operator = (Database && o) = default; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD const SQChar * ToString() const + { + return m_Handle ? m_Handle->mDb.filename : _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid database instance. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Release the manages handles/pointers and become a null instance. + */ + void Release() + { + m_Handle.Reset(); + } + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to the managed database instance. + */ + SQMOD_NODISCARD uint32_t GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Attempt to open the specified database. + */ + void Open(const SQChar * filepath) + { + Open(filepath, 0); + } + + /* -------------------------------------------------------------------------------------------- + * Attempt to open the specified database. + */ + void Open(const SQChar * filepath, uint32_t flags) + { + // Make sure there isn't another database handle + if (!m_Handle) + { + m_Handle = DbRef(new DbHnd(filepath, flags)); + } + else + { + STHROWF("Loading is disabled while database is referenced"); + } + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the metadata associated with the managed database handle. + */ + SQMOD_NODISCARD Metadata GetMetadata() const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the metadata associated with the managed database handle as an entry data list. + */ + SQMOD_NODISCARD LightObj GetMetadataAsEntryDataList() const; + + /* -------------------------------------------------------------------------------------------- + * Look up an IP address that is passed in as a null-terminated string. + */ + LookupResult LookupString(const SQChar * addr); + + /* -------------------------------------------------------------------------------------------- + * Looks up an IP address that has already been resolved by getaddrinfo(). + */ + LookupResult LookupSockAddr(SockAddr & sockaddr); + + /* -------------------------------------------------------------------------------------------- + * Retrieve a specific node from the managed database. + */ + SQMOD_NODISCARD SearchNode ReadNode(uint32_t node) const; +}; + +/* ------------------------------------------------------------------------------------------------ + * Class that can hold and be used inspect meta-data descriptions. +*/ +class Description +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef MMDB_description_s Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and description pointer and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD Pointer GetValid(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD Pointer GetValid() const; +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + DbRef m_Handle; // The database associated with this meta-data description. + Pointer m_Description; // The inspected meta-data description structure. + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. (null) + */ + Description() + : m_Handle(), m_Description(nullptr) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Construct and with a specific meta-data description. + */ + Description(const DbRef & db, Pointer description) // NOLINT(modernize-pass-by-value) + : m_Handle(db), m_Description(description) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + Description(const Description &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + Description(Description &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + Description & operator = (const Description &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + Description & operator = (Description &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD const SQChar * ToString() const + { + return m_Description ? m_Description->description : _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid database and description structure. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle; // If there's a database handle then there's a description too + } + + /* -------------------------------------------------------------------------------------------- + * Release the manages handles/pointers and become a null instance. + */ + void Release() + { + m_Handle.Reset(); + m_Description = nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the database associated with the managed handle/pointer. + */ + SQMOD_NODISCARD Database GetDatabase() const; + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to the managed database instance. + */ + SQMOD_NODISCARD uint32_t GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value of the managed description handle. + */ + SQMOD_NODISCARD const SQChar * GetDescriptionValue() const + { + return SQMOD_GET_VALID(*this)->description; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the language of the managed description handle. + */ + SQMOD_NODISCARD const SQChar * GetDescriptionLanguage() const + { + return SQMOD_GET_VALID(*this)->language; + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Class that can hold and be used to inspect entry data values. +*/ +class EntryData +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef MMDB_entry_data_s Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD ConstRef GetValid(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD ConstRef GetValid() const; +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + DbRef m_Handle; // The database from which this result comes from. + Type m_Entry; // The managed entry-data structure. + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. (null) + */ + EntryData(); + + /* -------------------------------------------------------------------------------------------- + * Construct and take ownership of a certain entry data. + */ + EntryData(const DbRef & db, Reference entry) // NOLINT(modernize-pass-by-value) + : m_Handle(db), m_Entry(entry) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + EntryData(const EntryData &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + EntryData(EntryData &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + EntryData & operator = (const EntryData &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + EntryData & operator = (EntryData &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD const SQChar * ToString() const + { + return AsTypeStr(m_Entry.type); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid database and entry structure. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle && m_Entry.has_data; + } + + /* -------------------------------------------------------------------------------------------- + * Release the manages handles/pointers and become a null instance. + */ + void Release(); + + /* -------------------------------------------------------------------------------------------- + * Retrieve the database associated with the managed handle/pointer. + */ + SQMOD_NODISCARD Database GetDatabase() const; + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to the managed database instance. + */ + SQMOD_NODISCARD uint32_t GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Used to retrieve the type of the current element as a string. + */ + SQMOD_NODISCARD const SQChar * TypeName() const + { + // Validate the handle + SQMOD_VALIDATE(*this); + // Return the requested information + return AsTypeStr(m_Entry.type); + } + + /* -------------------------------------------------------------------------------------------- + * See whether a valid element is currently processed. + */ + SQMOD_NODISCARD bool HasData() const + { + return ConvTo< bool >::From(SQMOD_GET_VALID(*this).has_data); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the type identifier of the current element. + */ + SQMOD_NODISCARD SQInteger GetType() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this).type); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the offset of the current element. + */ + SQMOD_NODISCARD SQInteger GetOffset() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this).offset); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the offset of the next element. + */ + SQMOD_NODISCARD SQInteger DataSize() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this).data_size); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a boolean. + */ + SQMOD_NODISCARD bool GetBool() const + { + return GetEntryAsBool(SQMOD_GET_VALID(*this)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a native integer. + */ + SQMOD_NODISCARD SQInteger GetInteger() const + { + return GetEntryAsInteger(SQMOD_GET_VALID(*this)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a floating point. + */ + SQMOD_NODISCARD SQFloat GetFloat() const + { + return GetEntryAsFloat(SQMOD_GET_VALID(*this)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a long integer. + */ + SQMOD_NODISCARD LightObj GetLong() const + { + return GetEntryAsLong(SQMOD_GET_VALID(*this)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a string. + */ + SQMOD_NODISCARD LightObj GetString() const + { + return GetEntryAsString(SQMOD_GET_VALID(*this)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a stream of bytes. + */ + SQMOD_NODISCARD LightObj GetBytes() const + { + return GetEntryAsBytes(SQMOD_GET_VALID(*this)); + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Class that can hold and be used inspect database meta-data. +*/ +class EntryDataList +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef MMDB_entry_data_list_s Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD Pointer GetValid(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD Pointer GetValid() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD Pointer GetValidElem(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD Pointer GetValidElem() const; +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + DbRef m_Handle; // The database associated with this meta-data. + Pointer m_List; // The managed entry data list. + Pointer m_Elem; // The currently processed element from the list. + +public: + + /* -------------------------------------------------------------------------------------------- + * Construct and with a specific entry list. + */ + EntryDataList(const DbRef & db, Pointer list) // NOLINT(modernize-pass-by-value) + : m_Handle(db), m_List(list), m_Elem(list) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Default constructor. (null) + */ + EntryDataList() + : m_Handle(), m_List(nullptr), m_Elem(nullptr) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. (disabled) + */ + EntryDataList(const EntryDataList &) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + EntryDataList(EntryDataList && o) + noexcept : m_Handle(std::move(o.m_Handle)) + , m_List(o.m_List) + , m_Elem(o.m_Elem) + { + o.m_List = nullptr; + o.m_Elem = nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~EntryDataList() + { + // Do we have to free any list? + if (m_List) + { + MMDB_free_entry_data_list(m_List); + } + } + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. (disabled) + */ + EntryDataList & operator = (const EntryDataList &) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + EntryDataList & operator = (EntryDataList && o) noexcept + { + if (m_List != o.m_List) + { + m_Handle = std::move(o.m_Handle); + m_List = o.m_List; + m_Elem = o.m_Elem; + o.m_List = nullptr; + o.m_Elem = nullptr; + } + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD const SQChar * ToString() const + { + return m_Elem ? AsTypeStr(m_Elem->entry_data.type) : _SC("invalid"); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid database and element pointer. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle && m_Elem; + } + + /* -------------------------------------------------------------------------------------------- + * Release the manages handles/pointers and become a null instance. + */ + void Release() + { + m_Handle.Reset(); + // Do we have to free any list? + if (m_List) + { + MMDB_free_entry_data_list(m_List); + } + // Finally, release those as well + m_List = nullptr; + m_Elem = nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the database associated with the managed handle/pointer. + */ + SQMOD_NODISCARD Database GetDatabase() const; + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to the managed database instance. + */ + SQMOD_NODISCARD uint32_t GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * See whether a valid element is currently processed. + */ + SQMOD_NODISCARD bool HaveElement() const + { + return m_Elem; + } + + /* -------------------------------------------------------------------------------------------- + * Used to retrieve the type of the current element as a string. + */ + SQMOD_NODISCARD const SQChar * TypeName() const + { + return AsTypeStr(SQMOD_GET_VALID_ELEM(*this)->entry_data.type); + } + + /* -------------------------------------------------------------------------------------------- + * Return the total entries in the list. + */ + SQMOD_NODISCARD uint32_t GetCount() const; + + /* -------------------------------------------------------------------------------------------- + * Go to the next element. + */ + bool Next(); + + /* -------------------------------------------------------------------------------------------- + * Advance a certain number of elements. + */ + bool Advance(SQInteger n); + + /* -------------------------------------------------------------------------------------------- + * Go back to the first element in the list. + */ + void Reset() + { + m_Elem = SQMOD_GET_VALID_ELEM(*this); + } + + /* -------------------------------------------------------------------------------------------- + * See whether a valid element is currently processed. + */ + SQMOD_NODISCARD bool HasData() const + { + return ConvTo< bool >::From(SQMOD_GET_VALID_ELEM(*this)->entry_data.has_data); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the type identifier of the current element. + */ + SQMOD_NODISCARD SQInteger GetType() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID_ELEM(*this)->entry_data.type); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the offset of the current element. + */ + SQMOD_NODISCARD SQInteger GetOffset() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID_ELEM(*this)->entry_data.offset); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the offset of the next element. + */ + SQMOD_NODISCARD SQInteger DataSize() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID_ELEM(*this)->entry_data.data_size); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a boolean. + */ + SQMOD_NODISCARD bool GetBool() const + { + return GetEntryAsBool(SQMOD_GET_VALID_ELEM(*this)->entry_data); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a native integer. + */ + SQMOD_NODISCARD SQInteger GetInteger() const + { + return GetEntryAsInteger(SQMOD_GET_VALID_ELEM(*this)->entry_data); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a floating point. + */ + SQMOD_NODISCARD SQFloat GetFloat() const + { + return GetEntryAsFloat(SQMOD_GET_VALID_ELEM(*this)->entry_data); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a long integer. + */ + SQMOD_NODISCARD LightObj GetLong() const + { + return GetEntryAsLong(SQMOD_GET_VALID_ELEM(*this)->entry_data); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a string. + */ + SQMOD_NODISCARD LightObj GetString() const + { + return GetEntryAsString(SQMOD_GET_VALID_ELEM(*this)->entry_data); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the value from the current element as a stream of bytes. + */ + SQMOD_NODISCARD LightObj GetBytes() const + { + return GetEntryAsBytes(SQMOD_GET_VALID_ELEM(*this)->entry_data); + } + + /* -------------------------------------------------------------------------------------------- + * Dump the contents of the list to the specified list. + */ + void DumpTo(const SQChar * filepath) const + { + DumpTo(filepath, 0); + } + + /* -------------------------------------------------------------------------------------------- + * Dump the contents of the list to the specified list. + */ + void DumpTo(const SQChar * filepath, int32_t indent) const; +}; + +/* ------------------------------------------------------------------------------------------------ + * Class that can hold and be used to work with lookup results. +*/ +class LookupResult +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef MMDB_lookup_result_s Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD const DbRef & GetValid(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD const DbRef & GetValid() const; +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + DbRef m_Handle; // The database from which this result comes from. + Type m_Result; // The managed result structure. + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. (null) + */ + LookupResult(); + + /* -------------------------------------------------------------------------------------------- + * Construct and take ownership of a certain result. + */ + LookupResult(const DbRef & db, Reference result) // NOLINT(modernize-pass-by-value) + : m_Handle(db), m_Result(result) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + LookupResult(const LookupResult &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + LookupResult(LookupResult &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + LookupResult & operator = (const LookupResult &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + LookupResult & operator = (LookupResult &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD String ToString() const + { + return fmt::format("{}", m_Result.entry.offset); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid database and result structure. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle && m_Result.found_entry; + } + + /* -------------------------------------------------------------------------------------------- + * Release the manages handles/pointers and become a null instance. + */ + void Release(); + + /* -------------------------------------------------------------------------------------------- + * Retrieve the database associated with the managed handle/pointer. + */ + SQMOD_NODISCARD Database GetDatabase() const; + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to the managed database instance. + */ + SQMOD_NODISCARD uint32_t GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * See whether the result contains a valid entry in the associated database. + */ + SQMOD_NODISCARD bool FoundEntry() const + { + // Validate the database handle + SQMOD_VALIDATE(*this); + // Return the requested information + return m_Result.found_entry; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the net-mask from the result structure. + */ + SQMOD_NODISCARD SQInteger GetNetMask() const + { + // Validate the database handle + SQMOD_VALIDATE(*this); + // Return the requested information + return static_cast< SQInteger >(m_Result.netmask); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the entire entry data list. + */ + Object GetEntryDataList(); + + /* -------------------------------------------------------------------------------------------- + * Lookup data in the associated result using the specified path. + */ + SQMOD_NODISCARD static SQInteger GetValue(HSQUIRRELVM vm); +}; + +/* ------------------------------------------------------------------------------------------------ + * Class that can hold and be used inspect database meta-data. +*/ +class Metadata +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef MMDB_metadata_s Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD Pointer GetValid(CCStr file, int32_t line) const; +#else + SQMOD_NODISCARD Pointer GetValid() const; +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + DbRef m_Handle; // The database associated with this meta-data. + Pointer m_Metadata; // The inspected meta-data structure. + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. (null) + */ + Metadata() + : m_Handle(), m_Metadata(nullptr) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Construct and with a specific meta-data. + */ + Metadata(const DbRef & db, Pointer metadata) // NOLINT(modernize-pass-by-value) + : m_Handle(db), m_Metadata(metadata) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + Metadata(const Metadata &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + Metadata(Metadata &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + Metadata & operator = (const Metadata &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + Metadata & operator = (Metadata &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD const SQChar * ToString() const + { + return m_Metadata ? m_Metadata->database_type : _SC(""); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid database and meta-data pointer. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle; // If there's a database handle then there's a meta-data too + } + + /* -------------------------------------------------------------------------------------------- + * Release the manages handles/pointers and become a null instance. + */ + void Release() + { + m_Handle.Reset(); + m_Metadata = nullptr; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the database associated with the managed handle/pointer. + */ + SQMOD_NODISCARD Database GetDatabase() const; + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to the managed database instance. + */ + SQMOD_NODISCARD uint32_t GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the node count. + */ + SQMOD_NODISCARD SQInteger GetNodeCount() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this)->node_count); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the record size. + */ + SQMOD_NODISCARD SQInteger GetRecordSize() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this)->record_size); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the IP version. + */ + SQMOD_NODISCARD SQInteger GetIpVersion() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this)->ip_version); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the database type. + */ + SQMOD_NODISCARD const SQChar * GetDatabaseType() const + { + return SQMOD_GET_VALID(*this)->database_type; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of language names. + */ + SQMOD_NODISCARD SQInteger GetLanguageCount() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this)->languages.count); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the name of a certain language. + */ + SQMOD_NODISCARD const SQChar * GetLanguageName(uint32_t idx) const + { + // Validate the specified index + if (idx > SQMOD_GET_VALID(*this)->languages.count) + { + STHROWF("The specified language index is out of range: %u > %u", idx, m_Metadata->languages.count); + } + // Return the requested name + return m_Metadata->languages.names[idx]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the major version of the binary format. + */ + SQMOD_NODISCARD SQInteger GetBinaryFormatMajorVersion() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this)->binary_format_major_version); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the minor version of the binary format. + */ + SQMOD_NODISCARD SQInteger GetBinaryFormatMinorVersion() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this)->binary_format_minor_version); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the build epoch. + */ + SQMOD_NODISCARD Object GetBuildEpoch() const + { + return Object(SqTypeIdentity< ULongInt >{}, SqVM(), ConvTo< uint64_t >::From(SQMOD_GET_VALID(*this)->build_epoch)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of available description handles. + */ + SQMOD_NODISCARD SQInteger GetDescriptionCount() const + { + return ConvTo< SQInteger >::From(SQMOD_GET_VALID(*this)->description.count); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the handle of a certain description. + */ + SQMOD_NODISCARD Description GetDescriptionHandle(uint32_t idx) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the description of a certain description handle. + */ + SQMOD_NODISCARD const SQChar * GetDescriptionValue(uint32_t idx) const + { + // Validate the specified index + if (idx > SQMOD_GET_VALID(*this)->description.count) + { + STHROWF("The specified description index is out of range: %u > %u", idx, m_Metadata->description.count); + } + // Return the requested description value + return m_Metadata->description.descriptions[idx]->description; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the language of a certain description handle. + */ + SQMOD_NODISCARD const SQChar * GetDescriptionLanguage(uint32_t idx) const + { + // Validate the specified index + if (idx > SQMOD_GET_VALID(*this)->description.count) + { + STHROWF("The specified description index is out of range: %u > %u", idx, m_Metadata->description.count); + } + // Return the requested description language + return m_Metadata->description.descriptions[idx]->language; + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Class that can hold and be used to work with search nodes. +*/ +class SearchNode +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef MMDB_search_node_s Type; // The managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type* Pointer; // Pointer to the managed type. + typedef const Type* ConstPtr; // Constant pointer to the managed type. + + // -------------------------------------------------------------------------------------------- + typedef Type& Reference; // Reference to the managed type. + typedef const Type& ConstRef; // Constant reference to the managed type. + +protected: + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + void Validate(CCStr file, int32_t line) const; +#else + void Validate() const; +#endif // _DEBUG + + /* -------------------------------------------------------------------------------------------- + * Validate the managed database handle and throw an error if invalid. + */ +#if defined(_DEBUG) || defined(SQMOD_EXCEPTLOC) + SQMOD_NODISCARD Reference GetValid(CCStr file, int32_t line); +#else + SQMOD_NODISCARD Reference GetValid(); +#endif // _DEBUG + +private: + + // --------------------------------------------------------------------------------------------- + DbRef m_Handle; // The database from which this search node comes from. + Type m_Node; // The managed search node structure. + +public: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. (null) + */ + SearchNode(); + + /* -------------------------------------------------------------------------------------------- + * Construct and take ownership of a certain search node. + */ + SearchNode(const DbRef & db, Reference node) // NOLINT(modernize-pass-by-value) + : m_Handle(db), m_Node(node) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + SearchNode(const SearchNode &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + SearchNode(SearchNode &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + SearchNode & operator = (const SearchNode &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + SearchNode & operator = (SearchNode &&) = default; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD String ToString() const + { + return fmt::format("<{}:{},{}:{}>", m_Node.left_record, AsTypeStr(m_Node.left_record_type) + , m_Node.right_record, AsTypeStr(m_Node.right_record_type)); + } + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * See whether this instance references a valid database and node structure. + */ + SQMOD_NODISCARD bool IsValid() const + { + return m_Handle; + } + + /* -------------------------------------------------------------------------------------------- + * Release the manages handles/pointers and become a null instance. + */ + void Release(); + + /* -------------------------------------------------------------------------------------------- + * Retrieve the database associated with the managed handle/pointer. + */ + SQMOD_NODISCARD Database GetDatabase() const; + + /* -------------------------------------------------------------------------------------------- + * Return the number of active references to the managed database instance. + */ + SQMOD_NODISCARD uint32_t GetRefCount() const + { + return m_Handle.Count(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the left record value. + */ + Object GetLeftRecord() + { + return Object(SqTypeIdentity< ULongInt >{}, SqVM(), ConvTo< uint64_t >::From(SQMOD_GET_VALID(*this).left_record)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the right record value. + */ + Object GetRightRecord() + { + return Object(SqTypeIdentity< ULongInt >{}, SqVM(), ConvTo< uint64_t >::From(SQMOD_GET_VALID(*this).right_record)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the left record value type. + */ + SQInteger GetLeftRecordType() + { + return static_cast< SQInteger >(SQMOD_GET_VALID(*this).left_record_type); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the right record value type. + */ + SQInteger GetRightRecordType() + { + return static_cast< SQInteger >(SQMOD_GET_VALID(*this).right_record_type); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the left record entry data list. + */ + Object GetLeftRecordEntryDataList(); + + /* -------------------------------------------------------------------------------------------- + * Retrieve the right record entry data list. + */ + Object GetRightRecordEntryDataList(); + + /* -------------------------------------------------------------------------------------------- + * Retrieve the left record entry data. + */ + static SQInteger GetLeftRecordEntryData(HSQUIRRELVM vm) + { + return GetRecordEntryData(vm, false); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the left record entry data. + */ + static SQInteger GetRightRecordEntryData(HSQUIRRELVM vm) + { + return GetRecordEntryData(vm, true); + } + +protected: + + /* -------------------------------------------------------------------------------------------- + * Shared function to retrieve the left/right record entry data. + */ + static SQInteger GetRecordEntryData(HSQUIRRELVM vm, bool right); +}; + +} // Namespace:: SqMod diff --git a/module/Library/Utils/Vector.hpp b/module/Library/Utils/Vector.hpp index a9136e0f..c4894435 100644 --- a/module/Library/Utils/Vector.hpp +++ b/module/Library/Utils/Vector.hpp @@ -222,7 +222,7 @@ template < class T > struct SqVector { if (static_cast< size_t >(i) >= mC->size()) { - STHROWF("Invalid vector container index(" PRINT_INT_FMT ")", i); + STHROWF("Invalid vector container index(%d" PRINT_INT_FMT ")", i); } return *mC; } @@ -234,7 +234,7 @@ template < class T > struct SqVector { if (static_cast< size_t >(i) >= mC->size()) { - STHROWF("Invalid vector container index(" PRINT_INT_FMT ")", i); + STHROWF("Invalid vector container index(%d" PRINT_INT_FMT ")", i); } return *mC; } @@ -623,7 +623,7 @@ template < class T > struct SqVector Validate(); if (static_cast< size_t >(p) >= mC->size()) { - STHROWF("Invalid container index (" PRINT_INT_FMT ")", p); + STHROWF("Invalid container index (%d" PRINT_INT_FMT ")", p); } for (auto i = static_cast< size_t >(p); n--; ++i) { @@ -646,7 +646,7 @@ template < class T > struct SqVector } else if (static_cast< size_t >(p + n) >= mC->size()) { - STHROWF("Invalid container index (" PRINT_INT_FMT ")", p + n); + STHROWF("Invalid container index (%d" PRINT_INT_FMT ")", p + n); } for (n = (p + n); p <= n; ++p) { diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 9110bd97..140c7c80 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -3,6 +3,9 @@ add_subdirectory(Fmt) add_subdirectory(Squirrel) add_subdirectory(SimpleIni) add_subdirectory(TinyDir) +set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE) +set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE) +add_subdirectory(MaxmindDB) set(USE_SYSTEM_CURL ON CACHE INTERNAL "" FORCE) set(BUILD_CPR_TESTS OFF CACHE INTERNAL "" FORCE) set(BUILD_CPR_TESTS_SSL OFF CACHE INTERNAL "" FORCE) diff --git a/vendor/MaxmindDB/AUTHORS b/vendor/MaxmindDB/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/vendor/MaxmindDB/CMakeLists.txt b/vendor/MaxmindDB/CMakeLists.txt new file mode 100644 index 00000000..4a26fca0 --- /dev/null +++ b/vendor/MaxmindDB/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required (VERSION 3.9) +project(maxminddb + LANGUAGES C + VERSION 1.5.0 +) +set(MAXMINDDB_SOVERSION 0.0.7) + +option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF) +option(BUILD_TESTING "Build test programs" OFF) + +include(CheckTypeSize) +check_type_size("unsigned __int128" UINT128) +check_type_size("unsigned int __attribute__((mode(TI)))" UINT128_USING_MODE) +if(HAVE_UINT128) + set(MMDB_UINT128_USING_MODE 0) + set(MMDB_UINT128_IS_BYTE_ARRAY 0) +elseif(HAVE_UINT128_USING_MODE) + set(MMDB_UINT128_USING_MODE 1) + set(MMDB_UINT128_IS_BYTE_ARRAY 0) +else() + set(MMDB_UINT128_USING_MODE 0) + set(MMDB_UINT128_IS_BYTE_ARRAY 1) +endif() + +include (TestBigEndian) +TEST_BIG_ENDIAN(IS_BIG_ENDIAN) + +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif() + +configure_file(${PROJECT_SOURCE_DIR}/include/maxminddb_config.h.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/include/maxminddb_config.h) + +add_library(maxminddb + src/maxminddb.c + src/data-pool.c +) +add_library(maxminddb::maxminddb ALIAS maxminddb) + +set_target_properties(maxminddb PROPERTIES VERSION ${MAXMINDDB_SOVERSION}) + +target_compile_definitions(maxminddb PUBLIC PACKAGE_VERSION="${PROJECT_VERSION}") + +if(NOT IS_BIG_ENDIAN) + target_compile_definitions(maxminddb PRIVATE MMDB_LITTLE_ENDIAN=1) +endif() + +if(MSVC) + target_compile_definitions(maxminddb PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + +if(WIN32) + target_link_libraries(maxminddb ws2_32) +endif() + +set(CMAKE_SHARED_LIBRARY_PREFIX lib) +set(CMAKE_STATIC_LIBRARY_PREFIX lib) + +target_include_directories(maxminddb PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(maxminddb PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set(MAXMINDB_HEADERS + include/maxminddb.h + include/maxminddb_config.h +) +set_target_properties(maxminddb PROPERTIES PUBLIC_HEADER "${MAXMINDB_HEADERS}") + +#install(TARGETS maxminddb +# EXPORT maxminddb +# ARCHIVE DESTINATION lib +# PUBLIC_HEADER DESTINATION include/ +#) + +# This is required to work with FetchContent +#install(EXPORT maxminddb +# FILE maxminddb-config.cmake +# NAMESPACE maxminddb:: +# DESTINATION lib/cmake/maxminddb) + +# We always want to build mmdblookup +add_subdirectory(bin) + +if (BUILD_TESTING) + enable_testing() + add_subdirectory(t) +endif() + diff --git a/vendor/MaxmindDB/Changes.md b/vendor/MaxmindDB/Changes.md new file mode 100644 index 00000000..5888d4be --- /dev/null +++ b/vendor/MaxmindDB/Changes.md @@ -0,0 +1,333 @@ +## 1.5.0 - 2021-01-05 + +* A CMake build script has been added for Windows builds. The Visual + Studio project files in `projects` are now considered deprecated and will + be removed in a future release. + + +## 1.4.3 - 2020-08-06 + +* On Windows, always call `CreateFileW` instead of `CreateFile`. + `CreateFile` could be mapped to `CreateFileA` and not work as expected. + Pull request by Sandu Liviu Catalin. GitHub #228. +* Fixed use of uninitialized memory in `dump_entry_data_list()` that could + cause a heap buffer overflow in `mmdblookup`. As part of this fix, most + uses of `malloc` were replaced with `calloc`. Reported by azhou. GitHub + #236. + + +## 1.4.2 - 2019-11-02 + +* The 1.4.0 release introduced a change that increased the size of `MMDB_s`, + unintentionally causing an ABI break. This release reverts the relevant + commit. + + +## 1.4.1 - 2019-11-01 + +* The man page links for function calls were not generated correctly in + 1.4.0. This has been corrected. + + +## 1.4.0 - 2019-11-01 + +* A negative array index may now be used with `MMDB_get_value`, + `MMDB_vget_value`, and `MMDB_aget_value`. This specifies the element + from the end of the array. For instance, `-1` would refer to the + last element of the array. PR by Kyle Box. GitHub #205. +* On Windows, the file name passed to `MMDB_open` is now expected to be + UTF-8 encoded. This allows Unicode characters to be used in file names. + As part of this change, `mmdblookup` on Windows now converts its + arguments to UTF-8. PR by Gerald Combs. GitHub #189 & #191. +* Fix a memory leak that occurred when freeing an `MMDB_s` where the + database had no languages defined in the metadata. If you are using an + official MaxMind database, this leak does not affect you. Pull request + by Kókai Péter. GitHub #180. +* Add `--disable-binaries` option to `configure`. Pull request by Fabrice + Fontaine. GitHub #166. +* Previous releases incorrectly included `*.Po` files in the `t` directory. + This has been corrected. Reported by Daniel Macks. GitHub #168. +* The internal use of the `MMDB_s` now has the `const` modifier. Public + functions that accepted an `MMDB_s` as an argument now also declare it as + `const`. Pull request by Kurt Johnson. GitHub #199. +* `mmdblookup` now displays the prefix length for the record when using + the verbose flag. GitHub #172. + + +## 1.3.2 - 2018-01-17 + +* Allocate memory for `MMDB_entry_data_list_s` structs in separate chunks + rather than one large chunk. This simplifies accessing memory in + `MMDB_get_entry_data_list()` and increases performance. It builds on the + changes in 1.3.0 and 1.3.1. +* We no longer export `data_pool_*` symbols. These are internal functions + but we were previously exporting them. Pull request by Faidon Liambotis. + GitHub #162. +* Build with POSIX.1-2008 by default if the system supports it. This allows + use of `open()` with `O_CLOEXEC`. We retain support for systems that + provide only POSIX.1-2001. +* Open the database with the `O_CLOEXEC` flag if the system provides it. + This avoids cases where we could leak fds when called in multi-threaded + programs that `fork()` and `exec()`. Original report and PR by Brandon L + Black. +* Added a test to ensure we export only intended symbols (e.g. MMDB_*). + + +## 1.3.1 - 2017-11-24 + +* Fix build problems related to `rpl_malloc()`. Pull request by Rainer + Gerhards. GitHub #152. +* Fix a race to set and read data in a field on the `MMDB_s` struct + (`ipv4_start_node`). GitHub #153. +* Fix cases of invalid memory access when using + `MMDB_get_entry_data_list()`. This was introduced in 1.3.0 and occurred + when performing large lookups. GitHub #153. + + +## 1.3.0 - 2017-11-10 + +* Perform fewer memory allocations in `MMDB_get_entry_data_list()`. This + significantly improves its performance. GitHub #147. +* Fix `mmdblookup`'s build epoch reporting on some systems. Big endian + systems with a 32-bit `time_t` no longer show a database build date of + 1970-01-01 00:00:00. Pull request by Rainer Jung. GitHub #143. + + +## 1.2.1 - 2017-05-15 + +* Use autoconf to check the system's endianness rather than trying to do this + with compiler-defined macros like `__BYTE_ORDER__`. Apparently this didn't + work properly on a Sparc system. GitHub #120. +* Several compiler warnings on Visual C++ were fixed. Pull request by Marcel + Raad. GitHub #130. +* Fix segmentation faults found in `MMDB_open()` using afl-fuzz. This + occurred on corrupt databases that had a data pointer large enough to + cause an integer overflow when doing bound checking. Reported by Ryan + Whitworth. GitHub #140. +* Add --disable-tests option to `configure`. Pull request by Fabrice + Fontaine. GitHub #136. + + +## 1.2.0 - 2016-03-23 + +* Four additional fields were added to the end of the `MMDB_search_node_s` + struct returned by `MMDB_read_node`. These fields allow the user to iterate + through the search tree without making undocumented assumptions about how + this library works internally and without knowing the specific details of + the database format. GitHub #110. + + +## 1.1.5 - 2016-03-20 + +* Previously, reading a database with a pointer in the metadata would cause an + `MMDB_INVALID_METADATA_ERROR` to be returned. This was due to an invalid + offset being used when calculating the pointer. The `data_section` and + `metadata_section` fields now both point to the beginning of the data + section. Previously, `data_section` pointed to the beginning of the data + separator. This will not affect anyone using only documented fields from + `MMDB_s`. +* `MMDB_lookup_sockaddr` will set `mmdb_error` to + `MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR` if an IPv6 `sockaddr` is looked up + in an IPv4-only database. Previously only `MMDB_lookup_string` would set + this error code. +* When resolving an address, this library now relies on `getaddrinfo` to + determine the address family rather than trying to guess it itself. + + +## 1.1.4 - 2016-01-06 + +* Packaging fixes. The 1.1.3 tarball release contained a lot of extra junk in + the t/ directory. + + +## 1.1.3 - 2016-01-05 + +* Added several additional checks to make sure that we don't attempt to read + past the end of the databases's data section. Implemented by Tobias + Stoeckmann. GitHub #103. +* When searching for the database metadata, there was a bug that caused the + code to think it had found valid metadata when none existed. In addition, + this could lead to an attempt to read past the end of the database + entirely. Finally, if there are multiple metadata markers in the database, + we treat the final one as the start of the metdata, instead of the first. + Implemented by Tobias Stoeckmann. GitHub #102. +* Don't attempt to mmap a file that is too large to be mmapped on the + system. Implemented by Tobias Stoeckmann. GitHub #101. +* Added a missing out of memory check when reading a file's + metadata. Implemented by Tobias Stoeckmann. GitHub #101. +* Added several additional checks to make sure that we never attempt to + `malloc` more than `SIZE_MAX` memory, which would lead to integer + overflow. This could only happen with pathological databases. Implemented by + Tobias Stoeckmann. GitHub #101. + + +## 1.1.2 - 2015-11-16 + +* IMPORTANT: This release includes a number of important security fixes. Among + these fixes is improved validation of the database metadata. Unfortunately, + MaxMind GeoIP2 and GeoLite2 databases created earlier than January 28, 2014 + had an invalid data type for the `record_size` in the metadata. Previously + these databases worked on little endian machines with libmaxminddb but did + not work on big endian machines. Due to increased safety checks when reading + the file, these databases will no longer work on any platform. If you are + using one of these databases, we recommend that you upgrade to the latest + GeoLite2 or GeoIP2 database +* Added pkg-config support. If your system supports it, then running `make + install` now installs a `libmaxminddb.pc` file for pkgconfig. Implemented by + Jan Vcelak. +* Several segmentation faults found with afl-fuzz were fixed. These were + caused by missing bounds checking and missing data type verification checks. +* `MMDB_get_entry_data_list` will now fail on data structures with a depth + greater than 512 and data structures that are cyclic. This should not + affect any known MaxMind DB in production. All databases produced by + MaxMind have a depth of less than five. + + +## 1.1.1 - 2015-07-22 + +* Added `maxminddb-compat-util.h` as a source file to dist. + + +## 1.1.0 - 2015-07-21 + +* Previously, when there was an error in `MMDB_open()`, `errno` would + generally be overwritten during cleanup, preventing a useful value from + being returned to the caller. This was changed so that the `errno` value + from the function call that caused the error is restored before returning to + the caller. In particular, this is important for `MMDB_IO_ERROR` errors as + checking `errno` is often the only way to determine what actually failed. +* If `mmap()` fails due to running out of memory space, an + `MMDB_OUT_OF_MEMORY_ERROR` is now returned from `MMDB_open` rather than an + `MMDB_IO_ERROR`. +* On Windows, the `CreateFileMappingA()` handle was not properly closed if + opening the database succeeded. Fixed by Bly Hostetler. GitHub #75 & #76. +* On Windows, we were not checking the return value of `CreateFileMappingA()` + properly for errors. Fixed by Bly Hotetler. GitHub #78. +* Several warnings from Clang's scan-build were fixed. GitHub #86. +* All headers are now installed in `$(includedir)`. GitHub #89. +* We no longer install `maxminddb-compat-util.h`. This header was intended for + internal use only. + + +## 1.0.4 - 2015-01-02 + +* If you used a non-integer string as an array index when doing a lookup with + `MMDB_get_value()`, `MMDB_vget_value()`, or `MMDB_aget_value()`, the first + element of the array would be returned rather than an error. A + `MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR` error will now be returned. + GitHub #61. +* If a number larger than `LONG_MAX` was used in the same functions, + `LONG_MAX` would have been used in the lookup. Now a + `MMDB_INVALID_LOOKUP_PATH_ERROR` error will be returned. +* Visual Studio build files were added for unit tests and some compatibility + issues with the tests were fixed. +* Visual Studio project was updated to use property pages. Patch by Andre. + GitHub #69. +* A test failure in `t/compile_c++_t.pl` on new installs was fixed. + + +## 1.0.3 - 2014-12-02 + +* A memory and file handle leak on Win32 was fixed when getting the database + size fails. Patch by Federico G. Schwindt. GitHub PR #49. +* Documentation fix. Federico G. Schwindt. GitHub PR #50. +* Added Visual Studio build files and fixed incorrect CreateFileMappingA + usage. Patch by Andre. GitHub #52. +* The includes for the Windows header files were made lowercase in order to + match the actual file names on case-sensitive file systems. GitHub PR #57. +* Removed `realloc()` calls that caused warnings on Windows and generally + cleaned up memory allocation in `MMDB_vget_value()`. See relevant discussion + in GitHub #52. +* Added an `extern "C" { ... }` wrapper to maxminddb.h when compiling with a + C++ compiler. GitHub #55. + + +## 1.0.2 - 2014-09-22 + +* Fixed a number of small issues found by Coverity. +* When freeing the MMDB struct in `MMDB_close()` we make sure to set the + pointers to NULL after freeing the memory they point to. This makes it safe + to call `MMDB_close` more than once on the same `MMDB_s` struct + pointer. Before this change, calling this function twice on the same pointer + could cause the code to free memory that belonged to something else in the + process. Patch by Shuxin Yang. GitHub PR #41. + + +## 1.0.1 - 2014-09-03 + +* Added missing LICENSE and NOTICE files to distribution. No code changes. + + +## 1.0.0 - 2014-09-02 + +* Bumped version to 1.0.0. No code changes. + + +## 0.5.6 - 2014-07-21 + +* There was a leak in the `MMDB_open()` sub when it was called against a file + which did not contain any MMDB metadata. Reported by Federico + G. Schwindt. GitHub issue #36. +* Fixed an error that occurred when passing AI_V4MAPPED to `getaddrinfo()` on + FreeBSD. Apparently this macro is defined but doesn't work the way we + expected it to on that platform. +* Made sure to call `freeaddrinfo()` when a call to `getaddrinfo()` fails but + still allocated memory. +* Fixed a segfault in the tests that occurred on FreeBSD if we passed a NULL + value to `freeaddrinfo()`. +* Added a missing step to the README.md file for installing from our GitHub + repository. Patch by Yasith Fernando. +* Added instructions for installing via Homebrew. Patch by Yasith Fernando. + + +## 0.5.5 - 2014-03-11 + +* The previous tarball failed to compile because it was missing the + src/maxminddb-compat-util.h file. Reported by Günter Grodotzki. GitHub issue + #18. + + +## 0.5.4 - 2014-03-03 + +* Added support for compiling in the MinGW environment. Patch by Michael + Eisendle. +* Added const declarations to many spots in the public API. None of these + should require changes to existing code. +* Various documentation improvements. +* Changed the license to the Apache 2.0 license. + + +## 0.5.3 - 2013-12-23 + +* The internal value_for_key_as_uint16 method was returning a uint32_t instead + of a uint16_t. Reported by Robert Wells. GitHub issue #11. +* The ip_version member of the MMDB_metadata_s struct was a uint8_t, even + though the docs and spec said it should be a uint16_t. Reported by Robert + Wells. GitHub issue #11. +* The mmdblookup_t.pl test now reports that it needs IPC::Run3 to run (which + it always did, but it didn't tell you this). Patch by Elan Ruusamäe. GitHub + issue #10. + + +## 0.5.2 - 2013-11-20 + +* Running `make` from the tarball failed. This is now fixed. + + +## 0.5.1 - 2013-11-20 + +* Renamed MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA define to + MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR for consistency. Fixes github + issue #5. Reported by Albert Strasheim. +* Updated README.md to show git clone with --recursive flag so you get the + needed submodules. Fixes github issue #4. Reported by Ryan Peck. +* Fixed some bugs with the MMDB_get_*value functions when navigating a data + structure that included pointers. Fixes github issue #3. Reported by + bagadon. +* Fixed compilation problems on OSX and OpenBSD. We have tested this on OSX + and OpenBSD 5.4. Fixes github issue #6. +* Removed some unneeded memory allocations and added const to many variable + declarations. Based on patches by Timo Teräs. Github issue #8. +* Added a test that uses threads to check for thread safety issue in the + library. +* Distro tarball now includes man pages, tests, and test data diff --git a/vendor/MaxmindDB/LICENSE b/vendor/MaxmindDB/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/vendor/MaxmindDB/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/MaxmindDB/Makefile.am b/vendor/MaxmindDB/Makefile.am new file mode 100644 index 00000000..dad33346 --- /dev/null +++ b/vendor/MaxmindDB/Makefile.am @@ -0,0 +1,48 @@ + +include_HEADERS = include/maxminddb.h + +nodist_include_HEADERS = include/maxminddb_config.h + +SUBDIRS = \ + src + +if BINARIES +SUBDIRS += \ + bin +endif + +if TESTS +SUBDIRS += \ + t +endif + +EXTRA_DIST = doc Changes.md LICENSE NOTICE README.md projects/VS12 projects/VS12-tests \ + CMakeLists.txt t/CMakeLists.txt bin/CMakeLists.txt \ + include/maxminddb_config.h.cmake.in + +dist-hook: + dev-bin/make-man-pages.pl $(distdir) + find $(distdir) -name '.git*' | xargs rm -fr + +safedist: clean dist + tmpdir="$${TMPDIR-/tmp}/safedist-$$$$" \ + && mkdir "$$tmpdir" \ + && tar -xvf $(distdir).tar.gz --directory "$$tmpdir" \ + && $(am__cd) "$$tmpdir/$(distdir)" \ + && ./configure \ + && make -j 4 check + +man1_MANS = man/man1/*.1 + +man3_MANS = man/man3/*.3 + +man/man1/*.1: + if [ ! -f man/man1/mmdblookup.1 ]; then mkdir -p man/man1 && touch man/man1/mmdblookup.1; fi + +man/man3/*.3: + if [ ! -f man/man3/libmaxminddb.3 ]; then mkdir -p man/man3 && touch man/man3/libmaxminddb.3; fi + +release: + dev-bin/make-release.sh + +.PHONY: man/man1/*.1 man/man3/*.3 release diff --git a/vendor/MaxmindDB/NOTICE b/vendor/MaxmindDB/NOTICE new file mode 100644 index 00000000..6b869475 --- /dev/null +++ b/vendor/MaxmindDB/NOTICE @@ -0,0 +1,13 @@ +Copyright 2013-2014 MaxMind, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/vendor/MaxmindDB/README.dev.md b/vendor/MaxmindDB/README.dev.md new file mode 100644 index 00000000..ea70388b --- /dev/null +++ b/vendor/MaxmindDB/README.dev.md @@ -0,0 +1,69 @@ +# Releasing this library + +We release by uploading the tarball to GitHub, uploading Ubuntu PPAs, and by +updating the Homebrew recipe for this library. + +## Creating the release tarball +You may want to refer to the section about prerequisites. + +* Check whether there are any open issues to fix while you're doing this. +* Update `Changes.md` to include specify the new version, today's date, and + list relevant changes. Commit this. +* Run `./dev-bin/release.sh` to update various files in the distro, our + GitHub pages, and creates a GitHub release with the tarball. +* Check the release looks good on both GitHub and launchpad.net. + +## PPA + +In order to upload a PPA, you have to create a launchpad.net account and +register a GPG key with that account. You also need to be added to the MaxMind +team. Ask in the dev channel for someone to add you. See +https://help.launchpad.net/Packaging/PPA for more details. + +The PPA release script is at `dev-bin/ppa-release.sh`. Running it should +guide you though the release, although it may require some changes to run on +configurations different than Greg's machine. + +Check whether any new Ubuntu versions need to be listed in this script +before running it. + +You should run it from `master`. + +## Homebrew + +* Go to https://github.com/Homebrew/homebrew-core/edit/master/Formula/libmaxminddb.rb +* Edit the file to update the url and sha256. You can get the sha256 for the + tarball with the `sha256sum` command line utility. +* Make a commit with the summary `libmaxminddb ` +* Submit a PR with the changes you just made. + +# Prerequisites for releasing + +* Required packages (Ubuntu Artful): vim git-core dput build-essential + autoconf automake libtool git-buildpackage libfile-slurp-perl pandoc + dirmngr libfile-slurp-tiny-perl libdatetime-perl debhelper dh-autoreconf + libipc-run3-perl libtest-output-perl devscripts +* Install [hub](https://github.com/github/hub/releases). (Using `./install` + from the tarball is fine) +* GitHub ssh key (e.g. in `~/.ssh/id_rsa`) +* Git config (e.g. `~/.gitconfig`) +* Import your GPG secret key (or create one if you don't have a suitable + one) + * `gpg --import /path/to/key` + * `gpg --edit-key KEYID` and trust it ultimately + * Ensure it shows with `gpg --list-secret-keys` +* You need to be invited to the launchpad.net MaxMind organization on your + launchpad.net account. +* You need your GPG key listed on your launchpad.net account + * You can add it in the web interface. It wants the output of + `gpg --fingerprint`. + * Part of the instructions involve having your key published on the + Ubuntu keyserver: + `gpg --keyserver keyserver.ubuntu.com --send-keys KEYID` + * You'll get an email with an encrypted payload that you need to decrypt + and follow the link to confirm it. +* Ensure `dch` knows your name and email. Refer to its man page for how to + tell it this. One way is to set the `DEBFULLNAME` and `DEBEMAIL` + environment variables. These should match your GPG key's name and email + exactly. This is what gets used in the Debian changelog as well as + defines what GPG key to use. diff --git a/vendor/MaxmindDB/README.md b/vendor/MaxmindDB/README.md new file mode 100644 index 00000000..0a85473b --- /dev/null +++ b/vendor/MaxmindDB/README.md @@ -0,0 +1,133 @@ +# About + +The libmaxminddb library provides a C library for reading MaxMind DB files, +including the GeoIP2 databases from MaxMind. This is a custom binary format +designed to facilitate fast lookups of IP addresses while allowing for great +flexibility in the type of data associated with an address. + +The MaxMind DB format is an open format. The spec is available at +http://maxmind.github.io/MaxMind-DB/. This spec is licensed under the Creative +Commons Attribution-ShareAlike 3.0 Unported License. + +See http://dev.maxmind.com/ for more details about MaxMind's GeoIP2 products. + +# License + +This library is licensed under the Apache License, Version 2. + +# Installation + +## From a Named Release Tarball + +**NOTE:** These instructions are for installation from the _named_ `.tar.gz` +tarballs on the [Releases](https://github.com/maxmind/libmaxminddb/releases) +page (e.g. `libmaxminddb-*.tar.gz`). + +This code is known to work with GCC 4.4+ and clang 3.2+. It should also work +on other compilers that supports C99, POSIX.1-2001, and the `-fms-extensions +flag` (or equivalent). The latter is needed to allow an anonymous union in a +structure. + +To install this code, run the following commands: + + $ ./configure + $ make + $ make check + $ sudo make install + $ sudo ldconfig + +You can skip the `make check` step but it's always good to know that tests are +passing on your platform. + +The `configure` script takes the standard options to set where files are +installed such as `--prefix`, etc. See `./configure --help` for details. + +If after installing, you receive an error that `libmaxminddb.so.0` is missing +you may need to add the `lib` directory in your `prefix` to your library path. +On most Linux distributions when using the default prefix (`/usr/local`), you +can do this by running the following commands: + + $ sudo sh -c "echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf" + $ ldconfig + +## From a GitHub "Source Code" Archive / Git Repo Clone (Achtung!) + +**NOTE:** These instructions are for installation from the GitHub "Source +Code" archives also available on the +[Releases](https://github.com/maxmind/libmaxminddb/releases) page (e.g. +`X.Y.Z.zip` or `X.Y.Z.tar.gz`), as well as installation directly from a clone +of the [Git repo](https://github.com/maxmind/libmaxminddb). Installation from +these sources are possible but will present challenges to users not +comfortable with manual dependency resolution. + +You will need `automake`, `autoconf`, and `libtool` installed +in addition to `make` and a compiler. + +You can clone this repository and build it by running: + + $ git clone --recursive https://github.com/maxmind/libmaxminddb + +After cloning, run `./bootstrap` from the `libmaxminddb` directory and then +follow the instructions for installing from a named release tarball as described above. + +## Using CMake + +We provide a CMake build script. This is primarily targeted at Windows users, +but it can be used in other circumstances where the Autotools script does not +work. + + $ cmake --build . + $ ctest -V . + $ cmake --build . --target install + +## On Ubuntu via PPA + +MaxMind provides a PPA for recent version of Ubuntu. To add the PPA to your +APT sources, run: + + $ sudo add-apt-repository ppa:maxmind/ppa + +Then install the packages by running: + + $ sudo apt update + $ sudo apt install libmaxminddb0 libmaxminddb-dev mmdb-bin + +## On OS X via Homebrew + +If you are on OS X and you have homebrew (see http://brew.sh/) you can install +libmaxminddb via brew. + + $ brew install libmaxminddb + +# Bug Reports + +Please report bugs by filing an issue with our GitHub issue tracker at +https://github.com/maxmind/libmaxminddb/issues + +# Dev Tools + +We have a few development tools under the `dev-bin` directory to make +development easier. These are written in Perl or shell. They are: + +* `uncrustify-all.sh` - This runs `uncrustify` on all the code. Please run + this before submitting patches. +* `valgrind-all.pl` - This runs Valgrind on the tests and `mmdblookup` to + check for memory leaks. + +# Creating a Release Tarball + +Use `make safedist` to check the resulting tarball. + +# Copyright and License + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/MaxmindDB/bin/CMakeLists.txt b/vendor/MaxmindDB/bin/CMakeLists.txt new file mode 100644 index 00000000..9026be83 --- /dev/null +++ b/vendor/MaxmindDB/bin/CMakeLists.txt @@ -0,0 +1,13 @@ +# getopt is required by mmdblookup which is not available by default on Windows +if(NOT WIN32) + add_executable(mmdblookup + mmdblookup.c + ) + + target_link_libraries(mmdblookup maxminddb pthread) + + install( + TARGETS mmdblookup + RUNTIME DESTINATION bin + ) +endif() diff --git a/vendor/MaxmindDB/bin/Makefile.am b/vendor/MaxmindDB/bin/Makefile.am new file mode 100644 index 00000000..c00ba95b --- /dev/null +++ b/vendor/MaxmindDB/bin/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/common.mk + +AM_LDFLAGS = $(top_builddir)/src/libmaxminddb.la + +bin_PROGRAMS = mmdblookup + +if !WINDOWS +AM_CPPFLAGS += -pthread +AM_LDFLAGS += -pthread +endif diff --git a/vendor/MaxmindDB/bin/mmdblookup.c b/vendor/MaxmindDB/bin/mmdblookup.c new file mode 100644 index 00000000..d7ec3fff --- /dev/null +++ b/vendor/MaxmindDB/bin/mmdblookup.c @@ -0,0 +1,762 @@ +#ifdef HAVE_CONFIG_H +#include +#endif +#include "maxminddb.h" +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#ifndef UNICODE +#define UNICODE +#endif +#include +#else +#include +#include +#endif + +#define LOCAL static + +LOCAL void usage(char *program, int exit_code, const char *error); +LOCAL const char **get_options( + int argc, + char **argv, + char **mmdb_file, + char **ip_address, + int *verbose, + int *iterations, + int *lookup_path_length, + int *const thread_count, + char **const ip_file); +LOCAL MMDB_s open_or_die(const char *fname); +LOCAL void dump_meta(MMDB_s *mmdb); +LOCAL bool lookup_from_file(MMDB_s *const mmdb, + char const *const ip_file, + bool const dump); +LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address, + const char **lookup_path, + int lookup_path_length, + bool verbose); +LOCAL int benchmark(MMDB_s *mmdb, int iterations); +LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr); +LOCAL void random_ipv4(char *ip); + +#ifndef _WIN32 +// These aren't with the automatically generated prototypes as we'd lose the +// enclosing macros. +static bool start_threaded_benchmark( + MMDB_s *const mmdb, + int const thread_count, + int const iterations); +static long double get_time(void); +static void *thread(void *arg); +#endif + +#ifdef _WIN32 +int wmain(int argc, wchar_t **wargv) +{ + // Convert our argument list from UTF-16 to UTF-8. + char **argv = (char **)calloc(argc, sizeof(char *)); + if (!argv) { + fprintf(stderr, "calloc(): %s\n", strerror(errno)); + exit(1); + } + for (int i = 0; i < argc; i++) { + int utf8_width; + char *utf8_string; + utf8_width = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, + NULL, NULL); + if (utf8_width < 1) { + fprintf(stderr, "WideCharToMultiByte() failed: %d\n", + GetLastError()); + exit(1); + } + utf8_string = calloc(utf8_width, sizeof(char)); + if (!utf8_string) { + fprintf(stderr, "calloc(): %s\n", strerror(errno)); + exit(1); + } + if (WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, utf8_string, + utf8_width, NULL, NULL) < 1) { + fprintf(stderr, "WideCharToMultiByte() failed: %d\n", + GetLastError()); + exit(1); + } + argv[i] = utf8_string; + } +#else // _WIN32 +int main(int argc, char **argv) +{ +#endif // _WIN32 + char *mmdb_file = NULL; + char *ip_address = NULL; + int verbose = 0; + int iterations = 0; + int lookup_path_length = 0; + int thread_count = 0; + char *ip_file = NULL; + + const char **lookup_path = + get_options(argc, argv, &mmdb_file, &ip_address, &verbose, &iterations, + &lookup_path_length, &thread_count, &ip_file); + + MMDB_s mmdb = open_or_die(mmdb_file); + + if (verbose) { + dump_meta(&mmdb); + } + + // The benchmarking and lookup from file modes are hidden features mainly + // intended for development right now. This means there are several flags + // that exist but are intentionally not mentioned in the usage or man page. + + // The lookup from file mode may be useful to expose publicly in the usage, + // but we should have it respect the lookup_path functionality if we do so. + if (ip_file) { + free((void *)lookup_path); + if (!lookup_from_file(&mmdb, ip_file, verbose == 1)) { + MMDB_close(&mmdb); + return 1; + } + MMDB_close(&mmdb); + return 0; + } + + if (0 == iterations) { + exit(lookup_and_print(&mmdb, ip_address, lookup_path, + lookup_path_length, verbose)); + } + + free((void *)lookup_path); + + srand( (int)time(NULL) ); + +#ifndef _WIN32 + if (thread_count > 0) { + if (!start_threaded_benchmark(&mmdb, thread_count, iterations)) { + MMDB_close(&mmdb); + exit(1); + } + MMDB_close(&mmdb); + exit(0); + } +#endif + + exit(benchmark(&mmdb, iterations)); +} + +LOCAL void usage(char *program, int exit_code, const char *error) +{ + if (NULL != error) { + fprintf(stderr, "\n *ERROR: %s\n", error); + } + + char *usage = "\n" + " %s --file /path/to/file.mmdb --ip 1.2.3.4 [path to lookup]\n" + "\n" + " This application accepts the following options:\n" + "\n" + " --file (-f) The path to the MMDB file. Required.\n" + "\n" + " --ip (-i) The IP address to look up. Required.\n" + "\n" + " --verbose (-v) Turns on verbose output. Specifically, this causes this\n" + " application to output the database metadata.\n" + "\n" + " --version Print the program's version number and exit.\n" + "\n" + " --help (-h -?) Show usage information.\n" + "\n" + " If an IP's data entry resolves to a map or array, you can provide\n" + " a lookup path to only show part of that data.\n" + "\n" + " For example, given a JSON structure like this:\n" + "\n" + " {\n" + " \"names\": {\n" + " \"en\": \"Germany\",\n" + " \"de\": \"Deutschland\"\n" + " },\n" + " \"cities\": [ \"Berlin\", \"Frankfurt\" ]\n" + " }\n" + "\n" + " You could look up just the English name by calling mmdblookup with a lookup path of:\n" + "\n" + " mmdblookup --file ... --ip ... names en\n" + "\n" + " Or you could look up the second city in the list with:\n" + "\n" + " mmdblookup --file ... --ip ... cities 1\n" + "\n" + " Array numbering begins with zero (0).\n" + "\n" + " If you do not provide a path to lookup, all of the information for a given IP\n" + " will be shown.\n" + "\n"; + + fprintf(stdout, usage, program); + exit(exit_code); +} + +LOCAL const char **get_options( + int argc, + char **argv, + char **mmdb_file, + char **ip_address, + int *verbose, + int *iterations, + int *lookup_path_length, + int *const thread_count, + char **const ip_file) +{ + static int help = 0; + static int version = 0; + + while (1) { + static struct option options[] = { + { "file", required_argument, 0, 'f' }, + { "ip", required_argument, 0, 'i' }, + { "verbose", no_argument, 0, 'v' }, + { "version", no_argument, 0, 'n' }, + { "benchmark", required_argument, 0, 'b' }, +#ifndef _WIN32 + { "threads", required_argument, 0, 't' }, +#endif + { "ip-file", required_argument, 0, 'I' }, + { "help", no_argument, 0, 'h' }, + { "?", no_argument, 0, 1 }, + { 0, 0, 0, 0 } + }; + + int opt_index; +#ifdef _WIN32 + char const * const optstring = "f:i:b:I:vnh?"; +#else + char const * const optstring = "f:i:b:t:I:vnh?"; +#endif + int opt_char = getopt_long(argc, argv, optstring, options, + &opt_index); + + if (-1 == opt_char) { + break; + } + + if ('f' == opt_char) { + *mmdb_file = optarg; + } else if ('i' == opt_char) { + *ip_address = optarg; + } else if ('v' == opt_char) { + *verbose = 1; + } else if ('n' == opt_char) { + version = 1; + } else if ('b' == opt_char) { + *iterations = strtol(optarg, NULL, 10); + } else if ('h' == opt_char || '?' == opt_char) { + help = 1; + } else if (opt_char == 't') { + *thread_count = strtol(optarg, NULL, 10); + } else if (opt_char == 'I') { + *ip_file = optarg; + } + } + +#ifdef _WIN32 + char *program = alloca(strlen(argv[0])); + _splitpath(argv[0], NULL, NULL, program, NULL); + _splitpath(argv[0], NULL, NULL, NULL, program + strlen(program)); +#else + char *program = basename(argv[0]); +#endif + + if (help) { + usage(program, 0, NULL); + } + + if (version) { + fprintf(stdout, "\n %s version %s\n\n", program, PACKAGE_VERSION); + exit(0); + } + + if (NULL == *mmdb_file) { + usage(program, 1, "You must provide a filename with --file"); + } + + if (*ip_address == NULL && *iterations == 0 && !*ip_file) { + usage(program, 1, "You must provide an IP address with --ip"); + } + + const char **lookup_path = + calloc((argc - optind) + 1, sizeof(const char *)); + int i; + for (i = 0; i < argc - optind; i++) { + lookup_path[i] = argv[i + optind]; + (*lookup_path_length)++; + } + lookup_path[i] = NULL; + + return lookup_path; +} + +LOCAL MMDB_s open_or_die(const char *fname) +{ + MMDB_s mmdb; + int status = MMDB_open(fname, MMDB_MODE_MMAP, &mmdb); + + if (MMDB_SUCCESS != status) { + fprintf(stderr, "\n Can't open %s - %s\n", fname, + MMDB_strerror(status)); + + if (MMDB_IO_ERROR == status) { + fprintf(stderr, " IO error: %s\n", strerror(errno)); + } + + fprintf(stderr, "\n"); + + exit(2); + } + + return mmdb; +} + +LOCAL void dump_meta(MMDB_s *mmdb) +{ + const char *meta_dump = "\n" + " Database metadata\n" + " Node count: %i\n" + " Record size: %i bits\n" + " IP version: IPv%i\n" + " Binary format: %i.%i\n" + " Build epoch: %llu (%s)\n" + " Type: %s\n" + " Languages: "; + + char date[40]; + const time_t epoch = (const time_t)mmdb->metadata.build_epoch; + strftime(date, 40, "%F %T UTC", gmtime(&epoch)); + + fprintf(stdout, meta_dump, + mmdb->metadata.node_count, + mmdb->metadata.record_size, + mmdb->metadata.ip_version, + mmdb->metadata.binary_format_major_version, + mmdb->metadata.binary_format_minor_version, + mmdb->metadata.build_epoch, + date, + mmdb->metadata.database_type); + + for (size_t i = 0; i < mmdb->metadata.languages.count; i++) { + fprintf(stdout, "%s", mmdb->metadata.languages.names[i]); + if (i < mmdb->metadata.languages.count - 1) { + fprintf(stdout, " "); + } + } + fprintf(stdout, "\n"); + + fprintf(stdout, " Description:\n"); + for (size_t i = 0; i < mmdb->metadata.description.count; i++) { + fprintf(stdout, " %s: %s\n", + mmdb->metadata.description.descriptions[i]->language, + mmdb->metadata.description.descriptions[i]->description); + } + fprintf(stdout, "\n"); +} + +// The input file should have one IP per line. +// +// We look up each IP. +// +// If dump is true, we dump the data for each IP to stderr. This is useful for +// comparison in that you can dump out the data for the IPs before and after +// making changes. It goes to stderr rather than stdout so that the report does +// not get included in what you will compare (since it will almost always be +// different). +// +// In addition to being useful for comparisons, this function provides a way to +// have a more deterministic set of lookups for benchmarking. +LOCAL bool lookup_from_file(MMDB_s *const mmdb, + char const *const ip_file, + bool const dump) +{ + FILE *const fh = fopen(ip_file, "r"); + if (!fh) { + fprintf(stderr, "fopen(): %s: %s\n", ip_file, strerror(errno)); + return false; + } + + clock_t const clock_start = clock(); + char buf[1024] = { 0 }; + // I'd normally use uint64_t, but support for it is optional in C99. + unsigned long long i = 0; + while (1) { + if (fgets(buf, sizeof(buf), fh) == NULL) { + if (!feof(fh)) { + fprintf(stderr, "fgets(): %s\n", strerror(errno)); + fclose(fh); + return false; + } + if (fclose(fh) != 0) { + fprintf(stderr, "fclose(): %s\n", strerror(errno)); + return false; + } + break; + } + + char *ptr = buf; + while (*ptr != '\0') { + if (*ptr == '\n') { + *ptr = '\0'; + break; + } + ptr++; + } + if (strlen(buf) == 0) { + continue; + } + + i++; + + MMDB_lookup_result_s result = lookup_or_die(mmdb, buf); + if (!result.found_entry) { + continue; + } + + MMDB_entry_data_list_s *entry_data_list = NULL; + int const status = MMDB_get_entry_data_list(&result.entry, + &entry_data_list); + if (status != MMDB_SUCCESS) { + fprintf(stderr, "MMDB_get_entry_data_list(): %s\n", + MMDB_strerror(status)); + fclose(fh); + MMDB_free_entry_data_list(entry_data_list); + return false; + } + + if (!entry_data_list) { + fprintf(stderr, "entry_data_list is NULL\n"); + fclose(fh); + return false; + } + + if (dump) { + fprintf(stdout, "%s:\n", buf); + int const status = MMDB_dump_entry_data_list(stderr, + entry_data_list, 0); + if (status != MMDB_SUCCESS) { + fprintf(stderr, "MMDB_dump_entry_data_list(): %s\n", + MMDB_strerror(status)); + fclose(fh); + MMDB_free_entry_data_list(entry_data_list); + return false; + } + } + + MMDB_free_entry_data_list(entry_data_list); + } + + clock_t const clock_diff = clock() - clock_start; + double const seconds = (double)clock_diff / CLOCKS_PER_SEC; + + fprintf( + stdout, + "Looked up %llu addresses in %.2f seconds. %.2f lookups per second.\n", + i, seconds, i / seconds); + + return true; +} + +LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address, + const char **lookup_path, + int lookup_path_length, + bool verbose) +{ + + MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address); + MMDB_entry_data_list_s *entry_data_list = NULL; + + int exit_code = 0; + + if (verbose) { + fprintf( + stdout, + "\n Record prefix length: %d\n", + result.netmask + ); + } + + if (result.found_entry) { + int status; + if (lookup_path_length) { + MMDB_entry_data_s entry_data; + status = MMDB_aget_value(&result.entry, &entry_data, + lookup_path); + if (MMDB_SUCCESS == status) { + if (entry_data.offset) { + MMDB_entry_s entry = + { .mmdb = mmdb, .offset = entry_data.offset }; + status = MMDB_get_entry_data_list(&entry, + &entry_data_list); + } else { + fprintf( + stdout, + "\n No data was found at the lookup path you provided\n\n"); + } + } + } else { + status = MMDB_get_entry_data_list(&result.entry, + &entry_data_list); + } + + if (MMDB_SUCCESS != status) { + fprintf(stderr, "Got an error looking up the entry data - %s\n", + MMDB_strerror(status)); + exit_code = 5; + goto end; + } + + if (NULL != entry_data_list) { + fprintf(stdout, "\n"); + MMDB_dump_entry_data_list(stdout, entry_data_list, 2); + fprintf(stdout, "\n"); + } + } else { + fprintf(stderr, + "\n Could not find an entry for this IP address (%s)\n\n", + ip_address); + exit_code = 6; + } + + end: + MMDB_free_entry_data_list(entry_data_list); + MMDB_close(mmdb); + free((void *)lookup_path); + + return exit_code; +} + +LOCAL int benchmark(MMDB_s *mmdb, int iterations) +{ + char ip_address[16]; + int exit_code = 0; + + clock_t time = clock(); + + for (int i = 0; i < iterations; i++) { + random_ipv4(ip_address); + + MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address); + MMDB_entry_data_list_s *entry_data_list = NULL; + + if (result.found_entry) { + + int status = MMDB_get_entry_data_list(&result.entry, + &entry_data_list); + + if (MMDB_SUCCESS != status) { + fprintf(stderr, "Got an error looking up the entry data - %s\n", + MMDB_strerror(status)); + exit_code = 5; + MMDB_free_entry_data_list(entry_data_list); + goto end; + } + } + + MMDB_free_entry_data_list(entry_data_list); + } + + time = clock() - time; + double seconds = ((double)time / CLOCKS_PER_SEC); + fprintf( + stdout, + "\n Looked up %i addresses in %.2f seconds. %.2f lookups per second.\n\n", + iterations, seconds, iterations / seconds); + + end: + MMDB_close(mmdb); + + return exit_code; +} + +LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr) +{ + int gai_error, mmdb_error; + MMDB_lookup_result_s result = + MMDB_lookup_string(mmdb, ipstr, &gai_error, &mmdb_error); + + if (0 != gai_error) { + fprintf(stderr, + "\n Error from call to getaddrinfo for %s - %s\n\n", + ipstr, +#ifdef _WIN32 + gai_strerrorA(gai_error) +#else + gai_strerror(gai_error) +#endif + ); + exit(3); + } + + if (MMDB_SUCCESS != mmdb_error) { + fprintf(stderr, "\n Got an error from the maxminddb library: %s\n\n", + MMDB_strerror(mmdb_error)); + exit(4); + } + + return result; +} + +LOCAL void random_ipv4(char *ip) +{ + // rand() is perfectly fine for this use case + // coverity[dont_call] + int ip_int = rand(); + uint8_t *bytes = (uint8_t *)&ip_int; + + snprintf(ip, 16, "%u.%u.%u.%u", + *bytes, *(bytes + 1), *(bytes + 2), *(bytes + 3)); +} + +#ifndef _WIN32 +struct thread_info { + pthread_t id; + int num; + MMDB_s *mmdb; + int iterations; +}; + +static bool start_threaded_benchmark( + MMDB_s *const mmdb, + int const thread_count, + int const iterations) +{ + struct thread_info *const tinfo = calloc(thread_count, + sizeof(struct thread_info)); + if (!tinfo) { + fprintf(stderr, "calloc(): %s\n", strerror(errno)); + return false; + } + + // Using clock() isn't appropriate for multiple threads. It's CPU time, not + // wall time. + long double const start_time = get_time(); + if (start_time == -1) { + free(tinfo); + return false; + } + + for (int i = 0; i < thread_count; i++) { + tinfo[i].num = i; + tinfo[i].mmdb = mmdb; + tinfo[i].iterations = iterations; + + if (pthread_create(&tinfo[i].id, NULL, &thread, &tinfo[i]) != 0) { + fprintf(stderr, "pthread_create() failed\n"); + free(tinfo); + return false; + } + } + + for (int i = 0; i < thread_count; i++) { + if (pthread_join(tinfo[i].id, NULL) != 0) { + fprintf(stderr, "pthread_join() failed\n"); + free(tinfo); + return false; + } + } + + free(tinfo); + + long double const end_time = get_time(); + if (end_time == -1) { + return false; + } + + long double const elapsed = end_time - start_time; + unsigned long long const total_ips = iterations * thread_count; + long double rate = total_ips; + if (elapsed != 0) { + rate = total_ips / elapsed; + } + + fprintf( + stdout, + "Looked up %llu addresses using %d threads in %.2Lf seconds. %.2Lf lookups per second.\n", + total_ips, thread_count, elapsed, rate); + + return true; +} + +static long double get_time(void) +{ + // clock_gettime() is not present on OSX until 10.12. +#ifdef HAVE_CLOCK_GETTIME + struct timespec tp = { + .tv_sec = 0, + .tv_nsec = 0, + }; + clockid_t clk_id = CLOCK_REALTIME; +#ifdef _POSIX_MONOTONIC_CLOCK + clk_id = CLOCK_MONOTONIC; +#endif + if (clock_gettime(clk_id, &tp) != 0) { + fprintf(stderr, "clock_gettime(): %s\n", strerror(errno)); + return -1; + } + return tp.tv_sec + ((float)tp.tv_nsec / 1e9); +#else + time_t t = time(NULL); + if (t == (time_t)-1) { + fprintf(stderr, "time(): %s\n", strerror(errno)); + return -1; + } + return (long double)t; +#endif +} + +static void *thread(void *arg) +{ + const struct thread_info *const tinfo = arg; + if (!tinfo) { + fprintf(stderr, "thread(): %s\n", strerror(EINVAL)); + return NULL; + } + + char ip_address[16] = { 0 }; + + for (int i = 0; i < tinfo->iterations; i++) { + memset(ip_address, 0, 16); + random_ipv4(ip_address); + + MMDB_lookup_result_s result = lookup_or_die(tinfo->mmdb, ip_address); + if (!result.found_entry) { + continue; + } + + MMDB_entry_data_list_s *entry_data_list = NULL; + int const status = MMDB_get_entry_data_list(&result.entry, + &entry_data_list); + if (status != MMDB_SUCCESS) { + fprintf(stderr, "MMDB_get_entry_data_list(): %s\n", + MMDB_strerror(status)); + MMDB_free_entry_data_list(entry_data_list); + return NULL; + } + + if (!entry_data_list) { + fprintf(stderr, "entry_data_list is NULL\n"); + return NULL; + } + + MMDB_free_entry_data_list(entry_data_list); + } + + return NULL; +} +#endif diff --git a/vendor/MaxmindDB/bootstrap b/vendor/MaxmindDB/bootstrap new file mode 100644 index 00000000..b48f6386 --- /dev/null +++ b/vendor/MaxmindDB/bootstrap @@ -0,0 +1,21 @@ +#! /bin/sh + +# make sure to use the installed libtool +if [ -f ltmain.sh ]; then + rm ltmain.sh +fi +autoreconf -fiv + +################################################### +# the steps below may help with outdated toolsets + +# disable dependency trackeing for OS X with multiply arch option's +# automake -i --gnu --add-missing + + +#aclocal \ +#&& automake -i --gnu --add-missing \ +#&& autoconf + +#LIBTOOLIZE=$( which libtoolize glibtoolize | head -1 ) +#$LIBTOOLIZE -f diff --git a/vendor/MaxmindDB/common.mk b/vendor/MaxmindDB/common.mk new file mode 100644 index 00000000..0ee15587 --- /dev/null +++ b/vendor/MaxmindDB/common.mk @@ -0,0 +1,7 @@ +if DEBUG +AM_CFLAGS=-O0 -g -Wall -Wextra +else +AM_CFLAGS=-O2 -g +endif + +AM_CPPFLAGS = -I$(top_srcdir)/include diff --git a/vendor/MaxmindDB/configure.ac b/vendor/MaxmindDB/configure.ac new file mode 100644 index 00000000..916fdf26 --- /dev/null +++ b/vendor/MaxmindDB/configure.ac @@ -0,0 +1,138 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.63]) +AC_INIT([libmaxminddb], [1.5.0], [support@maxmind.com]) +AC_CONFIG_SRCDIR([include/maxminddb.h]) +AC_CONFIG_HEADERS([config.h include/maxminddb_config.h]) + +PKG_PROG_PKG_CONFIG +m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR], [AC_SUBST([pkgconfigdir], [${libdir}/pkgconfig])]) +AC_CONFIG_FILES([src/libmaxminddb.pc]) + +LT_INIT +AM_INIT_AUTOMAKE(foreign m4_esyscmd([case `automake --version | head -n 1` in + *1.14*) echo subdir-objects;; + *1.11*);; + *) echo serial-tests;; + esac])) +AC_PROG_LIBTOOL +# Checks for programs. +AC_PROG_CC_C99 + +# Copied from http://stackoverflow.com/a/10682813/9832 and tweaked for C (as +# opposed to C++) +# +# AX_CHECK_CFLAGS(ADDITIONAL-CFLAGS, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND) +# +# checks whether the $(CC) compiler accepts the ADDITIONAL-CFLAGS +# if so, they are added to the CXXFLAGS +AC_DEFUN([AX_CHECK_CFLAGS], +[ + AC_MSG_CHECKING([whether compiler accepts "$1"]) + cat > conftest.c << EOF + int main(){ + return 0; + } +EOF + if $CC $CFLAGS -o conftest.o conftest.c [$1] > /dev/null 2>&1 + then + AC_MSG_RESULT([yes]) + CFLAGS="${CFLAGS} [$1]" + [$2] + else + AC_MSG_RESULT([no]) + [$3] + fi +])dnl AX_CHECK_CFLAGS + +AX_CHECK_CFLAGS([-fms-extensions]) + +# We will add this back for non-debug builds in the common.mk file +CFLAGS=`echo ${CFLAGS} | sed 's/-O2//'` +CXXFLAGS=`echo ${CXXFLAGS} | sed 's/-O2//'` + +# autoconf insists on giving us gnu99 if it's available +CC=`echo ${CC} | sed 's/-std=gnu99/-std=c99/'` + +AC_C_RESTRICT + +AC_CHECK_HEADERS([arpa/inet.h assert.h fcntl.h inttypes.h libgen.h math.h netdb.h netinet/in.h stdarg.h stdbool.h stdint.h stdio.h stdlib.h string.h sys/mman.h sys/socket.h sys/stat.h sys/time.h sys/types.h unistd.h]) + +# configure generates an invalid config for MinGW because of the type checks +# so we only run them on non MinGW-Systems. For MinGW we also need to link +# against ws2_32. +AC_CANONICAL_HOST +is_windows=false +case $host_os in + mingw*) + LDFLAGS="-lws2_32" + is_windows=true + ;; + *) + AC_TYPE_OFF_T + AC_TYPE_SIZE_T + AC_TYPE_SSIZE_T + AC_TYPE_UINT8_T + AC_TYPE_UINT32_T + AC_TYPE_UINT64_T + ;; +esac + +AM_CONDITIONAL([WINDOWS], [test x$is_windows = xtrue]) + +# This check is backwards in order to make life easier for people writing +# extensions in other languages that link to this library. If they want to +# simply assume that they are using a newish compiler, they don't need to +# check for this type nor do they need to define anything on the CLI. They'll +# just get code that assumes this type exists. +AC_CHECK_TYPE( + [unsigned __int128], + [AC_DEFINE([MMDB_UINT128_IS_BYTE_ARRAY], [0], [Missing the unsigned __int128 type])], + [AC_CHECK_TYPE( + [unsigned int __attribute__((mode(TI)))], + [AC_DEFINE([MMDB_UINT128_IS_BYTE_ARRAY], [0], [Missing the unsigned __int128 type]) + AC_DEFINE([MMDB_UINT128_USING_MODE], [1], [int128 types are available with __attribute__((mode(TI)))])], + [AC_DEFINE([MMDB_UINT128_IS_BYTE_ARRAY], [1], [Missing the unsigned __int128 type])])]) + +AC_CHECK_TYPES([boolean]) + +AC_CHECK_FUNCS([clock_gettime open_memstream]) + +AC_C_BIGENDIAN( + [AC_DEFINE([MMDB_LITTLE_ENDIAN], [0], [System is big-endian])], + [AC_DEFINE([MMDB_LITTLE_ENDIAN], [1], [System is little-endian])]) + +AC_FUNC_MMAP + +AC_SEARCH_LIBS([fabs], [m]) +AC_SEARCH_LIBS([fabsf], [m]) +AC_SEARCH_LIBS([getaddrinfo], [socket]) + +AC_ARG_ENABLE( + [debug], + [ --enable-debug Turn on debugging], + [case "${enableval}" in + yes) debug=true ;; + no) debug=false ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; + esac],[debug=false]) +AM_CONDITIONAL([DEBUG], [test x$debug = xtrue]) + +AC_ARG_ENABLE([binaries], + AS_HELP_STRING([--enable-binaries], [Compilation of binaries code]), + [enable_binaries=${enableval}], + [enable_binaries=yes]) +AM_CONDITIONAL([BINARIES], [test "${enable_binaries}" = "yes"]) + +AC_ARG_ENABLE([tests], + AS_HELP_STRING([--enable-tests], [Compilation of tests code]), + [enable_tests=${enableval}], + [enable_tests=yes]) +AM_CONDITIONAL([TESTS], [test "${enable_tests}" = "yes"]) + +AC_CONFIG_FILES([Makefile + src/Makefile + bin/Makefile + t/Makefile]) +AC_OUTPUT diff --git a/vendor/MaxmindDB/dev-bin/make-man-pages.pl b/vendor/MaxmindDB/dev-bin/make-man-pages.pl new file mode 100644 index 00000000..b6d84a20 --- /dev/null +++ b/vendor/MaxmindDB/dev-bin/make-man-pages.pl @@ -0,0 +1,76 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use autodie qw( :all ); + +use FindBin qw( $Bin ); + +use File::Path qw( mkpath ); +use File::Slurp qw( edit_file read_file write_file ); +use File::Temp qw( tempdir ); +use File::Which qw( which ); + +sub main { + my $target = shift || "$Bin/.."; + + my $pandoc = which('pandoc') + or die + "\n You must install pandoc in order to generate the man pages.\n\n"; + + _make_man( $target, 'libmaxminddb', 3 ); + _make_lib_man_links($target); + + _make_man( $target, 'mmdblookup', 1 ); +} + +sub _make_man { + my $target = shift; + my $name = shift; + my $section = shift; + + my $man_dir = "$target/man/man$section"; + mkpath($man_dir); + + my $tempdir = tempdir( CLEANUP => 1 ); + + my $markdown = <<"EOF"; +% $name($section) + +EOF + $markdown .= read_file("$Bin/../doc/$name.md"); + + my $tempfile = "$tempdir/$name.$section.md"; + write_file( $tempfile, $markdown ); + + my $man_file = "$man_dir/$name.$section"; + system( qw( pandoc -s -t man ), $tempfile, '-o', $man_file ); + + _fix_indentation($man_file); +} + +sub _make_lib_man_links { + my $target = shift; + + my $header = read_file("$Bin/../include/maxminddb.h"); + for my $proto ( $header =~ /^ *extern.+?(\w+)\(/gsm ) { + open my $fh, '>', "$target/man/man3/$proto.3"; + print {$fh} ".so man3/libmaxminddb.3\n"; + close $fh; + } +} + +# AFAICT there's no way to control the indentation depth for code blocks with +# Pandoc. +sub _fix_indentation { + my $file = shift; + + edit_file( + sub { + s/^\.IP\n\.nf/.IP "" 4\n.nf/gm; + }, + $file + ); +} + +main(shift); diff --git a/vendor/MaxmindDB/dev-bin/ppa-release.sh b/vendor/MaxmindDB/dev-bin/ppa-release.sh new file mode 100644 index 00000000..1f4a6ec1 --- /dev/null +++ b/vendor/MaxmindDB/dev-bin/ppa-release.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e +set -x +set -u + +DISTS=( artful zesty xenial trusty precise ) + +VERSION=$(perl -MFile::Slurp::Tiny=read_file -MDateTime <now->ymd eq \$2; +say \$1; +EOF +) + +RESULTS=/tmp/build-libmaxminddb-results/ +SRCDIR="$RESULTS/libmaxminddb" + +mkdir -p "$SRCDIR" + +# gbp does weird things without a pristine checkout +git clone git@github.com:maxmind/libmaxminddb.git -b ubuntu-ppa $SRCDIR + +pushd "$SRCDIR" +git merge "$VERSION" + +for dist in "${DISTS[@]}"; do + dch -v "$VERSION-0+maxmind1~$dist" -D "$dist" -u low "New upstream release." + gbp buildpackage -S --git-ignore-new + + git clean -xfd + git reset HEAD --hard +done + +read -e -p "Release to PPA? (y/n)" SHOULD_RELEASE + +if [ "$SHOULD_RELEASE" != "y" ]; then + echo "Aborting" + exit 1 +fi + +# Upload to launchpad +dput ppa:maxmind/ppa ../*source.changes + +# Make the changelog up to date in git + +dch -v "$VERSION-0+maxmind1" -D "${DISTS[0]}" -u low "New upstream release." + +git add debian/changelog +git commit -m "Update debian/changelog for $VERSION" +git push diff --git a/vendor/MaxmindDB/dev-bin/regen-win32-test-projs.pl b/vendor/MaxmindDB/dev-bin/regen-win32-test-projs.pl new file mode 100644 index 00000000..bc76b703 --- /dev/null +++ b/vendor/MaxmindDB/dev-bin/regen-win32-test-projs.pl @@ -0,0 +1,54 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use FindBin qw( $Bin ); + +use Data::UUID; +use File::Slurp qw( read_file write_file ); +use Path::Iterator::Rule; + +sub main { + my $rule = Path::Iterator::Rule->new; + $rule->file->name(qr/_t.c$/); + + my $ug = Data::UUID->new; + + my $template = read_file("$Bin/../projects/test.vcxproj.template"); + + my @names; + for my $file ( $rule->all("$Bin/../t/") ) { + my ($name) = $file =~ /(\w*)_t.c$/; + + next unless $name; + next if $name eq 'threads'; + + push @names, $name; + + my $project = $template; + + $project =~ s/%TESTNAME%/$name/g; + + my $uuid = $ug->to_string( $ug->create ); + $project =~ s/%UUID%/$uuid/g; + + write_file( "$Bin/../projects/VS12-tests/$name.vcxproj", $project ); + } + + _modify_yml(@names); +} + +sub _modify_yml { + my @names = @_; + + my $exe_block = join "\n", + map { " - .\\projects\\VS12\\Debug\\test_${_}.exe" } @names; + + my $file = "$Bin/../appveyor.yml"; + my $config = read_file($file); + $config =~ s/(#EXES).*?(#ENDEXES)/$1\n$exe_block\n $2/s; + write_file( $file, $config ); +} + +main(); diff --git a/vendor/MaxmindDB/dev-bin/release.sh b/vendor/MaxmindDB/dev-bin/release.sh new file mode 100644 index 00000000..71f94c39 --- /dev/null +++ b/vendor/MaxmindDB/dev-bin/release.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +set -eu -o pipefail + +changelog=$(cat Changes.md) + +regex='## ([0-9]+\.[0-9]+\.[0-9]+) - ([0-9]{4}-[0-9]{2}-[0-9]{2}) + +((.| +)*) +' + +if [[ ! $changelog =~ $regex ]]; then + echo "Could not find date line in change log!" + exit 1 +fi + +version="${BASH_REMATCH[1]}" +date="${BASH_REMATCH[2]}" +notes="$(echo "${BASH_REMATCH[3]}" | sed -n -e '/^## [0-9]\+\.[0-9]\+\.[0-9]\+/,$!p')" + +dist="libmaxminddb-$version.tar.gz" + +if [[ "$date" != $(date +"%Y-%m-%d") ]]; then + echo "$date is not today!" + exit 1 +fi + +if [ -n "$(git status --porcelain)" ]; then + echo ". is not clean." >&2 + exit 1 +fi + +old_version=$(perl -MFile::Slurp=read_file <&2 + exit 1 +fi + +index=index.md +cat < $index +--- +layout: default +title: libmaxminddb - a library for working with MaxMind DB files +version: $version +--- +EOF + +cat ../doc/libmaxminddb.md >> $index + +mmdblookup=mmdblookup.md +cat < $mmdblookup +--- +layout: default +title: mmdblookup - a utility to look up an IP address in a MaxMind DB file +version: $version +--- +EOF + +cat ../doc/mmdblookup.md >> $mmdblookup + +git commit -m "Updated for $version" -a + +read -p "Push to origin? (y/n) " should_push + +if [ "$should_push" != "y" ]; then + echo "Aborting" + exit 1 +fi + +git push + +popd + +git push + +message="$version + +$notes" + +hub release create -a "$dist" -m "$message" "$version" diff --git a/vendor/MaxmindDB/dev-bin/uncrustify-all.sh b/vendor/MaxmindDB/dev-bin/uncrustify-all.sh new file mode 100644 index 00000000..12512be2 --- /dev/null +++ b/vendor/MaxmindDB/dev-bin/uncrustify-all.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +uncrustify="uncrustify -c .uncrustify.cfg --replace --no-backup" + +# We indent each thing twice because uncrustify is not idempotent - in some +# cases it will flip-flop between two indentation styles. +for dir in bin include src t; do + c_files=`find $dir -maxdepth 1 -name '*.c'` + if [ "$c_files" != "" ]; then + $uncrustify $dir/*.c; + $uncrustify $dir/*.c; + fi + + h_files=`find $dir -maxdepth 1 -name '*.h'` + if [ "$h_files" != "" ]; then + $uncrustify $dir/*.h; + $uncrustify $dir/*.h; + fi +done diff --git a/vendor/MaxmindDB/dev-bin/valgrind-all.pl b/vendor/MaxmindDB/dev-bin/valgrind-all.pl new file mode 100644 index 00000000..e9973df2 --- /dev/null +++ b/vendor/MaxmindDB/dev-bin/valgrind-all.pl @@ -0,0 +1,53 @@ +#!/usr/bin/env perl + +# Note to run this you will probably want to build with ./configure +# --disable-shared. You don't want to valgrind the libtool script. +# +# Also make sure you compile the tests first (`make check'). + +use strict; +use warnings; + +use File::Basename qw( basename ); +use FindBin qw( $Bin ); +use IPC::Run3; + +my $top_dir = "$Bin/.."; + +my $output; + +my @tests; +push @tests, glob "$top_dir/t/*_t"; +push @tests, glob "$top_dir/t/*-t"; + +my @mmdblookup = ( + "$top_dir/bin/mmdblookup", + '--file', "$top_dir/t/maxmind-db/test-data/MaxMind-DB-test-decoder.mmdb", + '--ip', +); + +# We want IPv4 and IPv6 addresses - one of each that exists in the db and one +# that doesn't +my @ips = ( '1.1.1.1', '10.0.0.0', 'abcd::', '0900::' ); + +my @cmds = ( + ( map { [ @mmdblookup, $_ ] } @ips ), + ( map { [$_] } @tests ), +); + +for my $cmd (@cmds) { + my $output; + run3( + [ qw( valgrind -v --leak-check=full --show-leak-kinds=all -- ), @{$cmd} ], + \undef, + \$output, + \$output, + ); + + $output =~ s/^(?!=).*\n//mg; + + my $marker = '-' x 60; + print $marker, "\n", ( join q{ }, basename( shift @{$cmd} ), @{$cmd} ), + "\n", $marker, "\n", $output, + "\n\n"; +} diff --git a/vendor/MaxmindDB/doc/libmaxminddb.md b/vendor/MaxmindDB/doc/libmaxminddb.md new file mode 100644 index 00000000..6e841cbc --- /dev/null +++ b/vendor/MaxmindDB/doc/libmaxminddb.md @@ -0,0 +1,897 @@ +# NAME + +libmaxminddb - a library for working with MaxMind DB files + +# SYNOPSIS + +```c +#include + +int MMDB_open( + const char *const filename, + uint32_t flags, + MMDB_s *const mmdb); +void MMDB_close(MMDB_s *const mmdb); + +MMDB_lookup_result_s MMDB_lookup_string( + MMDB_s *const mmdb, + const char *const ipstr, + int *const gai_error, + int *const mmdb_error); +MMDB_lookup_result_s MMDB_lookup_sockaddr( + MMDB_s *const mmdb, + const struct sockaddr *const + sockaddr, + int *const mmdb_error); + +int MMDB_get_value( + MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + ...); +int MMDB_vget_value( + MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + va_list va_path); +int MMDB_aget_value( + MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + const char *const *const path); + +int MMDB_get_entry_data_list( + MMDB_entry_s *start, + MMDB_entry_data_list_s **const entry_data_list); +void MMDB_free_entry_data_list( + MMDB_entry_data_list_s *const entry_data_list); +int MMDB_get_metadata_as_entry_data_list( + MMDB_s *const mmdb, + MMDB_entry_data_list_s **const entry_data_list); +int MMDB_dump_entry_data_list( + FILE *const stream, + MMDB_entry_data_list_s *const entry_data_list, + int indent); + +int MMDB_read_node( + MMDB_s *const mmdb, + uint32_t node_number, + MMDB_search_node_s *const node); + +const char *MMDB_lib_version(void); +const char *MMDB_strerror(int error_code); + +typedef struct MMDB_lookup_result_s { + bool found_entry; + MMDB_entry_s entry; + uint16_t netmask; +} MMDB_lookup_result_s; + +typedef struct MMDB_entry_data_s { + bool has_data; + union { + uint32_t pointer; + const char *utf8_string; + double double_value; + const uint8_t *bytes; + uint16_t uint16; + uint32_t uint32; + int32_t int32; + uint64_t uint64; + {mmdb_uint128_t or uint8_t[16]} uint128; + bool boolean; + float float_value; + }; + ... + uint32_t data_size; + uint32_t type; +} MMDB_entry_data_s; + +typedef struct MMDB_entry_data_list_s { + MMDB_entry_data_s entry_data; + struct MMDB_entry_data_list_s *next; +} MMDB_entry_data_list_s; +``` + +# DESCRIPTION + +The libmaxminddb library provides functions for working MaxMind DB files. See +http://maxmind.github.io/MaxMind-DB/ for the MaxMind DB format specification. +The database and results are all represented by different data structures. +Databases are opened by calling `MMDB_open()`. You can look up IP addresses as +a string with `MMDB_lookup_string()` or as a pointer to a `sockaddr` +structure with `MMDB_lookup_sockaddr()`. + +If the lookup finds the IP address in the database, it returns a +`MMDB_lookup_result_s` structure. If that structure indicates that the database +has data for the IP, there are a number of functions that can be used to fetch +that data. These include `MMDB_get_value()` and `MMDB_get_entry_data_list()`. +See the function documentation below for more details. + +When you are done with the database handle you should call `MMDB_close()`. + +All publicly visible functions, structures, and macros begin with "MMDB_". + +# DATA STRUCTURES + +All data structures exported by this library's `maxminddb.h` header are +typedef'd in the form `typedef struct foo_s { ... } foo_s` so you can refer to +them without the `struct` prefix. + +This library provides the following data structures: + +## `MMDB_s` + +This is the handle for a MaxMind DB file. We only document some of this +structure's fields intended for public use. All other fields are subject to +change and are intended only for internal use. + +```c +typedef struct MMDB_s { + uint32_t flags; + const char *filename; + ... + MMDB_metadata_s metadata; +} MMDB_s; +``` + +* `uint32_t flags` - the flags this database was opened with. See the + `MMDB_open()` documentation for more details. +* `const char *filename` - the name of the file which was opened, as passed to + `MMDB_open()`. +* `MMDB_metadata_s metadata` - the metadata for the database. + +## `MMDB_metadata_s` and `MMDB_description_s` + +This structure can be retrieved from the `MMDB_s` structure. It contains the +metadata read from the database file. Note that you may find it more convenient +to access this metadata by calling `MMDB_get_metadata_as_entry_data_list()` +instead. + +```c +typedef struct MMDB_metadata_s { + uint32_t node_count; + uint16_t record_size; + uint16_t ip_version; + const char *database_type; + struct { + size_t count; + const char **names; + } languages; + uint16_t binary_format_major_version; + uint16_t binary_format_minor_version; + uint64_t build_epoch; + struct { + size_t count; + MMDB_description_s **descriptions; + } description; +} MMDB_metadata_s; + +typedef struct MMDB_description_s { + const char *language; + const char *description; +} MMDB_description_s; +``` + +These structures should be mostly self-explanatory. + +The `ip_version` member should always be `4` or `6`. The +`binary_format_major_version` should always be `2`. + +There is no requirement that the database metadata include languages or +descriptions, so the `count` for these parts of the metadata can be zero. All +of the other `MMDB_metadata_s` fields should be populated. + +## `MMDB_lookup_result_s` + +This structure is returned as the result of looking up an IP address. + +```c +typedef struct MMDB_lookup_result_s { + bool found_entry; + MMDB_entry_s entry; + uint16_t netmask; +} MMDB_lookup_result_s; +``` + +If the `found_entry` member is false then the other members of this structure +do not contain meaningful values. Always check that `found_entry` is true +first. + +The `entry` member is used to look up the data associated with the IP address. + +The `netmask` member tells you what subnet the IP address belongs to in this +database. For example, if you look up the address `1.1.1.1` in an IPv4 database +and the returned `netmask` is 16, then the address is part of the `1.1.0.0/16` +subnet. + +If the database is an IPv6 database, the returned netmask is always an IPv6 +prefix length (from 0-128), even if that database *also* contains IPv4 +networks. If you look up an IPv4 address and would like to turn the netmask +into an IPv4 netmask value, you can simply subtract `96` from the value. + +## `MMDB_result_s` + +You don't really need to dig around in this structure. You'll get this from a +`MMDB_lookup_result_s` structure and pass it to various functions. + +## `MMDB_entry_data_s` + +This structure is used to return a single data section entry for an IP. These +entries can in turn point to other entries, as is the case for things like maps +and arrays. Some members of this structure are not documented as they are only +for internal use. + +```c +typedef struct MMDB_entry_data_s { + bool has_data; + union { + uint32_t pointer; + const char *utf8_string; + double double_value; + const uint8_t *bytes; + uint16_t uint16; + uint32_t uint32; + int32_t int32; + uint64_t uint64; + {mmdb_uint128_t or uint8_t[16]} uint128; + bool boolean; + float float_value; + }; + ... + uint32_t data_size; + uint32_t type; +} MMDB_entry_data_s; +``` + +The `has_data` member is true if data was found for a given lookup. See +`MMDB_get_value()` for more details. If this member is false then none of the +other values in the structure are meaningful. + +The union at the beginning of the structure defines the actual data. To +determine which union member is populated you should look at the `type` member. +The `pointer` member of the union should never be populated in any data +returned by the API. Pointers should always be resolved internally. + +The `data_size` member is only relevant for `utf8_string` and `bytes` data. +`utf8_string` is not null terminated and `data_size` _must_ be used to +determine its length. + +The `type` member can be compared to one of the `MMDB_DATA_TYPE_*` macros. + +### 128-bit Integers + +The handling of `uint128` data depends on how your platform supports 128-bit +integers, if it does so at all. With GCC 4.4 and 4.5 we can write `unsigned +int __attribute__ ((__mode__ (TI)))`. With newer versions of GCC (4.6+) and +clang (3.2+) we can simply write "unsigned __int128". + +In order to work around these differences, this library defines an +`mmdb_uint128_t` type. This type is defined in the `maxminddb.h` header so you +can use it in your own code. + +With older compilers, we can't use an integer so we instead use a 16 byte +array of `uint8_t` values. This is the raw data from the database. + +This library provides a public macro `MMDB_UINT128_IS_BYTE_ARRAY` macro. If +this is true (1), then `uint128` values are returned as a byte array, if it is +false then they are returned as a `mmdb_uint128_t` integer. + +### Data Type Macros + +This library provides a macro for every data type defined by the MaxMind DB +spec. + +* `MMDB_DATA_TYPE_UTF8_STRING` +* `MMDB_DATA_TYPE_DOUBLE` +* `MMDB_DATA_TYPE_BYTES` +* `MMDB_DATA_TYPE_UINT16` +* `MMDB_DATA_TYPE_UINT32` +* `MMDB_DATA_TYPE_MAP` +* `MMDB_DATA_TYPE_INT32` +* `MMDB_DATA_TYPE_UINT64` +* `MMDB_DATA_TYPE_UINT128` +* `MMDB_DATA_TYPE_ARRAY` +* `MMDB_DATA_TYPE_BOOLEAN` +* `MMDB_DATA_TYPE_FLOAT` + +There are also a few types that are for internal use only: + +* `MMDB_DATA_TYPE_EXTENDED` +* `MMDB_DATA_TYPE_POINTER` +* `MMDB_DATA_TYPE_CONTAINER` +* `MMDB_DATA_TYPE_END_MARKER` + +If you see one of these in returned data then something has gone very wrong. +The database is damaged or was generated incorrectly or there is a bug in the +libmaxminddb code. + +### Pointer Values and `MMDB_close()` + +The `utf8_string`, `bytes`, and (maybe) the `uint128` members of this structure +are all pointers directly into the database's data section. This can either be +a `calloc`'d or `mmap`'d block of memory. In either case, these pointers will +become invalid after `MMDB_close()` is called. + +If you need to refer to this data after that time you should copy the data +with an appropriate function (`strdup`, `memcpy`, etc.). + +## `MMDB_entry_data_list_s` + +This structure encapsulates a linked list of `MMDB_entry_data_s` structures. + +```c +typedef struct MMDB_entry_data_list_s { + MMDB_entry_data_s entry_data; + struct MMDB_entry_data_list_s *next; +} MMDB_entry_data_list_s; +``` + +This structure lets you look at entire map or array data entry by iterating +over the linked list. + +## `MMDB_search_node_s` + +This structure encapsulates the two records in a search node. This is really +only useful if you want to write code that iterates over the entire search +tree as opposed to looking up a specific IP address. + +```c +typedef struct MMDB_search_node_s { + uint64_t left_record; + uint64_t right_record; + uint8_t left_record_type; + uint8_t right_record_type; + MMDB_entry_s left_record_entry; + MMDB_entry_s right_record_entry; +} MMDB_search_node_s; +``` + +The two record types will take one of the following values: + +* `MMDB_RECORD_TYPE_SEARCH_NODE` - The record points to the next search node. +* `MMDB_RECORD_TYPE_EMPTY` - The record is a placeholder that indicates there + is no data for the IP address. The search should end here. +* `MMDB_RECORD_TYPE_DATA` - The record is for data in the data section of the + database. Use the entry for the record when looking up the data for the + record. +* `MMDB_RECORD_TYPE_INVALID` - The record is invalid. Either an invalid node + was looked up or the database is corrupt. + +The `MMDB_entry_s` for the record is only valid if the type is +`MMDB_RECORD_TYPE_DATA`. Attempts to use an entry for other record types will +result in an error or invalid data. + +# STATUS CODES + +This library returns (or populates) status codes for many functions. These +status codes are: + +* `MMDB_SUCCESS` - everything worked +* `MMDB_FILE_OPEN_ERROR` - there was an error trying to open the MaxMind DB + file. +* `MMDB_IO_ERROR` - an IO operation failed. Check `errno` for more details. +* `MMDB_CORRUPT_SEARCH_TREE_ERROR` - looking up an IP address in the search + tree gave us an impossible result. The database is damaged or was generated + incorrectly or there is a bug in the libmaxminddb code. +* `MMDB_INVALID_METADATA_ERROR` - something in the database is wrong. This + includes missing metadata keys as well as impossible values (like an + `ip_version` of 7). +* `MMDB_UNKNOWN_DATABASE_FORMAT_ERROR` - The database metadata indicates that + it's major version is not 2. This library can only handle major version 2. +* `MMDB_OUT_OF_MEMORY_ERROR` - a memory allocation call (`malloc`, etc.) + failed. +* `MMDB_INVALID_DATA_ERROR` - an entry in the data section contains invalid + data. For example, a `uint16` field is claiming to be more than 2 bytes long. + The database is probably damaged or was generated incorrectly. +* `MMDB_INVALID_LOOKUP_PATH_ERROR` - The lookup path passed to + `MMDB_get_value`, `MMDB_vget_value`, or `MMDB_aget_value` contains an array + offset that is larger than LONG_MAX or smaller than LONG_MIN. +* `MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR` - The lookup path passed to + `MMDB_get_value`,`MMDB_vget_value`, or `MMDB_aget_value` does not match the + data structure for the entry. There are number of reasons this can + happen. The lookup path could include a key not in a map. The lookup path + could include an array index larger than an array or smaller than the + minimum offset from the end of an array. It can also happen when the path + expects to find a map or array where none exist. + +All status codes should be treated as `int` values. + +## `MMDB_strerror()` + +```c +const char *MMDB_strerror(int error_code) +``` + +This function takes a status code and returns an English string explaining the +status. + +# FUNCTIONS + +This library provides the following exported functions: + +## `MMDB_open()` + +```c +int MMDB_open( + const char *const filename, + uint32_t flags, + MMDB_s *const mmdb); +``` + +This function opens a handle to a MaxMind DB file. Its return value is a +status code as defined above. Always check this call's return value. + +```c +MMDB_s mmdb; +int status = + MMDB_open("/path/to/file.mmdb", MMDB_MODE_MMAP, &mmdb); +if (MMDB_SUCCESS != status) { ... } +... +MMDB_close(&mmdb); +``` + +`filename` must be encoded as UTF-8 on Windows. + +The `MMDB_s` structure you pass in can be on the stack or allocated from the +heap. However, if the open is successful it will contain heap-allocated data, +so you need to close it with `MMDB_close()`. If the status returned is not +`MMDB_SUCCESS` then this library makes sure that all allocated memory is freed +before returning. + +The flags currently provided are: + +* `MMDB_MODE_MMAP` - open the database with `mmap()`. + +Passing in other values for `flags` may yield unpredictable results. In the +future we may add additional flags that you can bitwise-or together with the +mode, as well as additional modes. + +You can also pass `0` as the `flags` value in which case the database will be +opened with the default flags. However, these defaults may change in future +releases. The current default is `MMDB_MODE_MMAP`. + +## `MMDB_close()` + +```c +void MMDB_close(MMDB_s *const mmdb); +``` + +This frees any allocated or mmap'd memory that is held from the `MMDB_s` +structure. *It does not free the memory allocated for the structure itself!* +If you allocated the structure from the heap then you are responsible for +freeing it. + +## `MMDB_lookup_string()` + +```c +MMDB_lookup_result_s MMDB_lookup_string( + MMDB_s *const mmdb, + const char *const ipstr, + int *const gai_error, + int *const mmdb_error); +``` + +This function looks up an IP address that is passed in as a null-terminated +string. Internally it calls `getaddrinfo()` to resolve the address into a +binary form. It then calls `MMDB_lookup_sockaddr()` to look the address up in +the database. If you have already resolved an address you can call +`MMDB_lookup_sockaddr()` directly, rather than resolving the address twice. + +```c +int gai_error, mmdb_error; +MMDB_lookup_result_s result = + MMDB_lookup_string(&mmdb, "1.2.3.4", &gai_error, &mmdb_error); +if (0 != gai_error) { ... } +if (MMDB_SUCCESS != mmdb_error) { ... } + +if (result.found_entry) { ... } +``` + +This function always returns an `MMDB_lookup_result_s` structure, but you +should also check the `gai_error` and `mmdb_error` parameters. If either of +these indicates an error then the returned structure is meaningless. + +If no error occurred you still need to make sure that the `found_entry` member +in the returned result is true. If it's not, this means that the IP address +does not have an entry in the database. + +This function will work with IPv4 addresses even when the database contains +data for both IPv4 and IPv6 addresses. The IPv4 address will be looked up as +'::xxx.xxx.xxx.xxx' rather than being remapped to the `::ffff:xxx.xxx.xxx.xxx` +block allocated for IPv4-mapped IPv6 addresses. + +If you pass an IPv6 address to a database with only IPv4 data then the +`found_entry` member will be false, but the `mmdb_error` status will still be +`MMDB_SUCCESS`. + +## `MMDB_lookup_sockaddr()` + +```c +MMDB_lookup_result_s MMDB_lookup_sockaddr( + MMDB_s *const mmdb, + const struct sockaddr *const sockaddr, + int *const mmdb_error); +``` + +This function looks up an IP address that has already been resolved by +`getaddrinfo()`. + +Other than not calling `getaddrinfo()` itself, this function is identical to +the `MMDB_lookup_string()` function. + +```c +int mmdb_error; +MMDB_lookup_result_s result = + MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error); +if (MMDB_SUCCESS != mmdb_error) { ... } + +if (result.found_entry) { ... } +``` + +## Data Lookup Functions + +There are three functions for looking up data associated with an IP address. + +```c +int MMDB_get_value( + MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + ...); +int MMDB_vget_value( + MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + va_list va_path); +int MMDB_aget_value( + MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + const char *const *const path); +``` + +The three functions allow three slightly different calling styles, but they +all do the same thing. + +The first parameter is an `MMDB_entry_s` value. In most cases this will come +from the `MMDB_lookup_result_s` value returned by `MMDB_lookup_string()` or +`MMDB_lookup_sockaddr()`. + +The second parameter is a reference to an `MMDB_entry_data_s` structure. This +will be populated with the data that is being looked up, if any is found. If +nothing is found, then the `has_data` member of this structure will be false. +If `has_data` is true then you can look at the `data_type` member. + +The final parameter is a lookup path. The path consists of a set of strings +representing either map keys (e.g, "city") or array indexes (e.g., "0", "1", +"-1") to use in the lookup. + +Negative array indexes will be treated as an offset from the end of the array. +For instance, "-1" refers to the last element of the array. + +The lookup path allows you to navigate a complex data structure. For example, +given this data: + +```js +{ + "names": { + "en": "Germany", + "de": "Deutschland" + }, + "cities": [ "Berlin", "Frankfurt" ] +} +``` + +We could look up the English name with this code: + +```c +MMDB_lookup_result_s result = + MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error); +MMDB_entry_data_s entry_data; +int status = + MMDB_get_value(&result.entry, &entry_data, + "names", "en", NULL); +if (MMDB_SUCCESS != status) { ... } +if (entry_data.has_data) { ... } +``` + +If we wanted to find the first city the lookup path would be `"cities", +"0"`. If you don't provide a lookup path at all, you'll get the entry which +corresponds to the top level map. The lookup path must always end with `NULL`, +regardless of which function you call. + +The `MMDB_get_value` function takes a variable number of arguments. All of the +arguments after the `MMDB_entry_data_s *` structure pointer are the lookup +path. The last argument must be `NULL`. + +The `MMDB_vget_value` function accepts a `va_list` as the lookup path. The +last element retrieved by `va_arg()` must be `NULL`. + +Finally, the `MMDB_aget_value` accepts an array of strings as the lookup +path. The last member of this array must be `NULL`. + +If you want to get all of the entry data at once you can call +`MMDB_get_entry_data_list()` instead. + +For each of the three functions, the return value is a status code as +defined above. + +## `MMDB_get_entry_data_list()` + +```c +int MMDB_get_entry_data_list( + MMDB_entry_s *start, + MMDB_entry_data_list_s **const entry_data_list); +``` + +This function allows you to get all of the data for a complex data structure +at once, rather than looking up each piece using repeated calls to +`MMDB_get_value()`. + +```c +MMDB_lookup_result_s result = + MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error); +MMDB_entry_data_list_s *entry_data_list, *first; +int status = + MMDB_get_entry_data_list(&result.entry, &entry_data_list); +if (MMDB_SUCCESS != status) { ... } +// save this so we can free this data later +first = entry_data_list; + +while (1) { + MMDB_entry_data_list_s *next = entry_data_list = entry_data_list->next; + if (NULL == next) { + break; + } + + switch (next->entry_data.type) { + case MMDB_DATA_TYPE_MAP: { ... } + case MMDB_DATA_TYPE_UTF8_STRING: { ... } + ... + } + +} + +MMDB_free_entry_data_list(first); +``` + +It's up to you to interpret the `entry_data_list` data structure. The list is +linked in a depth-first traversal. Let's use this structure as an example: + +```js +{ + "names": { + "en": "Germany", + "de": "Deutschland" + }, + "cities": [ "Berlin", "Frankfurt" ] +} +``` + +The list will consist of the following items: + +1. MAP - top level map +2. UTF8_STRING - "names" key +3. MAP - map for "names" key +4. UTF8_STRING - "en" key +5. UTF8_STRING - value for "en" key +6. UTF8_STRING - "de" key +7. UTF8_STRING - value for "de" key +8. UTF8_STRING - "cities" key +9. ARRAY - value for "cities" key +10. UTF8_STRING - array[0] +11. UTF8_STRING - array[1] + +The return value of the function is a status code as defined above. + +## `MMDB_free_entry_data_list()` + +```c +void MMDB_free_entry_data_list( + MMDB_entry_data_list_s *const entry_data_list); +``` + +The `MMDB_get_entry_data_list()` and `MMDB_get_metadata_as_entry_data_list()` +functions will allocate the linked list structure from the heap. Call this +function to free the `MMDB_entry_data_list_s` structure. + +## `MMDB_get_metadata_as_entry_data_list()` + +```c +int MMDB_get_metadata_as_entry_data_list( + MMDB_s *const mmdb, + MMDB_entry_data_list_s **const entry_data_list); +``` + +This function allows you to retrieve the database metadata as a linked list of +`MMDB_entry_data_list_s` structures. This can be a more convenient way to deal +with the metadata than using the metadata structure directly. + +```c + MMDB_entry_data_list_s *entry_data_list, *first; + int status = + MMDB_get_metadata_as_entry_data_list(&mmdb, &entry_data_list); + if (MMDB_SUCCESS != status) { ... } + first = entry_data_list; + ... // do something with the data + MMDB_free_entry_data_list(first); +``` + +The return value of the function is a status code as defined above. + +## `MMDB_dump_entry_data_list()` + +```c +int MMDB_dump_entry_data_list( + FILE *const stream, + MMDB_entry_data_list_s *const entry_data_list, + int indent); +``` + +This function takes a linked list of `MMDB_entry_data_list_s` structures and +stringifies it to the given `stream`. The `indent` parameter is the starting +indent level for the generated output. It is incremented for nested data +structures (maps, array, etc.). + +The `stream` must be a file handle (`stdout`, etc). If your platform provides +something like the GNU `open_memstream()` you can use that to capture the +output as a string. + +The output is formatted in a JSON-ish fashion, but values are marked with their +data type (except for maps and arrays which are shown with "{}" and "[]" +respectively). + +The specific output format may change in future releases, so you should not +rely on the specific formatting produced by this function. It is intended to be +used to show data to users in a readable way and for debugging purposes. + +The return value of the function is a status code as defined above. + +## `MMDB_read_node()` + +```c +int MMDB_read_node( + MMDB_s *const mmdb, + uint32_t node_number, + MMDB_search_node_s *const node); +``` + +This reads a specific node in the search tree. The third argument is a +reference to an `MMDB_search_node_s` structure that will be populated by this +function. + +The return value is a status code. If you pass a `node_number` that is greater +than the number of nodes in the database, this function will return +`MMDB_INVALID_NODE_NUMBER_ERROR`, otherwise it will return `MMDB_SUCCESS`. + +The first node in the search tree is always node 0. If you wanted to iterate +over the whole search tree, you would start by reading node 0 and then +following the the records that make up this node, based on the type of each +record. If the type is `MMDB_RECORD_TYPE_SEARCH_NODE` then the record contains +an integer for the next node to look up. + +## `MMDB_lib_version()` + +```c +const char *MMDB_lib_version(void) +``` + +This function returns the library version as a string, something like "2.0.0". + +# EXAMPLE + +```c +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char *filename = argv[1]; + char *ip_address = argv[2]; + + MMDB_s mmdb; + int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb); + + if (MMDB_SUCCESS != status) { + fprintf(stderr, "\n Can't open %s - %s\n", + filename, MMDB_strerror(status)); + + if (MMDB_IO_ERROR == status) { + fprintf(stderr, " IO error: %s\n", strerror(errno)); + } + exit(1); + } + + int gai_error, mmdb_error; + MMDB_lookup_result_s result = + MMDB_lookup_string(&mmdb, ip_address, &gai_error, &mmdb_error); + + if (0 != gai_error) { + fprintf(stderr, + "\n Error from getaddrinfo for %s - %s\n\n", + ip_address, gai_strerror(gai_error)); + exit(2); + } + + if (MMDB_SUCCESS != mmdb_error) { + fprintf(stderr, + "\n Got an error from libmaxminddb: %s\n\n", + MMDB_strerror(mmdb_error)); + exit(3); + } + + MMDB_entry_data_list_s *entry_data_list = NULL; + + int exit_code = 0; + if (result.found_entry) { + int status = MMDB_get_entry_data_list(&result.entry, + &entry_data_list); + + if (MMDB_SUCCESS != status) { + fprintf( + stderr, + "Got an error looking up the entry data - %s\n", + MMDB_strerror(status)); + exit_code = 4; + goto end; + } + + if (NULL != entry_data_list) { + MMDB_dump_entry_data_list(stdout, entry_data_list, 2); + } + } else { + fprintf( + stderr, + "\n No entry for this IP address (%s) was found\n\n", + ip_address); + exit_code = 5; + } + + end: + MMDB_free_entry_data_list(entry_data_list); + MMDB_close(&mmdb); + exit(exit_code); +} +``` + +# THREAD SAFETY + +This library is thread safe when compiled and linked with a thread-safe +`malloc` and `free` implementation. + +# INSTALLATION AND SOURCE + +You can download the latest release of libmaxminddb +[from GitHub](https://github.com/maxmind/libmaxminddb/releases). + +[Our GitHub repo](https://github.com/maxmind/libmaxminddb) is publicly +available. Please fork it! + +# BUG REPORTS AND PULL REQUESTS + +Please report all issues to +[our GitHub issue tracker](https://github.com/maxmind/libmaxminddb/issues). We +welcome bug reports and pull requests. Please note that pull requests are +greatly preferred over patches. + +# AUTHORS + +This library was written by Boris Zentner (bzentner@maxmind.com) and Dave +Rolsky (drolsky@maxmind.com). + +# COPYRIGHT AND LICENSE + +Copyright 2013-2014 MaxMind, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +# SEE ALSO + +mmdblookup(1) diff --git a/vendor/MaxmindDB/doc/mmdblookup.md b/vendor/MaxmindDB/doc/mmdblookup.md new file mode 100644 index 00000000..2d700052 --- /dev/null +++ b/vendor/MaxmindDB/doc/mmdblookup.md @@ -0,0 +1,103 @@ +# NAME + +mmdblookup - a utility to look up an IP address in a MaxMind DB file + +# SYNOPSIS + +mmdblookup --file [FILE PATH] --ip [IP ADDRESS] [DATA PATH] + +# DESCRIPTION + +`mmdblookup` looks up an IP address in the specified MaxMind DB file. The +record for the IP address is displayed in a JSON-like structure with type +annotations. + +If an IP's data entry resolves to a map or array, you can provide a lookup +path to only show part of that data. + +For example, given a JSON structure like this: + +```js +{ + "names": { + "en": "Germany", + "de": "Deutschland" + }, + "cities": [ "Berlin", "Frankfurt" ] +} +``` + +You could look up just the English name by calling mmdblookup with a lookup +path of: + +```bash +mmdblookup --file ... --ip ... names en +``` + +Or you could look up the second city in the list with: + +```bash +mmdblookup --file ... --ip ... cities 1 +``` + +Array numbering begins with zero (0). + +If you do not provide a path to lookup, all of the information for a given IP +will be shown. + +# OPTIONS + +This application accepts the following options: + +-f, --file + +: The path to the MMDB file. Required. + +-i, --ip + +: The IP address to look up. Required. + +-v, --verbose + +: Turns on verbose output. Specifically, this causes this + application to output the database metadata. + +--version + +: Print the program's version number and exit. + +-h, -?, --help + +: Show usage information. + +# BUG REPORTS AND PULL REQUESTS + +Please report all issues to +[our GitHub issue tracker](https://github.com/maxmind/libmaxminddb/issues). We +welcome bug reports and pull requests. Please note that pull requests are +greatly preferred over patches. + +# AUTHORS + +This utility was written by Boris Zentner (bzentner@maxmind.com) and Dave +Rolsky (drolsky@maxmind.com). + +# COPYRIGHT AND LICENSE + +Copyright 2013-2014 MaxMind, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +# SEE ALSO + +libmaxminddb(3) diff --git a/vendor/MaxmindDB/include/maxminddb.h b/vendor/MaxmindDB/include/maxminddb.h new file mode 100644 index 00000000..40f74254 --- /dev/null +++ b/vendor/MaxmindDB/include/maxminddb.h @@ -0,0 +1,255 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MAXMINDDB_H +#define MAXMINDDB_H + +/* Request POSIX.1-2008. However, we want to remain compatible with + * POSIX.1-2001 (since we have been historically and see no reason to drop + * compatibility). By requesting POSIX.1-2008, we can conditionally use + * features provided by that standard if the implementation provides it. We can + * check for what the implementation provides by checking the _POSIX_VERSION + * macro after including unistd.h. If a feature is in POSIX.1-2008 but not + * POSIX.1-2001, check that macro before using the feature (or check for the + * feature directly if possible). */ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif + +#include "maxminddb_config.h" +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +/* libmaxminddb package version from configure */ +#define PACKAGE_VERSION "1.5.0" + +typedef ADDRESS_FAMILY sa_family_t; + +#if defined(_MSC_VER) +/* MSVC doesn't define signed size_t, copy it from configure */ +#define ssize_t SSIZE_T + +/* MSVC doesn't support restricted pointers */ +#define restrict +#endif +#else +#include +#include +#include +#endif + +#define MMDB_DATA_TYPE_EXTENDED (0) +#define MMDB_DATA_TYPE_POINTER (1) +#define MMDB_DATA_TYPE_UTF8_STRING (2) +#define MMDB_DATA_TYPE_DOUBLE (3) +#define MMDB_DATA_TYPE_BYTES (4) +#define MMDB_DATA_TYPE_UINT16 (5) +#define MMDB_DATA_TYPE_UINT32 (6) +#define MMDB_DATA_TYPE_MAP (7) +#define MMDB_DATA_TYPE_INT32 (8) +#define MMDB_DATA_TYPE_UINT64 (9) +#define MMDB_DATA_TYPE_UINT128 (10) +#define MMDB_DATA_TYPE_ARRAY (11) +#define MMDB_DATA_TYPE_CONTAINER (12) +#define MMDB_DATA_TYPE_END_MARKER (13) +#define MMDB_DATA_TYPE_BOOLEAN (14) +#define MMDB_DATA_TYPE_FLOAT (15) + +#define MMDB_RECORD_TYPE_SEARCH_NODE (0) +#define MMDB_RECORD_TYPE_EMPTY (1) +#define MMDB_RECORD_TYPE_DATA (2) +#define MMDB_RECORD_TYPE_INVALID (3) + +/* flags for open */ +#define MMDB_MODE_MMAP (1) +#define MMDB_MODE_MASK (7) + +/* error codes */ +#define MMDB_SUCCESS (0) +#define MMDB_FILE_OPEN_ERROR (1) +#define MMDB_CORRUPT_SEARCH_TREE_ERROR (2) +#define MMDB_INVALID_METADATA_ERROR (3) +#define MMDB_IO_ERROR (4) +#define MMDB_OUT_OF_MEMORY_ERROR (5) +#define MMDB_UNKNOWN_DATABASE_FORMAT_ERROR (6) +#define MMDB_INVALID_DATA_ERROR (7) +#define MMDB_INVALID_LOOKUP_PATH_ERROR (8) +#define MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR (9) +#define MMDB_INVALID_NODE_NUMBER_ERROR (10) +#define MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR (11) + +#if !(MMDB_UINT128_IS_BYTE_ARRAY) +#if MMDB_UINT128_USING_MODE +typedef unsigned int mmdb_uint128_t __attribute__ ((__mode__(TI))); +#else +typedef unsigned __int128 mmdb_uint128_t; +#endif +#endif + +/* This is a pointer into the data section for a given IP address lookup */ +typedef struct MMDB_entry_s { + const struct MMDB_s *mmdb; + uint32_t offset; +} MMDB_entry_s; + +typedef struct MMDB_lookup_result_s { + bool found_entry; + MMDB_entry_s entry; + uint16_t netmask; +} MMDB_lookup_result_s; + +typedef struct MMDB_entry_data_s { + bool has_data; + union { + uint32_t pointer; + const char *utf8_string; + double double_value; + const uint8_t *bytes; + uint16_t uint16; + uint32_t uint32; + int32_t int32; + uint64_t uint64; +#if MMDB_UINT128_IS_BYTE_ARRAY + uint8_t uint128[16]; +#else + mmdb_uint128_t uint128; +#endif + bool boolean; + float float_value; + }; + /* This is a 0 if a given entry cannot be found. This can only happen + * when a call to MMDB_(v)get_value() asks for hash keys or array + * indices that don't exist. */ + uint32_t offset; + /* This is the next entry in the data section, but it's really only + * relevant for entries that part of a larger map or array + * struct. There's no good reason for an end user to look at this + * directly. */ + uint32_t offset_to_next; + /* This is only valid for strings, utf8_strings or binary data */ + uint32_t data_size; + /* This is an MMDB_DATA_TYPE_* constant */ + uint32_t type; +} MMDB_entry_data_s; + +/* This is the return type when someone asks for all the entry data in a map or array */ +typedef struct MMDB_entry_data_list_s { + MMDB_entry_data_s entry_data; + struct MMDB_entry_data_list_s *next; + void *pool; +} MMDB_entry_data_list_s; + +typedef struct MMDB_description_s { + const char *language; + const char *description; +} MMDB_description_s; + +/* WARNING: do not add new fields to this struct without bumping the SONAME. + * The struct is allocated by the users of this library and increasing the + * size will cause existing users to allocate too little space when the shared + * library is upgraded */ +typedef struct MMDB_metadata_s { + uint32_t node_count; + uint16_t record_size; + uint16_t ip_version; + const char *database_type; + struct { + size_t count; + const char **names; + } languages; + uint16_t binary_format_major_version; + uint16_t binary_format_minor_version; + uint64_t build_epoch; + struct { + size_t count; + MMDB_description_s **descriptions; + } description; + /* See above warning before adding fields */ +} MMDB_metadata_s; + +/* WARNING: do not add new fields to this struct without bumping the SONAME. + * The struct is allocated by the users of this library and increasing the + * size will cause existing users to allocate too little space when the shared + * library is upgraded */ +typedef struct MMDB_ipv4_start_node_s { + uint16_t netmask; + uint32_t node_value; + /* See above warning before adding fields */ +} MMDB_ipv4_start_node_s; + +/* WARNING: do not add new fields to this struct without bumping the SONAME. + * The struct is allocated by the users of this library and increasing the + * size will cause existing users to allocate too little space when the shared + * library is upgraded */ +typedef struct MMDB_s { + uint32_t flags; + const char *filename; + ssize_t file_size; + const uint8_t *file_content; + const uint8_t *data_section; + uint32_t data_section_size; + const uint8_t *metadata_section; + uint32_t metadata_section_size; + uint16_t full_record_byte_size; + uint16_t depth; + MMDB_ipv4_start_node_s ipv4_start_node; + MMDB_metadata_s metadata; + /* See above warning before adding fields */ +} MMDB_s; + +typedef struct MMDB_search_node_s { + uint64_t left_record; + uint64_t right_record; + uint8_t left_record_type; + uint8_t right_record_type; + MMDB_entry_s left_record_entry; + MMDB_entry_s right_record_entry; +} MMDB_search_node_s; + +extern int MMDB_open(const char *const filename, uint32_t flags, + MMDB_s *const mmdb); +extern MMDB_lookup_result_s MMDB_lookup_string(const MMDB_s *const mmdb, + const char *const ipstr, + int *const gai_error, + int *const mmdb_error); +extern MMDB_lookup_result_s MMDB_lookup_sockaddr( + const MMDB_s *const mmdb, + const struct sockaddr *const sockaddr, + int *const mmdb_error); +extern int MMDB_read_node(const MMDB_s *const mmdb, + uint32_t node_number, + MMDB_search_node_s *const node); +extern int MMDB_get_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + ...); +extern int MMDB_vget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + va_list va_path); +extern int MMDB_aget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + const char *const *const path); +extern int MMDB_get_metadata_as_entry_data_list( + const MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list); +extern int MMDB_get_entry_data_list( + MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list); +extern void MMDB_free_entry_data_list( + MMDB_entry_data_list_s *const entry_data_list); +extern void MMDB_close(MMDB_s *const mmdb); +extern const char *MMDB_lib_version(void); +extern int MMDB_dump_entry_data_list(FILE *const stream, + MMDB_entry_data_list_s *const entry_data_list, + int indent); +extern const char *MMDB_strerror(int error_code); + +#endif /* MAXMINDDB_H */ + +#ifdef __cplusplus +} +#endif diff --git a/vendor/MaxmindDB/include/maxminddb_config.h.cmake.in b/vendor/MaxmindDB/include/maxminddb_config.h.cmake.in new file mode 100644 index 00000000..8b1977f8 --- /dev/null +++ b/vendor/MaxmindDB/include/maxminddb_config.h.cmake.in @@ -0,0 +1,14 @@ +#ifndef MAXMINDDB_CONFIG_H +#define MAXMINDDB_CONFIG_H + +#ifndef MMDB_UINT128_USING_MODE +/* Define as 1 if we use unsigned int __atribute__ ((__mode__(TI))) for uint128 values */ +#cmakedefine MMDB_UINT128_USING_MODE @MMDB_UINT128_USING_MODE@ +#endif + +#ifndef MMDB_UINT128_IS_BYTE_ARRAY +/* Define as 1 if we don't have an unsigned __int128 type */ +#cmakedefine MMDB_UINT128_IS_BYTE_ARRAY @MMDB_UINT128_IS_BYTE_ARRAY@ +#endif + +#endif /* MAXMINDDB_CONFIG_H */ diff --git a/vendor/MaxmindDB/include/maxminddb_config.h.in b/vendor/MaxmindDB/include/maxminddb_config.h.in new file mode 100644 index 00000000..314d559d --- /dev/null +++ b/vendor/MaxmindDB/include/maxminddb_config.h.in @@ -0,0 +1,14 @@ +#ifndef MAXMINDDB_CONFIG_H +#define MAXMINDDB_CONFIG_H + +#ifndef MMDB_UINT128_USING_MODE +/* Define as 1 if we use unsigned int __atribute__ ((__mode__(TI))) for uint128 values */ +#define MMDB_UINT128_USING_MODE 0 +#endif + +#ifndef MMDB_UINT128_IS_BYTE_ARRAY +/* Define as 1 if we don't have an unsigned __int128 type */ +#undef MMDB_UINT128_IS_BYTE_ARRAY +#endif + +#endif /* MAXMINDDB_CONFIG_H */ diff --git a/vendor/MaxmindDB/projects/VS12-tests/bad_pointers.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/bad_pointers.vcxproj new file mode 100644 index 00000000..a5769300 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/bad_pointers.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B19839F4-376C-11E7-A95B-48C58C130AD6} + Win32Proj + bad_pointers + test_bad_pointers + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/basic_lookup.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/basic_lookup.vcxproj new file mode 100644 index 00000000..236adefd --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/basic_lookup.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1983C10-376C-11E7-A95B-48C58C130AD6} + Win32Proj + basic_lookup + test_basic_lookup + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/data_entry_list.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/data_entry_list.vcxproj new file mode 100644 index 00000000..5337a643 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/data_entry_list.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1983E90-376C-11E7-A95B-48C58C130AD6} + Win32Proj + data_entry_list + test_data_entry_list + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/data_types.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/data_types.vcxproj new file mode 100644 index 00000000..65d7f45a --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/data_types.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B198400C-376C-11E7-A95B-48C58C130AD6} + Win32Proj + data_types + test_data_types + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/dump.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/dump.vcxproj new file mode 100644 index 00000000..5e99e54f --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/dump.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1984188-376C-11E7-A95B-48C58C130AD6} + Win32Proj + dump + test_dump + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/get_value.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/get_value.vcxproj new file mode 100644 index 00000000..252aae98 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/get_value.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1984480-376C-11E7-A95B-48C58C130AD6} + Win32Proj + get_value + test_get_value + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/get_value_pointer_bug.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/get_value_pointer_bug.vcxproj new file mode 100644 index 00000000..c73f7c58 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/get_value_pointer_bug.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1984304-376C-11E7-A95B-48C58C130AD6} + Win32Proj + get_value_pointer_bug + test_get_value_pointer_bug + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/ipv4_start_cache.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/ipv4_start_cache.vcxproj new file mode 100644 index 00000000..96dbc31e --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/ipv4_start_cache.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B198466A-376C-11E7-A95B-48C58C130AD6} + Win32Proj + ipv4_start_cache + test_ipv4_start_cache + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/ipv6_lookup_in_ipv4.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/ipv6_lookup_in_ipv4.vcxproj new file mode 100644 index 00000000..e60bb99a --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/ipv6_lookup_in_ipv4.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1984872-376C-11E7-A95B-48C58C130AD6} + Win32Proj + ipv6_lookup_in_ipv4 + test_ipv6_lookup_in_ipv4 + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/libtap.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/libtap.vcxproj new file mode 100644 index 00000000..57383bbd --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/libtap.vcxproj @@ -0,0 +1,85 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA} + Win32Proj + libtap + + + + StaticLibrary + true + Unicode + v120 + + + StaticLibrary + false + true + Unicode + v120 + + + + + + + + + + + + + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + /Z7 %(AdditionalOptions) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/MaxmindDB/projects/VS12-tests/maxminddb_test_helper.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/maxminddb_test_helper.vcxproj new file mode 100644 index 00000000..d7705a90 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/maxminddb_test_helper.vcxproj @@ -0,0 +1,107 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5} + Win32Proj + ConsoleApplication1 + test_maxminddb_test_helper + + + + StaticLibrary + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + true + true + true + true + false + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + true + true + true + true + false + + + + + + + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/metadata.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/metadata.vcxproj new file mode 100644 index 00000000..82710adb --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/metadata.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1984C6E-376C-11E7-A95B-48C58C130AD6} + Win32Proj + metadata + test_metadata + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/metadata_pointers.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/metadata_pointers.vcxproj new file mode 100644 index 00000000..357503b5 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/metadata_pointers.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B19849D0-376C-11E7-A95B-48C58C130AD6} + Win32Proj + metadata_pointers + test_metadata_pointers + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/no_map_get_value.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/no_map_get_value.vcxproj new file mode 100644 index 00000000..f8c38629 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/no_map_get_value.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B1984EC6-376C-11E7-A95B-48C58C130AD6} + Win32Proj + no_map_get_value + test_no_map_get_value + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/read_node.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/read_node.vcxproj new file mode 100644 index 00000000..66ab72c7 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/read_node.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B198504C-376C-11E7-A95B-48C58C130AD6} + Win32Proj + read_node + test_read_node + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/shared.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/shared.vcxproj new file mode 100644 index 00000000..3b7cff1a --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/shared.vcxproj @@ -0,0 +1,104 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5} + Win32Proj + ConsoleApplication1 + test_maxminddb_test_helper + + + + StaticLibrary + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + + + + NotUsing + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + /FS %(AdditionalOptions) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + true + true + true + true + false + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/MaxmindDB/projects/VS12-tests/threads.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/threads.vcxproj new file mode 100644 index 00000000..74cecbef --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/threads.vcxproj @@ -0,0 +1,103 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8E11E9FC-7B63-11E4-AE98-6B41E8A9DDB2} + Win32Proj + threads + test_threads + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12-tests/version.vcxproj b/vendor/MaxmindDB/projects/VS12-tests/version.vcxproj new file mode 100644 index 00000000..2f91a32b --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12-tests/version.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B19851FA-376C-11E7-A95B-48C58C130AD6} + Win32Proj + version + test_version + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12/README.md b/vendor/MaxmindDB/projects/VS12/README.md new file mode 100644 index 00000000..f930144c --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/README.md @@ -0,0 +1,64 @@ +# Deprecated + +*These files are deprecated and will be removed in a future release. +Please use CMake instead.* + +# Project Notes + +DO NOT modify project settings for each configuration and/or platform +on a per-project basis. Use property sheets to set shared configuration +properties. The rule of thumb - if you set same value for the same +property in more than one configuration or platform, you did it wrong. + +## libmaxminddb.props + +This is the base property sheet for libMaxMindDB project. It contains +settings that are shared between all configurations, such as compiler +warning level, not using link-time code generation, etc. + +In order to minimize the number of property sheet files, this propery +sheet also contains settings for Win32 and Debug configurations, which +are overridden by the x64 and Release property sheets described below. + +## libmaxminddb-release.props + +This property sheet contains all Release settings and is shared between +Win32 and x64 release builds. It must be located higher than the base +property sheet in the property Manager window for each configuration +where it's used (i.e. Release|Win32 and Release|x64). + +## libmaxminddb-x64.props + +This property sheet contains all x64 settings and is shared between all +Debug and Release x64 configurations. It must be located higher than the +base property sheet in the property Manager window for each configuration +where it's used (i.e. Debug|x64 and Release|x64). + +## Adding More Projects + +If you want to add more projects into this solution, follow the same logic +and create their own property sheets. Do not use libmaxminddb property +sheets in those projects because it will interfere with their intermediate +directories. Instead, copy and rename libmaxminddb property sheets as a +starting point. + +DO NOT add libmaxminddb.lib to the Additional Dependencies box of command +line tools or any other libraries you built, for that matter. This box is +only for standard Windows libraries. Instead, add libmaxminddb as a reference +to all command line tool projects. Do the same for any other library projects +you added to this solutionn. + +For external 3rd-party .lib files, create a solution folder called Libraries +and add Debug, Debug64, Release, Release64 subfolders, then drag and drop all +versinos of .lib to their respective folders and use Exclude From Build in +each library file's property to assign them to the proper build confguration. +Unused libraries will be shown with a traffic stop sign in each configuration. +If you have a lot of projects, it might be easier to do this by editing .vcxproj +and .vcxproj.filters in a text editor. + +# Tests + +To use the tests, you must download the `libtap` and `maxmind-db` submodules. +This can be done by running `git submodule update --init --recursive` from +the Git checkout. Each test source file has a separate project. Once compiled, +the tests must be run from the base directory of the checkout. diff --git a/vendor/MaxmindDB/projects/VS12/libmaxminddb-release.props b/vendor/MaxmindDB/projects/VS12/libmaxminddb-release.props new file mode 100644 index 00000000..296bdce8 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/libmaxminddb-release.props @@ -0,0 +1,32 @@ + + + + + + + + MaxSpeed + + + + + AnySuitable + + + + + true + + + + + Speed + + + + + true + + + + \ No newline at end of file diff --git a/vendor/MaxmindDB/projects/VS12/libmaxminddb-x64.props b/vendor/MaxmindDB/projects/VS12/libmaxminddb-x64.props new file mode 100644 index 00000000..23c29fa7 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/libmaxminddb-x64.props @@ -0,0 +1,14 @@ + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\obj\ + + + + MachineX64 + + + + \ No newline at end of file diff --git a/vendor/MaxmindDB/projects/VS12/libmaxminddb.props b/vendor/MaxmindDB/projects/VS12/libmaxminddb.props new file mode 100644 index 00000000..033c1a77 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/libmaxminddb.props @@ -0,0 +1,32 @@ + + + + + + $(SolutionDir)$(Configuration)\obj\ + + + + ..\..\include + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ProgramDatabase + Level3 + false + $(OutDir)vc$(PlatformToolsetVersion).pdb + Disabled + Disabled + false + false + + + if NOT EXIST (..\..\include\maxminddb_config.h) ( +copy maxminddb_config.h ..\..\include\maxminddb_config.h +) + + + MachineX86 + false + + + + diff --git a/vendor/MaxmindDB/projects/VS12/libmaxminddb.sln b/vendor/MaxmindDB/projects/VS12/libmaxminddb.sln new file mode 100644 index 00000000..48134014 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/libmaxminddb.sln @@ -0,0 +1,150 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmaxminddb", "libmaxminddb.vcxproj", "{82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_basic_lookup", "..\VS12-tests\basic_lookup.vcxproj", "{8E11C512-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_data_entry_list", "..\VS12-tests\data_entry_list.vcxproj", "{8E11C882-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_data_types", "..\VS12-tests\data_types.vcxproj", "{8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_dump", "..\VS12-tests\dump.vcxproj", "{8E11CEEA-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_get_value", "..\VS12-tests\get_value.vcxproj", "{8E11D5E8-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_get_value_pointer_bug", "..\VS12-tests\get_value_pointer_bug.vcxproj", "{8E11D2AA-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_ipv4_start_cache", "..\VS12-tests\ipv4_start_cache.vcxproj", "{8E11D930-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_ipv6_lookup_in_ipv4", "..\VS12-tests\ipv6_lookup_in_ipv4.vcxproj", "{8E11DC64-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtap", "..\VS12-tests\libtap.vcxproj", "{4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_metadata", "..\VS12-tests\metadata.vcxproj", "{8E11DFC0-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_no_map_get_value", "..\VS12-tests\no_map_get_value.vcxproj", "{8E11E33A-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_read_node", "..\VS12-tests\read_node.vcxproj", "{8E11E68C-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_maxminddb_test_helper", "..\VS12-tests\shared.vcxproj", "{A8F568F6-5507-4EC2-A834-F2C0A3C635A5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_version", "..\VS12-tests\version.vcxproj", "{8E11ED26-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_bad_pointers", "..\VS12-tests\bad_pointers.vcxproj", "{8E11BAF4-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_metadata_pointers", "..\VS12-tests\metadata_pointers.vcxproj", "{8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Debug|Win32.Build.0 = Debug|Win32 + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Debug|x64.ActiveCfg = Debug|x64 + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Debug|x64.Build.0 = Debug|x64 + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Release|Win32.ActiveCfg = Release|Win32 + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Release|Win32.Build.0 = Release|Win32 + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Release|x64.ActiveCfg = Release|x64 + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D}.Release|x64.Build.0 = Release|x64 + {8E11C512-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11C512-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11C512-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11C512-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11C512-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11C512-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11C882-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11C882-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11C882-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11C882-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11C882-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11C882-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11CBD4-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11CEEA-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11CEEA-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11CEEA-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11CEEA-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11CEEA-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11CEEA-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11D5E8-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11D5E8-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11D5E8-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11D5E8-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11D5E8-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11D5E8-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11D2AA-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11D2AA-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11D2AA-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11D2AA-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11D2AA-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11D2AA-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11D930-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11D930-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11D930-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11D930-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11D930-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11D930-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11DC64-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11DC64-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11DC64-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11DC64-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11DC64-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11DC64-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA}.Debug|Win32.ActiveCfg = Debug|Win32 + {4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA}.Debug|Win32.Build.0 = Debug|Win32 + {4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA}.Debug|x64.ActiveCfg = Debug|Win32 + {4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA}.Release|Win32.ActiveCfg = Release|Win32 + {4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA}.Release|Win32.Build.0 = Release|Win32 + {4DFC985A-83D7-4E61-85FE-C6EA6E43E3AA}.Release|x64.ActiveCfg = Release|Win32 + {8E11DFC0-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11DFC0-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11DFC0-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11DFC0-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11DFC0-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11DFC0-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11E33A-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11E33A-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11E33A-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11E33A-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11E33A-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11E33A-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11E68C-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11E68C-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11E68C-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11E68C-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11E68C-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11E68C-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5}.Debug|Win32.ActiveCfg = Debug|Win32 + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5}.Debug|Win32.Build.0 = Debug|Win32 + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5}.Debug|x64.ActiveCfg = Debug|Win32 + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5}.Release|Win32.ActiveCfg = Release|Win32 + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5}.Release|Win32.Build.0 = Release|Win32 + {A8F568F6-5507-4EC2-A834-F2C0A3C635A5}.Release|x64.ActiveCfg = Release|Win32 + {8E11ED26-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11ED26-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11ED26-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11ED26-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11ED26-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11ED26-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + {8E11BAF4-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E11BAF4-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|Win32.Build.0 = Debug|Win32 + {8E11BAF4-7B63-11E4-AE98-6B41E8A9DDB2}.Debug|x64.ActiveCfg = Debug|Win32 + {8E11BAF4-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.ActiveCfg = Release|Win32 + {8E11BAF4-7B63-11E4-AE98-6B41E8A9DDB2}.Release|Win32.Build.0 = Release|Win32 + {8E11BAF4-7B63-11E4-AE98-6B41E8A9DDB2}.Release|x64.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vendor/MaxmindDB/projects/VS12/libmaxminddb.vcxproj b/vendor/MaxmindDB/projects/VS12/libmaxminddb.vcxproj new file mode 100644 index 00000000..8bc04f86 --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/libmaxminddb.vcxproj @@ -0,0 +1,143 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + {82953BDA-2960-4ADA-A6D5-92E65CCB4A3D} + Win32Proj + libmaxminddb + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectName)d + + + $(ProjectName)d + + + + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + + + + + + + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + + + + + + + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12/libmaxminddb.vcxproj.filters b/vendor/MaxmindDB/projects/VS12/libmaxminddb.vcxproj.filters new file mode 100644 index 00000000..cb2f35ec --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/libmaxminddb.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + + + + diff --git a/vendor/MaxmindDB/projects/VS12/maxminddb_config.h b/vendor/MaxmindDB/projects/VS12/maxminddb_config.h new file mode 100644 index 00000000..1a5c62dc --- /dev/null +++ b/vendor/MaxmindDB/projects/VS12/maxminddb_config.h @@ -0,0 +1,14 @@ +#ifndef MAXMINDDB_CONFIG_H +#define MAXMINDDB_CONFIG_H + +#ifndef MMDB_UINT128_USING_MODE +/* Define as 1 if we we use unsigned int __atribute__ ((__mode__(TI))) for uint128 values */ +#define MMDB_UINT128_USING_MODE 0 +#endif + +#ifndef MMDB_UINT128_IS_BYTE_ARRAY +/* Define as 1 if we don't have an unsigned __int128 type */ +#define MMDB_UINT128_IS_BYTE_ARRAY 1 +#endif + +#endif /* MAXMINDDB_CONFIG_H */ diff --git a/vendor/MaxmindDB/projects/test.vcxproj.template b/vendor/MaxmindDB/projects/test.vcxproj.template new file mode 100644 index 00000000..8ffacc36 --- /dev/null +++ b/vendor/MaxmindDB/projects/test.vcxproj.template @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {%UUID%} + Win32Proj + %TESTNAME% + test_%TESTNAME% + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + + + false + $(Configuration)\$(ProjectName)\ + + + + + + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)\..\..\include;$(SolutionDir)\..\..\t\libtap;$(SolutionDir)\..\..\src + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {4dfc985a-83d7-4e61-85fe-c6ea6e43e3aa} + + + {82953bda-2960-4ada-a6d5-92e65ccb4a3d} + + + {a8f568f6-5507-4ec2-a834-f2c0a3c635a5} + true + true + true + true + false + + + + + + diff --git a/vendor/MaxmindDB/src/Makefile.am b/vendor/MaxmindDB/src/Makefile.am new file mode 100644 index 00000000..6d57acaa --- /dev/null +++ b/vendor/MaxmindDB/src/Makefile.am @@ -0,0 +1,25 @@ +include $(top_srcdir)/common.mk + +lib_LTLIBRARIES = libmaxminddb.la + +libmaxminddb_la_SOURCES = maxminddb.c maxminddb-compat-util.h \ + data-pool.c data-pool.h +libmaxminddb_la_LDFLAGS = -version-info 0:7:0 -export-symbols-regex '^MMDB_.*' +include_HEADERS = $(top_srcdir)/include/maxminddb.h + +pkgconfig_DATA = libmaxminddb.pc + +TESTS = test-data-pool + +check_PROGRAMS = test-data-pool + +test_data_pool_SOURCES = data-pool.c data-pool.h +test_data_pool_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/t -DTEST_DATA_POOL +test_data_pool_LDADD = $(top_srcdir)/t/libmmdbtest.la \ + $(top_srcdir)/t/libtap/libtap.a + +$(top_srcdir)/t/libmmdbtest.la: + $(MAKE) -C $(top_srcdir)/t libmmdbtest.la + +$(top_srcdir)/t/libtap/libtap.a: + $(MAKE) -C $(top_srcdir)/t/libtap libtap.a diff --git a/vendor/MaxmindDB/src/data-pool.c b/vendor/MaxmindDB/src/data-pool.c new file mode 100644 index 00000000..48521b64 --- /dev/null +++ b/vendor/MaxmindDB/src/data-pool.c @@ -0,0 +1,180 @@ +#include "data-pool.h" +#include "maxminddb.h" + +#include +#include +#include + +static bool can_multiply(size_t const, size_t const, size_t const); + +// Allocate an MMDB_data_pool_s. It initially has space for size +// MMDB_entry_data_list_s structs. +MMDB_data_pool_s *data_pool_new(size_t const size) +{ + MMDB_data_pool_s *const pool = calloc(1, sizeof(MMDB_data_pool_s)); + if (!pool) { + return NULL; + } + + if (size == 0 || + !can_multiply(SIZE_MAX, size, sizeof(MMDB_entry_data_list_s))) { + data_pool_destroy(pool); + return NULL; + } + pool->size = size; + pool->blocks[0] = calloc(pool->size, sizeof(MMDB_entry_data_list_s)); + if (!pool->blocks[0]) { + data_pool_destroy(pool); + return NULL; + } + pool->blocks[0]->pool = pool; + + pool->sizes[0] = size; + + pool->block = pool->blocks[0]; + + return pool; +} + +// Determine if we can multiply m*n. We can do this if the result will be below +// the given max. max will typically be SIZE_MAX. +// +// We want to know if we'll wrap around. +static bool can_multiply(size_t const max, size_t const m, size_t const n) +{ + if (m == 0) { + return false; + } + + return n <= max / m; +} + +// Clean up the data pool. +void data_pool_destroy(MMDB_data_pool_s *const pool) +{ + if (!pool) { + return; + } + + for (size_t i = 0; i <= pool->index; i++) { + free(pool->blocks[i]); + } + + free(pool); +} + +// Claim a new struct from the pool. Doing this may cause the pool's size to +// grow. +MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const pool) +{ + if (!pool) { + return NULL; + } + + if (pool->used < pool->size) { + MMDB_entry_data_list_s *const element = pool->block + pool->used; + pool->used++; + return element; + } + + // Take it from a new block of memory. + + size_t const new_index = pool->index + 1; + if (new_index == DATA_POOL_NUM_BLOCKS) { + // See the comment about not growing this on DATA_POOL_NUM_BLOCKS. + return NULL; + } + + if (!can_multiply(SIZE_MAX, pool->size, 2)) { + return NULL; + } + size_t const new_size = pool->size * 2; + + if (!can_multiply(SIZE_MAX, new_size, sizeof(MMDB_entry_data_list_s))) { + return NULL; + } + pool->blocks[new_index] = calloc(new_size, sizeof(MMDB_entry_data_list_s)); + if (!pool->blocks[new_index]) { + return NULL; + } + + // We don't need to set this, but it's useful for introspection in tests. + pool->blocks[new_index]->pool = pool; + + pool->index = new_index; + pool->block = pool->blocks[pool->index]; + + pool->size = new_size; + pool->sizes[pool->index] = pool->size; + + MMDB_entry_data_list_s *const element = pool->block; + pool->used = 1; + return element; +} + +// Turn the structs in the array-like pool into a linked list. +// +// Before calling this function, the list isn't linked up. +MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const pool) +{ + if (!pool) { + return NULL; + } + + if (pool->index == 0 && pool->used == 0) { + return NULL; + } + + for (size_t i = 0; i <= pool->index; i++) { + MMDB_entry_data_list_s *const block = pool->blocks[i]; + + size_t size = pool->sizes[i]; + if (i == pool->index) { + size = pool->used; + } + + for (size_t j = 0; j < size - 1; j++) { + MMDB_entry_data_list_s *const cur = block + j; + cur->next = block + j + 1; + } + + if (i < pool->index) { + MMDB_entry_data_list_s *const last = block + size - 1; + last->next = pool->blocks[i + 1]; + } + } + + return pool->blocks[0]; +} + +#ifdef TEST_DATA_POOL + +#include +#include + +static void test_can_multiply(void); + +int main(void) +{ + plan(NO_PLAN); + test_can_multiply(); + done_testing(); +} + +static void test_can_multiply(void) +{ + { + ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok"); + } + + { + ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok"); + } + + { + ok(can_multiply(SIZE_MAX, 10240, sizeof(MMDB_entry_data_list_s)), + "1024 entry_data_list_s's are okay"); + } +} + +#endif diff --git a/vendor/MaxmindDB/src/data-pool.h b/vendor/MaxmindDB/src/data-pool.h new file mode 100644 index 00000000..25d09923 --- /dev/null +++ b/vendor/MaxmindDB/src/data-pool.h @@ -0,0 +1,52 @@ +#ifndef DATA_POOL_H +#define DATA_POOL_H + +#include "maxminddb.h" + +#include +#include + +// This should be large enough that we never need to grow the array of pointers +// to blocks. 32 is enough. Even starting out of with size 1 (1 struct), the +// 32nd element alone will provide 2**32 structs as we exponentially increase +// the number in each block. Being confident that we do not have to grow the +// array lets us avoid writing code to do that. That code would be risky as it +// would rarely be hit and likely not be well tested. +#define DATA_POOL_NUM_BLOCKS 32 + +// A pool of memory for MMDB_entry_data_list_s structs. This is so we can +// allocate multiple up front rather than one at a time for performance +// reasons. +// +// The order you add elements to it (by calling data_pool_alloc()) ends up as +// the order of the list. +// +// The memory only grows. There is no support for releasing an element you take +// back to the pool. +typedef struct MMDB_data_pool_s { + // Index of the current block we're allocating out of. + size_t index; + + // The size of the current block, counting by structs. + size_t size; + + // How many used in the current block, counting by structs. + size_t used; + + // The current block we're allocating out of. + MMDB_entry_data_list_s *block; + + // The size of each block. + size_t sizes[DATA_POOL_NUM_BLOCKS]; + + // An array of pointers to blocks of memory holding space for list + // elements. + MMDB_entry_data_list_s *blocks[DATA_POOL_NUM_BLOCKS]; +} MMDB_data_pool_s; + +MMDB_data_pool_s *data_pool_new(size_t const); +void data_pool_destroy(MMDB_data_pool_s *const); +MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const); +MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const); + +#endif diff --git a/vendor/MaxmindDB/src/libmaxminddb.pc.in b/vendor/MaxmindDB/src/libmaxminddb.pc.in new file mode 100644 index 00000000..00ced3ba --- /dev/null +++ b/vendor/MaxmindDB/src/libmaxminddb.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libmaxminddb +Description: C library for the MaxMind DB file format +URL: http://maxmind.github.io/libmaxminddb/ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lmaxminddb +Cflags: -I${includedir} diff --git a/vendor/MaxmindDB/src/maxminddb-compat-util.h b/vendor/MaxmindDB/src/maxminddb-compat-util.h new file mode 100644 index 00000000..e3f0320f --- /dev/null +++ b/vendor/MaxmindDB/src/maxminddb-compat-util.h @@ -0,0 +1,167 @@ +#include +#include + +/* *INDENT-OFF* */ + +/* The memmem, strdup, and strndup functions were all copied from the + * FreeBSD source, along with the relevant copyright notice. + * + * It'd be nicer to simply use the functions available on the system if they + * exist, but there doesn't seem to be a good way to detect them without also + * defining things like _GNU_SOURCE, which we want to avoid, because then we + * end up _accidentally_ using GNU features without noticing, which then + * breaks on systems like OSX. + * + * C is fun! */ + +/* Applies to memmem implementation */ +/*- + * Copyright (c) 2005 Pascal Gloor + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +static void * +mmdb_memmem(const void *l, size_t l_len, const void *s, size_t s_len) +{ + register char *cur, *last; + const char *cl = (const char *)l; + const char *cs = (const char *)s; + + /* we need something to compare */ + if (l_len == 0 || s_len == 0) + return NULL; + + /* "s" must be smaller or equal to "l" */ + if (l_len < s_len) + return NULL; + + /* special case where s_len == 1 */ + if (s_len == 1) + return memchr(l, (int)*cs, l_len); + + /* the last position where its possible to find "s" in "l" */ + last = (char *)cl + l_len - s_len; + + for (cur = (char *)cl; cur <= last; cur++) + if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) + return cur; + + return NULL; +} + +/* Applies to strnlen implementation */ +/*- + * Copyright (c) 2009 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +static size_t +mmdb_strnlen(const char *s, size_t maxlen) +{ + size_t len; + + for (len = 0; len < maxlen; len++, s++) { + if (!*s) + break; + } + return (len); +} + +/* Applies to strdup and strndup implementation */ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +static char * +mmdb_strdup(const char *str) +{ + size_t len; + char *copy; + + len = strlen(str) + 1; + if ((copy = malloc(len)) == NULL) + return (NULL); + memcpy(copy, str, len); + return (copy); +} + +static char * +mmdb_strndup(const char *str, size_t n) +{ + size_t len; + char *copy; + + len = mmdb_strnlen(str, n); + if ((copy = malloc(len + 1)) == NULL) + return (NULL); + memcpy(copy, str, len); + copy[len] = '\0'; + return (copy); +} +/* *INDENT-ON* */ diff --git a/vendor/MaxmindDB/src/maxminddb.c b/vendor/MaxmindDB/src/maxminddb.c new file mode 100644 index 00000000..427f48af --- /dev/null +++ b/vendor/MaxmindDB/src/maxminddb.c @@ -0,0 +1,2157 @@ +#if HAVE_CONFIG_H +#include +#endif +#include "data-pool.h" +#include "maxminddb.h" +#include "maxminddb-compat-util.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#ifndef UNICODE +#define UNICODE +#endif +#include +#include +#else +#include +#include +#include +#endif + +#define MMDB_DATA_SECTION_SEPARATOR (16) +#define MAXIMUM_DATA_STRUCTURE_DEPTH (512) + +#ifdef MMDB_DEBUG +#define LOCAL +#define DEBUG_MSG(msg) fprintf(stderr, msg "\n") +#define DEBUG_MSGF(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__) +#define DEBUG_BINARY(fmt, byte) \ + do { \ + char *binary = byte_to_binary(byte); \ + if (NULL == binary) { \ + fprintf(stderr, "Calloc failed in DEBUG_BINARY\n"); \ + abort(); \ + } \ + fprintf(stderr, fmt "\n", binary); \ + free(binary); \ + } while (0) +#define DEBUG_NL fprintf(stderr, "\n") +#else +#define LOCAL static +#define DEBUG_MSG(...) +#define DEBUG_MSGF(...) +#define DEBUG_BINARY(...) +#define DEBUG_NL +#endif + +#ifdef MMDB_DEBUG +char *byte_to_binary(uint8_t byte) +{ + char *bits = calloc(9, sizeof(char)); + if (NULL == bits) { + return bits; + } + + for (uint8_t i = 0; i < 8; i++) { + bits[i] = byte & (128 >> i) ? '1' : '0'; + } + bits[8] = '\0'; + + return bits; +} + +char *type_num_to_name(uint8_t num) +{ + switch (num) { + case 0: + return "extended"; + case 1: + return "pointer"; + case 2: + return "utf8_string"; + case 3: + return "double"; + case 4: + return "bytes"; + case 5: + return "uint16"; + case 6: + return "uint32"; + case 7: + return "map"; + case 8: + return "int32"; + case 9: + return "uint64"; + case 10: + return "uint128"; + case 11: + return "array"; + case 12: + return "container"; + case 13: + return "end_marker"; + case 14: + return "boolean"; + case 15: + return "float"; + default: + return "unknown type"; + } +} +#endif + +/* None of the values we check on the lhs are bigger than uint32_t, so on + * platforms where SIZE_MAX is a 64-bit integer, this would be a no-op, and it + * makes the compiler complain if we do the check anyway. */ +#if SIZE_MAX == UINT32_MAX +#define MAYBE_CHECK_SIZE_OVERFLOW(lhs, rhs, error) \ + if ((lhs) > (rhs)) { \ + return error; \ + } +#else +#define MAYBE_CHECK_SIZE_OVERFLOW(...) +#endif + +typedef struct record_info_s { + uint16_t record_length; + uint32_t (*left_record_getter)(const uint8_t *); + uint32_t (*right_record_getter)(const uint8_t *); + uint8_t right_record_offset; +} record_info_s; + +#define METADATA_MARKER "\xab\xcd\xefMaxMind.com" +/* This is 128kb */ +#define METADATA_BLOCK_MAX_SIZE 131072 + +// 64 leads us to allocating 4 KiB on a 64bit system. +#define MMDB_POOL_INIT_SIZE 64 + +LOCAL int map_file(MMDB_s *const mmdb); +LOCAL const uint8_t *find_metadata(const uint8_t *file_content, + ssize_t file_size, uint32_t *metadata_size); +LOCAL int read_metadata(MMDB_s *mmdb); +LOCAL MMDB_s make_fake_metadata_db(const MMDB_s *const mmdb); +LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, + uint16_t *value); +LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, + uint32_t *value); +LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, + uint64_t *value); +LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, + char const **value); +LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start); +LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start); +LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses); +LOCAL int find_address_in_search_tree(const MMDB_s *const mmdb, + uint8_t *address, + sa_family_t address_family, + MMDB_lookup_result_s *result); +LOCAL record_info_s record_info_for_database(const MMDB_s *const mmdb); +LOCAL int find_ipv4_start_node(MMDB_s *const mmdb); +LOCAL uint8_t record_type(const MMDB_s *const mmdb, uint64_t record); +LOCAL uint32_t get_left_28_bit_record(const uint8_t *record); +LOCAL uint32_t get_right_28_bit_record(const uint8_t *record); +LOCAL uint32_t data_section_offset_for_record(const MMDB_s *const mmdb, + uint64_t record); +LOCAL int path_length(va_list va_path); +LOCAL int lookup_path_in_array(const char *path_elem, const MMDB_s *const mmdb, + MMDB_entry_data_s *entry_data); +LOCAL int lookup_path_in_map(const char *path_elem, const MMDB_s *const mmdb, + MMDB_entry_data_s *entry_data); +LOCAL int skip_map_or_array(const MMDB_s *const mmdb, + MMDB_entry_data_s *entry_data); +LOCAL int decode_one_follow(const MMDB_s *const mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data); +LOCAL int decode_one(const MMDB_s *const mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data); +LOCAL int get_ext_type(int raw_ext_type); +LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, + int ptr_size); +LOCAL int get_entry_data_list(const MMDB_s *const mmdb, + uint32_t offset, + MMDB_entry_data_list_s *const entry_data_list, + MMDB_data_pool_s *const pool, + int depth); +LOCAL float get_ieee754_float(const uint8_t *restrict p); +LOCAL double get_ieee754_double(const uint8_t *restrict p); +LOCAL uint32_t get_uint32(const uint8_t *p); +LOCAL uint32_t get_uint24(const uint8_t *p); +LOCAL uint32_t get_uint16(const uint8_t *p); +LOCAL uint64_t get_uintX(const uint8_t *p, int length); +LOCAL int32_t get_sintX(const uint8_t *p, int length); +LOCAL void free_mmdb_struct(MMDB_s *const mmdb); +LOCAL void free_languages_metadata(MMDB_s *mmdb); +LOCAL void free_descriptions_metadata(MMDB_s *mmdb); +LOCAL MMDB_entry_data_list_s *dump_entry_data_list( + FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent, + int *status); +LOCAL void print_indentation(FILE *stream, int i); +LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size); + +#define CHECKED_DECODE_ONE(mmdb, offset, entry_data) \ + do { \ + int status = decode_one(mmdb, offset, entry_data); \ + if (MMDB_SUCCESS != status) { \ + DEBUG_MSGF("CHECKED_DECODE_ONE failed." \ + " status = %d (%s)", status, MMDB_strerror(status)); \ + return status; \ + } \ + } while (0) + +#define CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data) \ + do { \ + int status = decode_one_follow(mmdb, offset, entry_data); \ + if (MMDB_SUCCESS != status) { \ + DEBUG_MSGF("CHECKED_DECODE_ONE_FOLLOW failed." \ + " status = %d (%s)", status, MMDB_strerror(status)); \ + return status; \ + } \ + } while (0) + +#define FREE_AND_SET_NULL(p) { free((void *)(p)); (p) = NULL; } + +int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb) +{ + int status = MMDB_SUCCESS; + + mmdb->file_content = NULL; + mmdb->data_section = NULL; + mmdb->metadata.database_type = NULL; + mmdb->metadata.languages.count = 0; + mmdb->metadata.languages.names = NULL; + mmdb->metadata.description.count = 0; + + mmdb->filename = mmdb_strdup(filename); + if (NULL == mmdb->filename) { + status = MMDB_OUT_OF_MEMORY_ERROR; + goto cleanup; + } + + if ((flags & MMDB_MODE_MASK) == 0) { + flags |= MMDB_MODE_MMAP; + } + mmdb->flags = flags; + + if (MMDB_SUCCESS != (status = map_file(mmdb))) { + goto cleanup; + } + +#ifdef _WIN32 + WSADATA wsa; + WSAStartup(MAKEWORD(2, 2), &wsa); +#endif + + uint32_t metadata_size = 0; + const uint8_t *metadata = find_metadata(mmdb->file_content, mmdb->file_size, + &metadata_size); + if (NULL == metadata) { + status = MMDB_INVALID_METADATA_ERROR; + goto cleanup; + } + + mmdb->metadata_section = metadata; + mmdb->metadata_section_size = metadata_size; + + status = read_metadata(mmdb); + if (MMDB_SUCCESS != status) { + goto cleanup; + } + + if (mmdb->metadata.binary_format_major_version != 2) { + status = MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + goto cleanup; + } + + uint32_t search_tree_size = mmdb->metadata.node_count * + mmdb->full_record_byte_size; + + mmdb->data_section = mmdb->file_content + search_tree_size + + MMDB_DATA_SECTION_SEPARATOR; + if (search_tree_size + MMDB_DATA_SECTION_SEPARATOR > + (uint32_t)mmdb->file_size) { + status = MMDB_INVALID_METADATA_ERROR; + goto cleanup; + } + mmdb->data_section_size = (uint32_t)mmdb->file_size - search_tree_size - + MMDB_DATA_SECTION_SEPARATOR; + + // Although it is likely not possible to construct a database with valid + // valid metadata, as parsed above, and a data_section_size less than 3, + // we do this check as later we assume it is at least three when doing + // bound checks. + if (mmdb->data_section_size < 3) { + status = MMDB_INVALID_DATA_ERROR; + goto cleanup; + } + + mmdb->metadata_section = metadata; + mmdb->ipv4_start_node.node_value = 0; + mmdb->ipv4_start_node.netmask = 0; + + // We do this immediately as otherwise there is a race to set + // ipv4_start_node.node_value and ipv4_start_node.netmask. + if (mmdb->metadata.ip_version == 6) { + status = find_ipv4_start_node(mmdb); + if (status != MMDB_SUCCESS) { + goto cleanup; + } + } + + cleanup: + if (MMDB_SUCCESS != status) { + int saved_errno = errno; + free_mmdb_struct(mmdb); + errno = saved_errno; + } + return status; +} + +#ifdef _WIN32 + +LOCAL LPWSTR utf8_to_utf16(const char *utf8_str) +{ + int wide_chars = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0); + wchar_t *utf16_str = (wchar_t *)calloc(wide_chars, sizeof(wchar_t)); + + if (MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, utf16_str, + wide_chars) < 1) { + free(utf16_str); + return NULL; + } + + return utf16_str; +} + +LOCAL int map_file(MMDB_s *const mmdb) +{ + DWORD size; + int status = MMDB_SUCCESS; + HANDLE mmh = NULL; + HANDLE fd = INVALID_HANDLE_VALUE; + LPWSTR utf16_filename = utf8_to_utf16(mmdb->filename); + if (!utf16_filename) { + status = MMDB_FILE_OPEN_ERROR; + goto cleanup; + } + fd = CreateFileW(utf16_filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd == INVALID_HANDLE_VALUE) { + status = MMDB_FILE_OPEN_ERROR; + goto cleanup; + } + size = GetFileSize(fd, NULL); + if (size == INVALID_FILE_SIZE) { + status = MMDB_FILE_OPEN_ERROR; + goto cleanup; + } + mmh = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, size, NULL); + /* Microsoft documentation for CreateFileMapping indicates this returns + NULL not INVALID_HANDLE_VALUE on error */ + if (NULL == mmh) { + status = MMDB_IO_ERROR; + goto cleanup; + } + uint8_t *file_content = + (uint8_t *)MapViewOfFile(mmh, FILE_MAP_READ, 0, 0, 0); + if (file_content == NULL) { + status = MMDB_IO_ERROR; + goto cleanup; + } + + mmdb->file_size = size; + mmdb->file_content = file_content; + + cleanup:; + int saved_errno = errno; + if (INVALID_HANDLE_VALUE != fd) { + CloseHandle(fd); + } + if (NULL != mmh) { + CloseHandle(mmh); + } + errno = saved_errno; + free(utf16_filename); + + return status; +} + +#else // _WIN32 + +LOCAL int map_file(MMDB_s *const mmdb) +{ + ssize_t size; + int status = MMDB_SUCCESS; + + int flags = O_RDONLY; +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + int fd = open(mmdb->filename, flags); + struct stat s; + if (fd < 0 || fstat(fd, &s)) { + status = MMDB_FILE_OPEN_ERROR; + goto cleanup; + } + + size = s.st_size; + if (size < 0 || size != s.st_size) { + status = MMDB_OUT_OF_MEMORY_ERROR; + goto cleanup; + } + + uint8_t *file_content = + (uint8_t *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (MAP_FAILED == file_content) { + if (ENOMEM == errno) { + status = MMDB_OUT_OF_MEMORY_ERROR; + } else { + status = MMDB_IO_ERROR; + } + goto cleanup; + } + + mmdb->file_size = size; + mmdb->file_content = file_content; + + cleanup:; + int saved_errno = errno; + if (fd >= 0) { + close(fd); + } + errno = saved_errno; + + return status; +} + +#endif // _WIN32 + +LOCAL const uint8_t *find_metadata(const uint8_t *file_content, + ssize_t file_size, uint32_t *metadata_size) +{ + const ssize_t marker_len = sizeof(METADATA_MARKER) - 1; + ssize_t max_size = file_size > + METADATA_BLOCK_MAX_SIZE ? METADATA_BLOCK_MAX_SIZE : + file_size; + + uint8_t *search_area = (uint8_t *)(file_content + (file_size - max_size)); + uint8_t *start = search_area; + uint8_t *tmp; + do { + tmp = mmdb_memmem(search_area, max_size, + METADATA_MARKER, marker_len); + + if (NULL != tmp) { + max_size -= tmp - search_area; + search_area = tmp; + + /* Continue searching just after the marker we just read, in case + * there are multiple markers in the same file. This would be odd + * but is certainly not impossible. */ + max_size -= marker_len; + search_area += marker_len; + } + } while (NULL != tmp); + + if (search_area == start) { + return NULL; + } + + *metadata_size = (uint32_t)max_size; + + return search_area; +} + +LOCAL int read_metadata(MMDB_s *mmdb) +{ + /* We need to create a fake MMDB_s struct in order to decode values from + the metadata. The metadata is basically just like the data section, so we + want to use the same functions we use for the data section to get metadata + values. */ + MMDB_s metadata_db = make_fake_metadata_db(mmdb); + + MMDB_entry_s metadata_start = { + .mmdb = &metadata_db, + .offset = 0 + }; + + int status = + value_for_key_as_uint32(&metadata_start, "node_count", + &mmdb->metadata.node_count); + if (MMDB_SUCCESS != status) { + return status; + } + if (!mmdb->metadata.node_count) { + DEBUG_MSG("could not find node_count value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + status = value_for_key_as_uint16(&metadata_start, "record_size", + &mmdb->metadata.record_size); + if (MMDB_SUCCESS != status) { + return status; + } + if (!mmdb->metadata.record_size) { + DEBUG_MSG("could not find record_size value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + if (mmdb->metadata.record_size != 24 && mmdb->metadata.record_size != 28 + && mmdb->metadata.record_size != 32) { + DEBUG_MSGF("bad record size in metadata: %i", + mmdb->metadata.record_size); + return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + } + + status = value_for_key_as_uint16(&metadata_start, "ip_version", + &mmdb->metadata.ip_version); + if (MMDB_SUCCESS != status) { + return status; + } + if (!mmdb->metadata.ip_version) { + DEBUG_MSG("could not find ip_version value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + if (!(mmdb->metadata.ip_version == 4 || mmdb->metadata.ip_version == 6)) { + DEBUG_MSGF("ip_version value in metadata is not 4 or 6 - it was %i", + mmdb->metadata.ip_version); + return MMDB_INVALID_METADATA_ERROR; + } + + status = value_for_key_as_string(&metadata_start, "database_type", + &mmdb->metadata.database_type); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("error finding database_type value in metadata"); + return status; + } + + status = + populate_languages_metadata(mmdb, &metadata_db, &metadata_start); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("could not populate languages from metadata"); + return status; + } + + status = value_for_key_as_uint16( + &metadata_start, "binary_format_major_version", + &mmdb->metadata.binary_format_major_version); + if (MMDB_SUCCESS != status) { + return status; + } + if (!mmdb->metadata.binary_format_major_version) { + DEBUG_MSG( + "could not find binary_format_major_version value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + status = value_for_key_as_uint16( + &metadata_start, "binary_format_minor_version", + &mmdb->metadata.binary_format_minor_version); + if (MMDB_SUCCESS != status) { + return status; + } + + status = value_for_key_as_uint64(&metadata_start, "build_epoch", + &mmdb->metadata.build_epoch); + if (MMDB_SUCCESS != status) { + return status; + } + if (!mmdb->metadata.build_epoch) { + DEBUG_MSG("could not find build_epoch value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + status = populate_description_metadata(mmdb, &metadata_db, &metadata_start); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("could not populate description from metadata"); + return status; + } + + mmdb->full_record_byte_size = mmdb->metadata.record_size * 2 / 8U; + + mmdb->depth = mmdb->metadata.ip_version == 4 ? 32 : 128; + + return MMDB_SUCCESS; +} + +LOCAL MMDB_s make_fake_metadata_db(const MMDB_s *const mmdb) +{ + MMDB_s fake_metadata_db = { + .data_section = mmdb->metadata_section, + .data_section_size = mmdb->metadata_section_size + }; + + return fake_metadata_db; +} + +LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key, + uint16_t *value) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + int status = MMDB_aget_value(start, &entry_data, path); + if (MMDB_SUCCESS != status) { + return status; + } + if (MMDB_DATA_TYPE_UINT16 != entry_data.type) { + DEBUG_MSGF("expect uint16 for %s but received %s", key, + type_num_to_name( + entry_data.type)); + return MMDB_INVALID_METADATA_ERROR; + } + *value = entry_data.uint16; + return MMDB_SUCCESS; +} + +LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, + uint32_t *value) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + int status = MMDB_aget_value(start, &entry_data, path); + if (MMDB_SUCCESS != status) { + return status; + } + if (MMDB_DATA_TYPE_UINT32 != entry_data.type) { + DEBUG_MSGF("expect uint32 for %s but received %s", key, + type_num_to_name( + entry_data.type)); + return MMDB_INVALID_METADATA_ERROR; + } + *value = entry_data.uint32; + return MMDB_SUCCESS; +} + +LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, + uint64_t *value) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + int status = MMDB_aget_value(start, &entry_data, path); + if (MMDB_SUCCESS != status) { + return status; + } + if (MMDB_DATA_TYPE_UINT64 != entry_data.type) { + DEBUG_MSGF("expect uint64 for %s but received %s", key, + type_num_to_name( + entry_data.type)); + return MMDB_INVALID_METADATA_ERROR; + } + *value = entry_data.uint64; + return MMDB_SUCCESS; +} + +LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, + char const **value) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + int status = MMDB_aget_value(start, &entry_data, path); + if (MMDB_SUCCESS != status) { + return status; + } + if (MMDB_DATA_TYPE_UTF8_STRING != entry_data.type) { + DEBUG_MSGF("expect string for %s but received %s", key, + type_num_to_name( + entry_data.type)); + return MMDB_INVALID_METADATA_ERROR; + } + *value = mmdb_strndup((char *)entry_data.utf8_string, entry_data.data_size); + if (NULL == *value) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + return MMDB_SUCCESS; +} + +LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start) +{ + MMDB_entry_data_s entry_data; + + const char *path[] = { "languages", NULL }; + int status = MMDB_aget_value(metadata_start, &entry_data, path); + if (MMDB_SUCCESS != status) { + return status; + } + if (MMDB_DATA_TYPE_ARRAY != entry_data.type) { + return MMDB_INVALID_METADATA_ERROR; + } + + MMDB_entry_s array_start = { + .mmdb = metadata_db, + .offset = entry_data.offset + }; + + MMDB_entry_data_list_s *member; + status = MMDB_get_entry_data_list(&array_start, &member); + if (MMDB_SUCCESS != status) { + return status; + } + + MMDB_entry_data_list_s *first_member = member; + + uint32_t array_size = member->entry_data.data_size; + MAYBE_CHECK_SIZE_OVERFLOW(array_size, SIZE_MAX / sizeof(char *), + MMDB_INVALID_METADATA_ERROR); + + mmdb->metadata.languages.count = 0; + mmdb->metadata.languages.names = calloc(array_size, sizeof(char *)); + if (NULL == mmdb->metadata.languages.names) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + for (uint32_t i = 0; i < array_size; i++) { + member = member->next; + if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata.languages.names[i] = + mmdb_strndup((char *)member->entry_data.utf8_string, + member->entry_data.data_size); + + if (NULL == mmdb->metadata.languages.names[i]) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + // We assign this as we go so that if we fail a calloc and need to + // free it, the count is right. + mmdb->metadata.languages.count = i + 1; + } + + MMDB_free_entry_data_list(first_member); + + return MMDB_SUCCESS; +} + +LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start) +{ + MMDB_entry_data_s entry_data; + + const char *path[] = { "description", NULL }; + int status = MMDB_aget_value(metadata_start, &entry_data, path); + if (MMDB_SUCCESS != status) { + return status; + } + + if (MMDB_DATA_TYPE_MAP != entry_data.type) { + DEBUG_MSGF("Unexpected entry_data type: %d", entry_data.type); + return MMDB_INVALID_METADATA_ERROR; + } + + MMDB_entry_s map_start = { + .mmdb = metadata_db, + .offset = entry_data.offset + }; + + MMDB_entry_data_list_s *member; + status = MMDB_get_entry_data_list(&map_start, &member); + if (MMDB_SUCCESS != status) { + DEBUG_MSGF( + "MMDB_get_entry_data_list failed while populating description." + " status = %d (%s)", status, MMDB_strerror(status)); + return status; + } + + MMDB_entry_data_list_s *first_member = member; + + uint32_t map_size = member->entry_data.data_size; + mmdb->metadata.description.count = 0; + if (0 == map_size) { + mmdb->metadata.description.descriptions = NULL; + goto cleanup; + } + MAYBE_CHECK_SIZE_OVERFLOW(map_size, SIZE_MAX / sizeof(MMDB_description_s *), + MMDB_INVALID_METADATA_ERROR); + + mmdb->metadata.description.descriptions = + calloc(map_size, sizeof(MMDB_description_s *)); + if (NULL == mmdb->metadata.description.descriptions) { + status = MMDB_OUT_OF_MEMORY_ERROR; + goto cleanup; + } + + for (uint32_t i = 0; i < map_size; i++) { + mmdb->metadata.description.descriptions[i] = + calloc(1, sizeof(MMDB_description_s)); + if (NULL == mmdb->metadata.description.descriptions[i]) { + status = MMDB_OUT_OF_MEMORY_ERROR; + goto cleanup; + } + + mmdb->metadata.description.count = i + 1; + mmdb->metadata.description.descriptions[i]->language = NULL; + mmdb->metadata.description.descriptions[i]->description = NULL; + + member = member->next; + + if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { + status = MMDB_INVALID_METADATA_ERROR; + goto cleanup; + } + + mmdb->metadata.description.descriptions[i]->language = + mmdb_strndup((char *)member->entry_data.utf8_string, + member->entry_data.data_size); + + if (NULL == mmdb->metadata.description.descriptions[i]->language) { + status = MMDB_OUT_OF_MEMORY_ERROR; + goto cleanup; + } + + member = member->next; + + if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { + status = MMDB_INVALID_METADATA_ERROR; + goto cleanup; + } + + mmdb->metadata.description.descriptions[i]->description = + mmdb_strndup((char *)member->entry_data.utf8_string, + member->entry_data.data_size); + + if (NULL == mmdb->metadata.description.descriptions[i]->description) { + status = MMDB_OUT_OF_MEMORY_ERROR; + goto cleanup; + } + } + + cleanup: + MMDB_free_entry_data_list(first_member); + + return status; +} + +MMDB_lookup_result_s MMDB_lookup_string(const MMDB_s *const mmdb, + const char *const ipstr, + int *const gai_error, + int *const mmdb_error) +{ + MMDB_lookup_result_s result = { + .found_entry = false, + .netmask = 0, + .entry = { + .mmdb = mmdb, + .offset = 0 + } + }; + + struct addrinfo *addresses = NULL; + *gai_error = resolve_any_address(ipstr, &addresses); + + if (!*gai_error) { + result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, mmdb_error); + } + + if (NULL != addresses) { + freeaddrinfo(addresses); + } + + return result; +} + +LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses) +{ + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_NUMERICHOST, + // We set ai_socktype so that we only get one result back + .ai_socktype = SOCK_STREAM + }; + + int gai_status = getaddrinfo(ipstr, NULL, &hints, addresses); + if (gai_status) { + return gai_status; + } + + return 0; +} + +MMDB_lookup_result_s MMDB_lookup_sockaddr( + const MMDB_s *const mmdb, + const struct sockaddr *const sockaddr, + int *const mmdb_error) +{ + MMDB_lookup_result_s result = { + .found_entry = false, + .netmask = 0, + .entry = { + .mmdb = mmdb, + .offset = 0 + } + }; + + uint8_t mapped_address[16], *address; + if (mmdb->metadata.ip_version == 4) { + if (sockaddr->sa_family == AF_INET6) { + *mmdb_error = MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR; + return result; + } + address = (uint8_t *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr; + } else { + if (sockaddr->sa_family == AF_INET6) { + address = + (uint8_t *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr. + s6_addr; + } else { + address = mapped_address; + memset(address, 0, 12); + memcpy(address + 12, + &((struct sockaddr_in *)sockaddr)->sin_addr.s_addr, 4); + } + } + + *mmdb_error = + find_address_in_search_tree(mmdb, address, sockaddr->sa_family, + &result); + + return result; +} + +LOCAL int find_address_in_search_tree(const MMDB_s *const mmdb, + uint8_t *address, + sa_family_t address_family, + MMDB_lookup_result_s *result) +{ + record_info_s record_info = record_info_for_database(mmdb); + if (0 == record_info.right_record_offset) { + return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + } + + uint32_t value = 0; + uint16_t current_bit = 0; + if (mmdb->metadata.ip_version == 6 && address_family == AF_INET) { + value = mmdb->ipv4_start_node.node_value; + current_bit = mmdb->ipv4_start_node.netmask; + } + + uint32_t node_count = mmdb->metadata.node_count; + const uint8_t *search_tree = mmdb->file_content; + const uint8_t *record_pointer; + for (; current_bit < mmdb->depth && value < node_count; current_bit++) { + uint8_t bit = 1U & + (address[current_bit >> 3] >> (7 - (current_bit % 8))); + + record_pointer = &search_tree[value * record_info.record_length]; + if (record_pointer + record_info.record_length > mmdb->data_section) { + return MMDB_CORRUPT_SEARCH_TREE_ERROR; + } + if (bit) { + record_pointer += record_info.right_record_offset; + value = record_info.right_record_getter(record_pointer); + } else { + value = record_info.left_record_getter(record_pointer); + } + } + + result->netmask = current_bit; + + if (value >= node_count + mmdb->data_section_size) { + // The pointer points off the end of the database. + return MMDB_CORRUPT_SEARCH_TREE_ERROR; + } + + if (value == node_count) { + // record is empty + result->found_entry = false; + return MMDB_SUCCESS; + } + result->found_entry = true; + result->entry.offset = data_section_offset_for_record(mmdb, value); + + return MMDB_SUCCESS; +} + +LOCAL record_info_s record_info_for_database(const MMDB_s *const mmdb) +{ + record_info_s record_info = { + .record_length = mmdb->full_record_byte_size, + .right_record_offset = 0 + }; + + if (record_info.record_length == 6) { + record_info.left_record_getter = &get_uint24; + record_info.right_record_getter = &get_uint24; + record_info.right_record_offset = 3; + } else if (record_info.record_length == 7) { + record_info.left_record_getter = &get_left_28_bit_record; + record_info.right_record_getter = &get_right_28_bit_record; + record_info.right_record_offset = 3; + } else if (record_info.record_length == 8) { + record_info.left_record_getter = &get_uint32; + record_info.right_record_getter = &get_uint32; + record_info.right_record_offset = 4; + } else { + assert(false); + } + + return record_info; +} + +LOCAL int find_ipv4_start_node(MMDB_s *const mmdb) +{ + /* In a pathological case of a database with a single node search tree, + * this check will be true even after we've found the IPv4 start node, but + * that doesn't seem worth trying to fix. */ + if (mmdb->ipv4_start_node.node_value != 0) { + return MMDB_SUCCESS; + } + + record_info_s record_info = record_info_for_database(mmdb); + + const uint8_t *search_tree = mmdb->file_content; + uint32_t node_value = 0; + const uint8_t *record_pointer; + uint16_t netmask; + uint32_t node_count = mmdb->metadata.node_count; + + for (netmask = 0; netmask < 96 && node_value < node_count; netmask++) { + record_pointer = &search_tree[node_value * record_info.record_length]; + if (record_pointer + record_info.record_length > mmdb->data_section) { + return MMDB_CORRUPT_SEARCH_TREE_ERROR; + } + node_value = record_info.left_record_getter(record_pointer); + } + + mmdb->ipv4_start_node.node_value = node_value; + mmdb->ipv4_start_node.netmask = netmask; + + return MMDB_SUCCESS; +} + +LOCAL uint8_t record_type(const MMDB_s *const mmdb, uint64_t record) +{ + uint32_t node_count = mmdb->metadata.node_count; + + /* Ideally we'd check to make sure that a record never points to a + * previously seen value, but that's more complicated. For now, we can + * at least check that we don't end up at the top of the tree again. */ + if (record == 0) { + DEBUG_MSG("record has a value of 0"); + return MMDB_RECORD_TYPE_INVALID; + } + + if (record < node_count) { + return MMDB_RECORD_TYPE_SEARCH_NODE; + } + + if (record == node_count) { + return MMDB_RECORD_TYPE_EMPTY; + } + + if (record - node_count < mmdb->data_section_size) { + return MMDB_RECORD_TYPE_DATA; + } + + DEBUG_MSG("record has a value that points outside of the database"); + return MMDB_RECORD_TYPE_INVALID; +} + +LOCAL uint32_t get_left_28_bit_record(const uint8_t *record) +{ + return record[0] * 65536 + record[1] * 256 + record[2] + + ((record[3] & 0xf0) << 20); +} + +LOCAL uint32_t get_right_28_bit_record(const uint8_t *record) +{ + uint32_t value = get_uint32(record); + return value & 0xfffffff; +} + +int MMDB_read_node(const MMDB_s *const mmdb, uint32_t node_number, + MMDB_search_node_s *const node) +{ + record_info_s record_info = record_info_for_database(mmdb); + if (0 == record_info.right_record_offset) { + return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + } + + if (node_number > mmdb->metadata.node_count) { + return MMDB_INVALID_NODE_NUMBER_ERROR; + } + + const uint8_t *search_tree = mmdb->file_content; + const uint8_t *record_pointer = + &search_tree[node_number * record_info.record_length]; + node->left_record = record_info.left_record_getter(record_pointer); + record_pointer += record_info.right_record_offset; + node->right_record = record_info.right_record_getter(record_pointer); + + node->left_record_type = record_type(mmdb, node->left_record); + node->right_record_type = record_type(mmdb, node->right_record); + + // Note that offset will be invalid if the record type is not + // MMDB_RECORD_TYPE_DATA, but that's ok. Any use of the record entry + // for other data types is a programming error. + node->left_record_entry = (struct MMDB_entry_s) { + .mmdb = mmdb, + .offset = data_section_offset_for_record(mmdb, node->left_record), + }; + node->right_record_entry = (struct MMDB_entry_s) { + .mmdb = mmdb, + .offset = data_section_offset_for_record(mmdb, node->right_record), + }; + + return MMDB_SUCCESS; +} + +LOCAL uint32_t data_section_offset_for_record(const MMDB_s *const mmdb, + uint64_t record) +{ + return (uint32_t)record - mmdb->metadata.node_count - + MMDB_DATA_SECTION_SEPARATOR; +} + +int MMDB_get_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + ...) +{ + va_list path; + va_start(path, entry_data); + int status = MMDB_vget_value(start, entry_data, path); + va_end(path); + return status; +} + +int MMDB_vget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + va_list va_path) +{ + int length = path_length(va_path); + const char *path_elem; + int i = 0; + + MAYBE_CHECK_SIZE_OVERFLOW(length, SIZE_MAX / sizeof(const char *) - 1, + MMDB_INVALID_METADATA_ERROR); + + const char **path = calloc(length + 1, sizeof(const char *)); + if (NULL == path) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + while (NULL != (path_elem = va_arg(va_path, char *))) { + path[i] = path_elem; + i++; + } + path[i] = NULL; + + int status = MMDB_aget_value(start, entry_data, path); + + free((char **)path); + + return status; +} + +LOCAL int path_length(va_list va_path) +{ + int i = 0; + const char *ignore; + va_list path_copy; + va_copy(path_copy, va_path); + + while (NULL != (ignore = va_arg(path_copy, char *))) { + i++; + } + + va_end(path_copy); + + return i; +} + +int MMDB_aget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + const char *const *const path) +{ + const MMDB_s *const mmdb = start->mmdb; + uint32_t offset = start->offset; + + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + DEBUG_NL; + DEBUG_MSG("looking up value by path"); + + CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data); + + DEBUG_NL; + DEBUG_MSGF("top level element is a %s", type_num_to_name(entry_data->type)); + + /* Can this happen? It'd probably represent a pathological case under + * normal use, but there's nothing preventing someone from passing an + * invalid MMDB_entry_s struct to this function */ + if (!entry_data->has_data) { + return MMDB_INVALID_LOOKUP_PATH_ERROR; + } + + const char *path_elem; + int i = 0; + while (NULL != (path_elem = path[i++])) { + DEBUG_NL; + DEBUG_MSGF("path elem = %s", path_elem); + + /* XXX - it'd be good to find a quicker way to skip through these + entries that doesn't involve decoding them + completely. Basically we need to just use the size from the + control byte to advance our pointer rather than calling + decode_one(). */ + if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { + int status = lookup_path_in_array(path_elem, mmdb, entry_data); + if (MMDB_SUCCESS != status) { + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return status; + } + } else if (entry_data->type == MMDB_DATA_TYPE_MAP) { + int status = lookup_path_in_map(path_elem, mmdb, entry_data); + if (MMDB_SUCCESS != status) { + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return status; + } + } else { + /* Once we make the code traverse maps & arrays without calling + * decode_one() we can get rid of this. */ + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; + } + } + + return MMDB_SUCCESS; +} + +LOCAL int lookup_path_in_array(const char *path_elem, + const MMDB_s *const mmdb, + MMDB_entry_data_s *entry_data) +{ + uint32_t size = entry_data->data_size; + char *first_invalid; + + int saved_errno = errno; + errno = 0; + int array_index = strtol(path_elem, &first_invalid, 10); + if (ERANGE == errno) { + errno = saved_errno; + return MMDB_INVALID_LOOKUP_PATH_ERROR; + } + errno = saved_errno; + + if (array_index < 0) { + array_index += size; + + if (array_index < 0) { + return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; + } + } + + if (*first_invalid || (uint32_t)array_index >= size) { + return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; + } + + for (int i = 0; i < array_index; i++) { + /* We don't want to follow a pointer here. If the next element is a + * pointer we simply skip it and keep going */ + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); + int status = skip_map_or_array(mmdb, entry_data); + if (MMDB_SUCCESS != status) { + return status; + } + } + + MMDB_entry_data_s value; + CHECKED_DECODE_ONE_FOLLOW(mmdb, entry_data->offset_to_next, &value); + memcpy(entry_data, &value, sizeof(MMDB_entry_data_s)); + + return MMDB_SUCCESS; +} + +LOCAL int lookup_path_in_map(const char *path_elem, + const MMDB_s *const mmdb, + MMDB_entry_data_s *entry_data) +{ + uint32_t size = entry_data->data_size; + uint32_t offset = entry_data->offset_to_next; + size_t path_elem_len = strlen(path_elem); + + while (size-- > 0) { + MMDB_entry_data_s key, value; + CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, &key); + + uint32_t offset_to_value = key.offset_to_next; + + if (MMDB_DATA_TYPE_UTF8_STRING != key.type) { + return MMDB_INVALID_DATA_ERROR; + } + + if (key.data_size == path_elem_len && + !memcmp(path_elem, key.utf8_string, path_elem_len)) { + + DEBUG_MSG("found key matching path elem"); + + CHECKED_DECODE_ONE_FOLLOW(mmdb, offset_to_value, &value); + memcpy(entry_data, &value, sizeof(MMDB_entry_data_s)); + return MMDB_SUCCESS; + } else { + /* We don't want to follow a pointer here. If the next element is + * a pointer we simply skip it and keep going */ + CHECKED_DECODE_ONE(mmdb, offset_to_value, &value); + int status = skip_map_or_array(mmdb, &value); + if (MMDB_SUCCESS != status) { + return status; + } + offset = value.offset_to_next; + } + } + + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; +} + +LOCAL int skip_map_or_array(const MMDB_s *const mmdb, + MMDB_entry_data_s *entry_data) +{ + if (entry_data->type == MMDB_DATA_TYPE_MAP) { + uint32_t size = entry_data->data_size; + while (size-- > 0) { + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // key + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value + int status = skip_map_or_array(mmdb, entry_data); + if (MMDB_SUCCESS != status) { + return status; + } + } + } else if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { + uint32_t size = entry_data->data_size; + while (size-- > 0) { + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value + int status = skip_map_or_array(mmdb, entry_data); + if (MMDB_SUCCESS != status) { + return status; + } + } + } + + return MMDB_SUCCESS; +} + +LOCAL int decode_one_follow(const MMDB_s *const mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data) +{ + CHECKED_DECODE_ONE(mmdb, offset, entry_data); + if (entry_data->type == MMDB_DATA_TYPE_POINTER) { + uint32_t next = entry_data->offset_to_next; + CHECKED_DECODE_ONE(mmdb, entry_data->pointer, entry_data); + /* Pointers to pointers are illegal under the spec */ + if (entry_data->type == MMDB_DATA_TYPE_POINTER) { + DEBUG_MSG("pointer points to another pointer"); + return MMDB_INVALID_DATA_ERROR; + } + + /* The pointer could point to any part of the data section but the + * next entry for this particular offset may be the one after the + * pointer, not the one after whatever the pointer points to. This + * depends on whether the pointer points to something that is a simple + * value or a compound value. For a compound value, the next one is + * the one after the pointer result, not the one after the pointer. */ + if (entry_data->type != MMDB_DATA_TYPE_MAP + && entry_data->type != MMDB_DATA_TYPE_ARRAY) { + + entry_data->offset_to_next = next; + } + } + + return MMDB_SUCCESS; +} + +#if !MMDB_UINT128_IS_BYTE_ARRAY +LOCAL mmdb_uint128_t get_uint128(const uint8_t *p, int length) +{ + mmdb_uint128_t value = 0; + while (length-- > 0) { + value <<= 8; + value += *p++; + } + return value; +} +#endif + +LOCAL int decode_one(const MMDB_s *const mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data) +{ + const uint8_t *mem = mmdb->data_section; + + // We subtract rather than add as it possible that offset + 1 + // could overflow for a corrupt database while an underflow + // from data_section_size - 1 should not be possible. + if (offset > mmdb->data_section_size - 1) { + DEBUG_MSGF("Offset (%d) past data section (%d)", offset, + mmdb->data_section_size); + return MMDB_INVALID_DATA_ERROR; + } + + entry_data->offset = offset; + entry_data->has_data = true; + + DEBUG_NL; + DEBUG_MSGF("Offset: %i", offset); + + uint8_t ctrl = mem[offset++]; + DEBUG_BINARY("Control byte: %s", ctrl); + + int type = (ctrl >> 5) & 7; + DEBUG_MSGF("Type: %i (%s)", type, type_num_to_name(type)); + + if (type == MMDB_DATA_TYPE_EXTENDED) { + // Subtracting 1 to avoid possible overflow on offset + 1 + if (offset > mmdb->data_section_size - 1) { + DEBUG_MSGF("Extended type offset (%d) past data section (%d)", + offset, + mmdb->data_section_size); + return MMDB_INVALID_DATA_ERROR; + } + type = get_ext_type(mem[offset++]); + DEBUG_MSGF("Extended type: %i (%s)", type, type_num_to_name(type)); + } + + entry_data->type = type; + + if (type == MMDB_DATA_TYPE_POINTER) { + uint8_t psize = ((ctrl >> 3) & 3) + 1; + DEBUG_MSGF("Pointer size: %i", psize); + + // We check that the offset does not extend past the end of the + // database and that the subtraction of psize did not underflow. + if (offset > mmdb->data_section_size - psize || + mmdb->data_section_size < psize) { + DEBUG_MSGF("Pointer offset (%d) past data section (%d)", offset + + psize, + mmdb->data_section_size); + return MMDB_INVALID_DATA_ERROR; + } + entry_data->pointer = get_ptr_from(ctrl, &mem[offset], psize); + DEBUG_MSGF("Pointer to: %i", entry_data->pointer); + + entry_data->data_size = psize; + entry_data->offset_to_next = offset + psize; + return MMDB_SUCCESS; + } + + uint32_t size = ctrl & 31; + switch (size) { + case 29: + // We subtract when checking offset to avoid possible overflow + if (offset > mmdb->data_section_size - 1) { + DEBUG_MSGF("String end (%d, case 29) past data section (%d)", + offset, + mmdb->data_section_size); + return MMDB_INVALID_DATA_ERROR; + } + size = 29 + mem[offset++]; + break; + case 30: + // We subtract when checking offset to avoid possible overflow + if (offset > mmdb->data_section_size - 2) { + DEBUG_MSGF("String end (%d, case 30) past data section (%d)", + offset, + mmdb->data_section_size); + return MMDB_INVALID_DATA_ERROR; + } + size = 285 + get_uint16(&mem[offset]); + offset += 2; + break; + case 31: + // We subtract when checking offset to avoid possible overflow + if (offset > mmdb->data_section_size - 3) { + DEBUG_MSGF("String end (%d, case 31) past data section (%d)", + offset, + mmdb->data_section_size); + return MMDB_INVALID_DATA_ERROR; + } + size = 65821 + get_uint24(&mem[offset]); + offset += 3; + default: + break; + } + + DEBUG_MSGF("Size: %i", size); + + if (type == MMDB_DATA_TYPE_MAP || type == MMDB_DATA_TYPE_ARRAY) { + entry_data->data_size = size; + entry_data->offset_to_next = offset; + return MMDB_SUCCESS; + } + + if (type == MMDB_DATA_TYPE_BOOLEAN) { + entry_data->boolean = size ? true : false; + entry_data->data_size = 0; + entry_data->offset_to_next = offset; + DEBUG_MSGF("boolean value: %s", entry_data->boolean ? "true" : "false"); + return MMDB_SUCCESS; + } + + // Check that the data doesn't extend past the end of the memory + // buffer and that the calculation in doing this did not underflow. + if (offset > mmdb->data_section_size - size || + mmdb->data_section_size < size) { + DEBUG_MSGF("Data end (%d) past data section (%d)", offset + size, + mmdb->data_section_size); + return MMDB_INVALID_DATA_ERROR; + } + + if (type == MMDB_DATA_TYPE_UINT16) { + if (size > 2) { + DEBUG_MSGF("uint16 of size %d", size); + return MMDB_INVALID_DATA_ERROR; + } + entry_data->uint16 = (uint16_t)get_uintX(&mem[offset], size); + DEBUG_MSGF("uint16 value: %u", entry_data->uint16); + } else if (type == MMDB_DATA_TYPE_UINT32) { + if (size > 4) { + DEBUG_MSGF("uint32 of size %d", size); + return MMDB_INVALID_DATA_ERROR; + } + entry_data->uint32 = (uint32_t)get_uintX(&mem[offset], size); + DEBUG_MSGF("uint32 value: %u", entry_data->uint32); + } else if (type == MMDB_DATA_TYPE_INT32) { + if (size > 4) { + DEBUG_MSGF("int32 of size %d", size); + return MMDB_INVALID_DATA_ERROR; + } + entry_data->int32 = get_sintX(&mem[offset], size); + DEBUG_MSGF("int32 value: %i", entry_data->int32); + } else if (type == MMDB_DATA_TYPE_UINT64) { + if (size > 8) { + DEBUG_MSGF("uint64 of size %d", size); + return MMDB_INVALID_DATA_ERROR; + } + entry_data->uint64 = get_uintX(&mem[offset], size); + DEBUG_MSGF("uint64 value: %" PRIu64, entry_data->uint64); + } else if (type == MMDB_DATA_TYPE_UINT128) { + if (size > 16) { + DEBUG_MSGF("uint128 of size %d", size); + return MMDB_INVALID_DATA_ERROR; + } +#if MMDB_UINT128_IS_BYTE_ARRAY + memset(entry_data->uint128, 0, 16); + if (size > 0) { + memcpy(entry_data->uint128 + 16 - size, &mem[offset], size); + } +#else + entry_data->uint128 = get_uint128(&mem[offset], size); +#endif + } else if (type == MMDB_DATA_TYPE_FLOAT) { + if (size != 4) { + DEBUG_MSGF("float of size %d", size); + return MMDB_INVALID_DATA_ERROR; + } + size = 4; + entry_data->float_value = get_ieee754_float(&mem[offset]); + DEBUG_MSGF("float value: %f", entry_data->float_value); + } else if (type == MMDB_DATA_TYPE_DOUBLE) { + if (size != 8) { + DEBUG_MSGF("double of size %d", size); + return MMDB_INVALID_DATA_ERROR; + } + size = 8; + entry_data->double_value = get_ieee754_double(&mem[offset]); + DEBUG_MSGF("double value: %f", entry_data->double_value); + } else if (type == MMDB_DATA_TYPE_UTF8_STRING) { + entry_data->utf8_string = size == 0 ? "" : (char *)&mem[offset]; + entry_data->data_size = size; +#ifdef MMDB_DEBUG + char *string = mmdb_strndup(entry_data->utf8_string, + size > 50 ? 50 : size); + if (NULL == string) { + abort(); + } + DEBUG_MSGF("string value: %s", string); + free(string); +#endif + } else if (type == MMDB_DATA_TYPE_BYTES) { + entry_data->bytes = &mem[offset]; + entry_data->data_size = size; + } + + entry_data->offset_to_next = offset + size; + + return MMDB_SUCCESS; +} + +LOCAL int get_ext_type(int raw_ext_type) +{ + return 7 + raw_ext_type; +} + +LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, + int ptr_size) +{ + uint32_t new_offset; + switch (ptr_size) { + case 1: + new_offset = ( (ctrl & 7) << 8) + ptr[0]; + break; + case 2: + new_offset = 2048 + ( (ctrl & 7) << 16 ) + ( ptr[0] << 8) + ptr[1]; + break; + case 3: + new_offset = 2048 + 524288 + ( (ctrl & 7) << 24 ) + get_uint24(ptr); + break; + case 4: + default: + new_offset = get_uint32(ptr); + break; + } + return new_offset; +} + +int MMDB_get_metadata_as_entry_data_list( + const MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list) +{ + MMDB_s metadata_db = make_fake_metadata_db(mmdb); + + MMDB_entry_s metadata_start = { + .mmdb = &metadata_db, + .offset = 0 + }; + + return MMDB_get_entry_data_list(&metadata_start, entry_data_list); +} + +int MMDB_get_entry_data_list( + MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list) +{ + MMDB_data_pool_s *const pool = data_pool_new(MMDB_POOL_INIT_SIZE); + if (!pool) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + MMDB_entry_data_list_s *const list = data_pool_alloc(pool); + if (!list) { + data_pool_destroy(pool); + return MMDB_OUT_OF_MEMORY_ERROR; + } + + int const status = get_entry_data_list(start->mmdb, start->offset, list, + pool, 0); + + *entry_data_list = data_pool_to_list(pool); + if (!*entry_data_list) { + data_pool_destroy(pool); + return MMDB_OUT_OF_MEMORY_ERROR; + } + + return status; +} + +LOCAL int get_entry_data_list(const MMDB_s *const mmdb, + uint32_t offset, + MMDB_entry_data_list_s *const entry_data_list, + MMDB_data_pool_s *const pool, + int depth) +{ + if (depth >= MAXIMUM_DATA_STRUCTURE_DEPTH) { + DEBUG_MSG("reached the maximum data structure depth"); + return MMDB_INVALID_DATA_ERROR; + } + depth++; + CHECKED_DECODE_ONE(mmdb, offset, &entry_data_list->entry_data); + + switch (entry_data_list->entry_data.type) { + case MMDB_DATA_TYPE_POINTER: + { + uint32_t next_offset = entry_data_list->entry_data.offset_to_next; + uint32_t last_offset; + CHECKED_DECODE_ONE(mmdb, last_offset = + entry_data_list->entry_data.pointer, + &entry_data_list->entry_data); + + /* Pointers to pointers are illegal under the spec */ + if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_POINTER) { + DEBUG_MSG("pointer points to another pointer"); + return MMDB_INVALID_DATA_ERROR; + } + + if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_ARRAY + || entry_data_list->entry_data.type == MMDB_DATA_TYPE_MAP) { + + int status = + get_entry_data_list(mmdb, last_offset, entry_data_list, + pool, depth); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("get_entry_data_list on pointer failed."); + return status; + } + } + entry_data_list->entry_data.offset_to_next = next_offset; + } + break; + case MMDB_DATA_TYPE_ARRAY: + { + uint32_t array_size = entry_data_list->entry_data.data_size; + uint32_t array_offset = entry_data_list->entry_data.offset_to_next; + while (array_size-- > 0) { + MMDB_entry_data_list_s *entry_data_list_to = + data_pool_alloc(pool); + if (!entry_data_list_to) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + int status = + get_entry_data_list(mmdb, array_offset, entry_data_list_to, + pool, depth); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("get_entry_data_list on array element failed."); + return status; + } + + array_offset = entry_data_list_to->entry_data.offset_to_next; + } + entry_data_list->entry_data.offset_to_next = array_offset; + + } + break; + case MMDB_DATA_TYPE_MAP: + { + uint32_t size = entry_data_list->entry_data.data_size; + + offset = entry_data_list->entry_data.offset_to_next; + while (size-- > 0) { + MMDB_entry_data_list_s *list_key = data_pool_alloc(pool); + if (!list_key) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + int status = + get_entry_data_list(mmdb, offset, list_key, pool, depth); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("get_entry_data_list on map key failed."); + return status; + } + + offset = list_key->entry_data.offset_to_next; + + MMDB_entry_data_list_s *list_value = data_pool_alloc(pool); + if (!list_value) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + status = get_entry_data_list(mmdb, offset, list_value, pool, + depth); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("get_entry_data_list on map element failed."); + return status; + } + offset = list_value->entry_data.offset_to_next; + } + entry_data_list->entry_data.offset_to_next = offset; + } + break; + default: + break; + } + + return MMDB_SUCCESS; +} + +LOCAL float get_ieee754_float(const uint8_t *restrict p) +{ + volatile float f; + uint8_t *q = (void *)&f; +/* Windows builds don't use autoconf but we can assume they're all + * little-endian. */ +#if MMDB_LITTLE_ENDIAN || _WIN32 + q[3] = p[0]; + q[2] = p[1]; + q[1] = p[2]; + q[0] = p[3]; +#else + memcpy(q, p, 4); +#endif + return f; +} + +LOCAL double get_ieee754_double(const uint8_t *restrict p) +{ + volatile double d; + uint8_t *q = (void *)&d; +#if MMDB_LITTLE_ENDIAN || _WIN32 + q[7] = p[0]; + q[6] = p[1]; + q[5] = p[2]; + q[4] = p[3]; + q[3] = p[4]; + q[2] = p[5]; + q[1] = p[6]; + q[0] = p[7]; +#else + memcpy(q, p, 8); +#endif + + return d; +} + +LOCAL uint32_t get_uint32(const uint8_t *p) +{ + return p[0] * 16777216U + p[1] * 65536 + p[2] * 256 + p[3]; +} + +LOCAL uint32_t get_uint24(const uint8_t *p) +{ + return p[0] * 65536U + p[1] * 256 + p[2]; +} + +LOCAL uint32_t get_uint16(const uint8_t *p) +{ + return p[0] * 256U + p[1]; +} + +LOCAL uint64_t get_uintX(const uint8_t *p, int length) +{ + uint64_t value = 0; + while (length-- > 0) { + value <<= 8; + value += *p++; + } + return value; +} + +LOCAL int32_t get_sintX(const uint8_t *p, int length) +{ + return (int32_t)get_uintX(p, length); +} + +void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list) +{ + if (entry_data_list == NULL) { + return; + } + data_pool_destroy(entry_data_list->pool); +} + +void MMDB_close(MMDB_s *const mmdb) +{ + free_mmdb_struct(mmdb); +} + +LOCAL void free_mmdb_struct(MMDB_s *const mmdb) +{ + if (!mmdb) { + return; + } + + if (NULL != mmdb->filename) { + FREE_AND_SET_NULL(mmdb->filename); + } + if (NULL != mmdb->file_content) { +#ifdef _WIN32 + UnmapViewOfFile(mmdb->file_content); + /* Winsock is only initialized if open was successful so we only have + * to cleanup then. */ + WSACleanup(); +#else + munmap((void *)mmdb->file_content, mmdb->file_size); +#endif + } + + if (NULL != mmdb->metadata.database_type) { + FREE_AND_SET_NULL(mmdb->metadata.database_type); + } + + free_languages_metadata(mmdb); + free_descriptions_metadata(mmdb); +} + +LOCAL void free_languages_metadata(MMDB_s *mmdb) +{ + if (!mmdb->metadata.languages.names) { + return; + } + + for (size_t i = 0; i < mmdb->metadata.languages.count; i++) { + FREE_AND_SET_NULL(mmdb->metadata.languages.names[i]); + } + FREE_AND_SET_NULL(mmdb->metadata.languages.names); +} + +LOCAL void free_descriptions_metadata(MMDB_s *mmdb) +{ + if (!mmdb->metadata.description.count) { + return; + } + + for (size_t i = 0; i < mmdb->metadata.description.count; i++) { + if (NULL != mmdb->metadata.description.descriptions[i]) { + if (NULL != + mmdb->metadata.description.descriptions[i]->language) { + FREE_AND_SET_NULL( + mmdb->metadata.description.descriptions[i]->language); + } + + if (NULL != + mmdb->metadata.description.descriptions[i]->description) { + FREE_AND_SET_NULL( + mmdb->metadata.description.descriptions[i]->description); + } + FREE_AND_SET_NULL(mmdb->metadata.description.descriptions[i]); + } + } + + FREE_AND_SET_NULL(mmdb->metadata.description.descriptions); +} + +const char *MMDB_lib_version(void) +{ + return PACKAGE_VERSION; +} + +int MMDB_dump_entry_data_list(FILE *const stream, + MMDB_entry_data_list_s *const entry_data_list, + int indent) +{ + int status; + dump_entry_data_list(stream, entry_data_list, indent, &status); + return status; +} + +LOCAL MMDB_entry_data_list_s *dump_entry_data_list( + FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent, + int *status) +{ + switch (entry_data_list->entry_data.type) { + case MMDB_DATA_TYPE_MAP: + { + uint32_t size = entry_data_list->entry_data.data_size; + + print_indentation(stream, indent); + fprintf(stream, "{\n"); + indent += 2; + + for (entry_data_list = entry_data_list->next; + size && entry_data_list; size--) { + + if (MMDB_DATA_TYPE_UTF8_STRING != + entry_data_list->entry_data.type) { + *status = MMDB_INVALID_DATA_ERROR; + return NULL; + } + char *key = + mmdb_strndup( + (char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + if (NULL == key) { + *status = MMDB_OUT_OF_MEMORY_ERROR; + return NULL; + } + + print_indentation(stream, indent); + fprintf(stream, "\"%s\": \n", key); + free(key); + + entry_data_list = entry_data_list->next; + entry_data_list = + dump_entry_data_list(stream, entry_data_list, indent + 2, + status); + + if (MMDB_SUCCESS != *status) { + return NULL; + } + } + + indent -= 2; + print_indentation(stream, indent); + fprintf(stream, "}\n"); + } + break; + case MMDB_DATA_TYPE_ARRAY: + { + uint32_t size = entry_data_list->entry_data.data_size; + + print_indentation(stream, indent); + fprintf(stream, "[\n"); + indent += 2; + + for (entry_data_list = entry_data_list->next; + size && entry_data_list; size--) { + entry_data_list = + dump_entry_data_list(stream, entry_data_list, indent, + status); + if (MMDB_SUCCESS != *status) { + return NULL; + } + } + + indent -= 2; + print_indentation(stream, indent); + fprintf(stream, "]\n"); + } + break; + case MMDB_DATA_TYPE_UTF8_STRING: + { + char *string = + mmdb_strndup((char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + if (NULL == string) { + *status = MMDB_OUT_OF_MEMORY_ERROR; + return NULL; + } + print_indentation(stream, indent); + fprintf(stream, "\"%s\" \n", string); + free(string); + entry_data_list = entry_data_list->next; + } + break; + case MMDB_DATA_TYPE_BYTES: + { + char *hex_string = + bytes_to_hex((uint8_t *)entry_data_list->entry_data.bytes, + entry_data_list->entry_data.data_size); + + if (NULL == hex_string) { + *status = MMDB_OUT_OF_MEMORY_ERROR; + return NULL; + } + + print_indentation(stream, indent); + fprintf(stream, "%s \n", hex_string); + free(hex_string); + + entry_data_list = entry_data_list->next; + } + break; + case MMDB_DATA_TYPE_DOUBLE: + print_indentation(stream, indent); + fprintf(stream, "%f \n", + entry_data_list->entry_data.double_value); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_FLOAT: + print_indentation(stream, indent); + fprintf(stream, "%f \n", + entry_data_list->entry_data.float_value); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT16: + print_indentation(stream, indent); + fprintf(stream, "%u \n", entry_data_list->entry_data.uint16); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT32: + print_indentation(stream, indent); + fprintf(stream, "%u \n", entry_data_list->entry_data.uint32); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_BOOLEAN: + print_indentation(stream, indent); + fprintf(stream, "%s \n", + entry_data_list->entry_data.boolean ? "true" : "false"); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT64: + print_indentation(stream, indent); + fprintf(stream, "%" PRIu64 " \n", + entry_data_list->entry_data.uint64); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT128: + print_indentation(stream, indent); +#if MMDB_UINT128_IS_BYTE_ARRAY + char *hex_string = + bytes_to_hex((uint8_t *)entry_data_list->entry_data.uint128, 16); + if (NULL == hex_string) { + *status = MMDB_OUT_OF_MEMORY_ERROR; + return NULL; + } + fprintf(stream, "0x%s \n", hex_string); + free(hex_string); +#else + uint64_t high = entry_data_list->entry_data.uint128 >> 64; + uint64_t low = (uint64_t)entry_data_list->entry_data.uint128; + fprintf(stream, "0x%016" PRIX64 "%016" PRIX64 " \n", high, + low); +#endif + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_INT32: + print_indentation(stream, indent); + fprintf(stream, "%d \n", entry_data_list->entry_data.int32); + entry_data_list = entry_data_list->next; + break; + default: + *status = MMDB_INVALID_DATA_ERROR; + return NULL; + } + + *status = MMDB_SUCCESS; + return entry_data_list; +} + +LOCAL void print_indentation(FILE *stream, int i) +{ + char buffer[1024]; + int size = i >= 1024 ? 1023 : i; + memset(buffer, 32, size); + buffer[size] = '\0'; + fputs(buffer, stream); +} + +LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size) +{ + char *hex_string; + MAYBE_CHECK_SIZE_OVERFLOW(size, SIZE_MAX / 2 - 1, NULL); + + hex_string = calloc((size * 2) + 1, sizeof(char)); + if (NULL == hex_string) { + return NULL; + } + + for (uint32_t i = 0; i < size; i++) { + sprintf(hex_string + (2 * i), "%02X", bytes[i]); + } + + + + return hex_string; +} + +const char *MMDB_strerror(int error_code) +{ + switch (error_code) { + case MMDB_SUCCESS: + return "Success (not an error)"; + case MMDB_FILE_OPEN_ERROR: + return "Error opening the specified MaxMind DB file"; + case MMDB_CORRUPT_SEARCH_TREE_ERROR: + return "The MaxMind DB file's search tree is corrupt"; + case MMDB_INVALID_METADATA_ERROR: + return "The MaxMind DB file contains invalid metadata"; + case MMDB_IO_ERROR: + return "An attempt to read data from the MaxMind DB file failed"; + case MMDB_OUT_OF_MEMORY_ERROR: + return "A memory allocation call failed"; + case MMDB_UNKNOWN_DATABASE_FORMAT_ERROR: + return + "The MaxMind DB file is in a format this library can't handle (unknown record size or binary format version)"; + case MMDB_INVALID_DATA_ERROR: + return + "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"; + case MMDB_INVALID_LOOKUP_PATH_ERROR: + return + "The lookup path contained an invalid value (like a negative integer for an array index)"; + case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR: + return + "The lookup path does not match the data (key that doesn't exist, array index bigger than the array, expected array or map where none exists)"; + case MMDB_INVALID_NODE_NUMBER_ERROR: + return + "The MMDB_read_node function was called with a node number that does not exist in the search tree"; + case MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR: + return + "You attempted to look up an IPv6 address in an IPv4-only database"; + default: + return "Unknown error code"; + } +} diff --git a/vendor/MaxmindDB/t/CMakeLists.txt b/vendor/MaxmindDB/t/CMakeLists.txt new file mode 100644 index 00000000..afbe484b --- /dev/null +++ b/vendor/MaxmindDB/t/CMakeLists.txt @@ -0,0 +1,46 @@ +add_library(tap + libtap/tap.c +) + +# test programs +set(TEST_TARGET_NAMES + bad_pointers_t + basic_lookup_t + data_entry_list_t + data-pool-t + data_types_t + dump_t + get_value_pointer_bug_t + get_value_t + ipv4_start_cache_t + ipv6_lookup_in_ipv4_t + metadata_pointers_t + metadata_t + no_map_get_value_t + read_node_t + version_t +) + +if(UNIX) # or if (NOT WIN32) + list(APPEND TEST_TARGET_NAMES + bad_databases_t + threads_t + ) + find_package(Threads) +endif() + +foreach(TEST_TARGET_NAME ${TEST_TARGET_NAMES}) + add_executable(${TEST_TARGET_NAME} ${TEST_TARGET_NAME}.c maxminddb_test_helper.c) + target_include_directories(${TEST_TARGET_NAME} PRIVATE ../src) + target_link_libraries(${TEST_TARGET_NAME} maxminddb tap) + + if(UNIX) + target_link_libraries(${TEST_TARGET_NAME} m) + endif() + + if (UNIX) + target_link_libraries(${TEST_TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) + endif() + + add_test(${TEST_TARGET_NAME} ${TEST_TARGET_NAME}) +endforeach() diff --git a/vendor/MaxmindDB/t/Makefile.am b/vendor/MaxmindDB/t/Makefile.am new file mode 100644 index 00000000..d8d416f1 --- /dev/null +++ b/vendor/MaxmindDB/t/Makefile.am @@ -0,0 +1,31 @@ +include $(top_srcdir)/common.mk + +all-local: + cd libtap && $(MAKE) $(AM_MAKEFLAGS) all +clean-local: + cd libtap && $(MAKE) $(AM_MAKEFLAGS) clean + +AM_LDFLAGS = $(top_builddir)/src/libmaxminddb.la +CFLAGS += -I$(top_srcdir)/src + +noinst_LTLIBRARIES = libmmdbtest.la +libmmdbtest_la_SOURCES = maxminddb_test_helper.c maxminddb_test_helper.h + +EXTRA_DIST = compile_c++_t.pl external_symbols_t.pl mmdblookup_t.pl \ + libtap/COPYING libtap/INSTALL libtap/Makefile libtap/README.md \ + libtap/tap.c libtap/tap.h maxmind-db + +check_PROGRAMS = \ + bad_pointers_t bad_databases_t basic_lookup_t data_entry_list_t \ + data-pool-t data_types_t dump_t get_value_t get_value_pointer_bug_t \ + ipv4_start_cache_t ipv6_lookup_in_ipv4_t metadata_t metadata_pointers_t \ + no_map_get_value_t read_node_t threads_t version_t + +data_pool_t_LDFLAGS = $(AM_LDFLAGS) -lm +data_pool_t_SOURCES = data-pool-t.c ../src/data-pool.c + +threads_t_CFLAGS = $(CFLAGS) -pthread + +TESTS = $(check_PROGRAMS) compile_c++_t.pl external_symbols_t.pl mmdblookup_t.pl + +LDADD = libmmdbtest.la libtap/libtap.a diff --git a/vendor/MaxmindDB/t/bad_databases_t.c b/vendor/MaxmindDB/t/bad_databases_t.c new file mode 100644 index 00000000..7b698a38 --- /dev/null +++ b/vendor/MaxmindDB/t/bad_databases_t.c @@ -0,0 +1,67 @@ +// This test currently does not work on Windows as nftw is +// not available. +#define _XOPEN_SOURCE 500 +#include + +#include +#include + +#include "maxminddb_test_helper.h" + +int test_read(const char *path, const struct stat *UNUSED( + sbuf), int flags, struct FTW *UNUSED(ftw)) +{ + // Check if path is a regular file) + if (flags != FTW_F) { + return 0; + } + + MMDB_s *mmdb = (MMDB_s *)calloc(1, sizeof(MMDB_s)); + + if (NULL == mmdb) { + BAIL_OUT("could not allocate memory for our MMDB_s struct"); + } + + int status = MMDB_open(path, MMDB_MODE_MMAP, mmdb); + + if (status != MMDB_SUCCESS) { + ok(1, "received error when opening %s", path); + free(mmdb); + return 0; + } + + int gai_error, mmdb_error; + MMDB_lookup_string(mmdb, "1.1.1.1", &gai_error, &mmdb_error); + if (gai_error != 0) { + BAIL_OUT("could not parse IP address"); + } + + cmp_ok(mmdb_error, "!=", MMDB_SUCCESS, "opening %s returned an error", + path); + + MMDB_close(mmdb); + free(mmdb); + return 0; +} + +int main(void) +{ + char *test_db_dir; +#ifdef _WIN32 + test_db_dir = "../t/maxmind-db/bad-data"; +#else + char cwd[500]; + char *UNUSED(tmp) = getcwd(cwd, 500); + + if (strcmp(basename(cwd), "t") == 0) { + test_db_dir = "./maxmind-db/bad-data"; + } else { + test_db_dir = "./t/maxmind-db/bad-data"; + } +#endif + plan(NO_PLAN); + if (nftw(test_db_dir, test_read, 10, FTW_PHYS) != 0) { + BAIL_OUT("nftw failed"); + } + done_testing(); +} diff --git a/vendor/MaxmindDB/t/bad_pointers_t.c b/vendor/MaxmindDB/t/bad_pointers_t.c new file mode 100644 index 00000000..ab6db482 --- /dev/null +++ b/vendor/MaxmindDB/t/bad_pointers_t.c @@ -0,0 +1,53 @@ +#include "maxminddb_test_helper.h" + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-broken-pointers-24.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + { + const char *ip = "1.1.1.16"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + MMDB_entry_data_s entry_data; + int status = MMDB_get_value(&result.entry, &entry_data, NULL); + + cmp_ok( + status, "==", MMDB_INVALID_DATA_ERROR, + "MMDB_get_value returns MMDB_INVALID_DATA_ERROR for bad pointer in data section"); + + MMDB_entry_data_list_s *entry_data_list; + status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); + + cmp_ok( + status, "==", MMDB_INVALID_DATA_ERROR, + "MMDB_get_entry_data_list returns MMDB_INVALID_DATA_ERROR for bad pointer in data section"); + + MMDB_free_entry_data_list(entry_data_list); + } + + { + const char *ip = "1.1.1.32"; + + int gai_error, mmdb_error; + MMDB_lookup_result_s UNUSED(result) = + MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); + + cmp_ok( + mmdb_error, "==", MMDB_CORRUPT_SEARCH_TREE_ERROR, + "MMDB_lookup_string sets mmdb_error to MMDB_CORRUPT_SEARCH_TREE_ERROR when a search tree record points outside the data section"); + } + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/basic_lookup_t.c b/vendor/MaxmindDB/t/basic_lookup_t.c new file mode 100644 index 00000000..6a66183f --- /dev/null +++ b/vendor/MaxmindDB/t/basic_lookup_t.c @@ -0,0 +1,207 @@ +#include "maxminddb_test_helper.h" + +static void test_big_lookup(void); + +/* These globals are gross but it's the easiest way to mix calling + * for_all_modes() and for_all_record_sizes() */ +static int Current_Mode; +static const char *Current_Mode_Description; + +void test_one_result(MMDB_s *mmdb, MMDB_lookup_result_s result, + const char *ip, const char *expect, + const char *function, const char *filename, + const char *mode_desc) +{ + int is_ok = ok(result.found_entry, + "got a result for an IP in the database - %s - %s - %s - %s", + function, ip, filename, mode_desc); + + if (!is_ok) { + return; + } + + MMDB_entry_data_s data = + data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "result{ip}", "ip", NULL); + + char *string = mmdb_strndup(data.utf8_string, data.data_size); + + char *real_expect; + if (mmdb->metadata.ip_version == 4 || strncmp(expect, "::", 2) == 0) { + real_expect = mmdb_strndup(expect, strlen(expect)); + } else { + // When looking up IPv4 addresses in a mixed DB the result will be + // something like "::1.2.3.4", not just "1.2.3.4". + int maxlen = strlen(expect) + 3; + real_expect = malloc(maxlen); + snprintf(real_expect, maxlen, "::%s", expect); + } + + is(string, real_expect, + "found expected result for ip key - %s - %s - %s - %s", function, ip, + filename, mode_desc); + + free(real_expect); + free(string); +} + +void test_one_ip(MMDB_s *mmdb, const char *ip, const char *expect, + const char *filename, const char *mode_desc) +{ + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + test_one_result(mmdb, result, ip, expect, "MMDB_lookup_string", filename, + mode_desc); + + result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc); + test_one_result(mmdb, result, ip, expect, "MMDB_lookup_addrinfo", filename, + mode_desc); +} + +void run_ipX_tests(const char *filename, const char **missing_ips, + int missing_ips_length, const char *pairs[][2], + int pairs_rows) +{ + const char *path = test_database_path(filename); + int mode = Current_Mode; + const char *mode_desc = Current_Mode_Description; + + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + char desc_suffix[500]; + snprintf(desc_suffix, 500, "%s - %s", filename, mode_desc); + + for (int i = 0; i < missing_ips_length; i++) { + const char *ip = missing_ips[i]; + + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + ok( + !result.found_entry, + "no result entry struct returned for IP address not in the database (string lookup) - %s - %s - %s", + ip, filename, mode_desc); + + result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc); + + ok( + !result.found_entry, + "no result entry struct returned for IP address not in the database (ipv4 lookup) - %s - %s - %s", + ip, filename, mode_desc); + } + + for (int i = 0; i < pairs_rows; i += 1) { + const char *ip_to_lookup = pairs[i][0]; + const char *expect = pairs[i][1]; + + test_one_ip(mmdb, ip_to_lookup, expect, filename, mode_desc); + } + + MMDB_close(mmdb); + free(mmdb); +} + +void run_ipv4_tests(int UNUSED( + record_size), const char *filename, const char *UNUSED( + ignored)) +{ + const char *pairs[9][2] = { + { "1.1.1.1", "1.1.1.1" }, + { "1.1.1.2", "1.1.1.2" }, + { "1.1.1.3", "1.1.1.2" }, + { "1.1.1.7", "1.1.1.4" }, + { "1.1.1.9", "1.1.1.8" }, + { "1.1.1.15", "1.1.1.8" }, + { "1.1.1.17", "1.1.1.16" }, + { "1.1.1.31", "1.1.1.16" }, + { "1.1.1.32", "1.1.1.32" }, + }; + + const char *missing[1] = { "2.3.4.5" }; + run_ipX_tests(filename, missing, 1, pairs, 9); +} + +void run_ipv6_tests(int UNUSED( + record_size), const char *filename, const char *UNUSED( + ignored)) +{ + const char *pairs[9][2] = { + { "::1:ffff:ffff", "::1:ffff:ffff" }, + { "::2:0:0", "::2:0:0" }, + { "::2:0:1a", "::2:0:0" }, + { "::2:0:40", "::2:0:40" }, + { "::2:0:4f", "::2:0:40" }, + { "::2:0:50", "::2:0:50" }, + { "::2:0:52", "::2:0:50" }, + { "::2:0:58", "::2:0:58" }, + { "::2:0:59", "::2:0:58" }, + }; + + const char *missing[2] = { "2.3.4.5", "::abcd" }; + run_ipX_tests(filename, missing, 2, pairs, 9); +} + +void all_record_sizes(int mode, const char *description) +{ + const char *ipv4_filename_fmts[] = { + "MaxMind-DB-test-ipv4-%i.mmdb", + "MaxMind-DB-test-mixed-%i.mmdb" + }; + + Current_Mode = mode; + Current_Mode_Description = description; + + for (int i = 0; i < 2; i++) { + for_all_record_sizes(ipv4_filename_fmts[i], &run_ipv4_tests); + } + + const char *ipv6_filename_fmts[] = { + "MaxMind-DB-test-ipv6-%i.mmdb", + "MaxMind-DB-test-mixed-%i.mmdb" + }; + + for (int i = 0; i < 2; i++) { + for_all_record_sizes(ipv6_filename_fmts[i], &run_ipv6_tests); + } +} + +static void test_big_lookup(void) +{ + const char *const db_filename = "GeoIP2-Precision-Enterprise-Test.mmdb"; + const char *const db_path = test_database_path(db_filename); + ok(db_path != NULL, "got database path"); + + MMDB_s * const mmdb = open_ok(db_path, MMDB_MODE_MMAP, "mmap mode"); + ok(mmdb != NULL, "opened MMDB"); + free((char *)db_path); + + int gai_err = 0, mmdb_err = 0; + const char *const ip_address = "81.2.69.160"; + MMDB_lookup_result_s result = MMDB_lookup_string(mmdb, ip_address, &gai_err, + &mmdb_err); + ok(gai_err == 0, "no getaddrinfo error"); + ok(mmdb_err == MMDB_SUCCESS, "no error from maxminddb library"); + ok(result.found_entry, "found IP"); + + MMDB_entry_data_list_s *entry_data_list = NULL; + ok( + MMDB_get_entry_data_list(&result.entry, + &entry_data_list) == MMDB_SUCCESS, + "successfully looked up entry data list" + ); + ok(entry_data_list != NULL, "got an entry_data_list"); + + MMDB_free_entry_data_list(entry_data_list); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&all_record_sizes); + test_big_lookup(); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/compile_c++_t.pl b/vendor/MaxmindDB/t/compile_c++_t.pl new file mode 100644 index 00000000..ec1d8864 --- /dev/null +++ b/vendor/MaxmindDB/t/compile_c++_t.pl @@ -0,0 +1,107 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Cwd qw( abs_path ); +use FindBin qw( $Bin ); + +eval <<'EOF'; +use Test::More 0.88; +use File::Temp qw( tempdir ); +use IPC::Run3 qw( run3 ); +EOF + +if ($@) { + print + "1..0 # skip all tests skipped - these tests need the Test::More 0.88, File::Temp, and IPC::Run3 modules:\n"; + print "$@"; + exit 0; +} + +my $test_db = "$Bin/maxmind-db/test-data/GeoIP2-City-Test.mmdb"; + +my $cpp_code = <<"EOF"; +#include + +int main(int argc, char *argv[]) +{ + const char *fname = "$test_db"; + MMDB_s mmdb; + return MMDB_open(fname, MMDB_MODE_MMAP, &mmdb); +} +EOF + +my $tempdir = tempdir(CLEANUP => 1 ); + +my $file = "$tempdir/open.cpp"; +open my $fh, '>', $file or die $!; +print {$fh} $cpp_code or die $!; +close $fh or die $!; + +my $exe = "$tempdir/open"; + +my $include_dir = abs_path("$Bin/../include"); +my $lib_dir = abs_path("$Bin/../src/.libs"); + +my $cxx = $ENV{CXX} || 'c++'; +_test_cmd( + [ $cxx, $file, "-I$include_dir", "-L$lib_dir", "-lmaxminddb", "-o$exe" ], + qr/^$/, + q{}, + 0, + 'compile C++ program which links against libmaxminddb', +); + +# DYLD_LIBRARY_PATH is for Mac OS X +$ENV{LD_LIBRARY_PATH} = $ENV{DYLD_LIBRARY_PATH} = $lib_dir; + +_test_cmd( + [$exe], + qr/^$/, + q{}, + 0, + 'compiled C++ program executes without errors' +); + +done_testing(); + +sub _test_cmd { + my $cmd = shift; + my $expect_stdout = shift; + my $expect_stderr = shift; + my $expect_status = shift; + my $desc = shift; + + my $stdout; + my $stderr; + run3( + $cmd, + \undef, + \$stdout, + \$stderr, + ); + + my $exit_status = $? >> 8; + + # We don't need to retest that the help output shows up for all errors + if ( defined $expect_stdout ) { + like( + $stdout, + $expect_stdout, + "stdout for @{$cmd}" + ); + } + + if ( ref $expect_stderr ) { + like( $stderr, $expect_stderr, "stderr for @{$cmd}" ); + } + else { + is( $stderr, $expect_stderr, "stderr for @{$cmd}" ); + } + + is( + $exit_status, $expect_status, + "exit status was $expect_status for @{$cmd}" + ); +} diff --git a/vendor/MaxmindDB/t/data-pool-t.c b/vendor/MaxmindDB/t/data-pool-t.c new file mode 100644 index 00000000..56cbaafe --- /dev/null +++ b/vendor/MaxmindDB/t/data-pool-t.c @@ -0,0 +1,374 @@ +#include +#include +#include +#include "libtap/tap.h" +#include +#include "maxminddb_test_helper.h" + +static void test_data_pool_new(void); +static void test_data_pool_destroy(void); +static void test_data_pool_alloc(void); +static void test_data_pool_to_list(void); +static bool create_and_check_list(size_t const, + size_t const); +static void check_block_count(MMDB_entry_data_list_s const *const, + size_t const); + +int main(void) +{ + plan(NO_PLAN); + test_data_pool_new(); + test_data_pool_destroy(); + test_data_pool_alloc(); + test_data_pool_to_list(); + done_testing(); +} + +static void test_data_pool_new(void) +{ + { + MMDB_data_pool_s *const pool = data_pool_new(0); + ok(!pool, "size 0 is not valid"); + } + + { + MMDB_data_pool_s *const pool = data_pool_new(SIZE_MAX - 10); + ok(!pool, "very large size is not valid"); + } + + { + MMDB_data_pool_s *const pool = data_pool_new(512); + ok(pool != NULL, "size 512 is valid"); + cmp_ok(pool->size, "==", 512, "size is 512"); + cmp_ok(pool->used, "==", 0, "used size is 0"); + data_pool_destroy(pool); + } +} + +static void test_data_pool_destroy(void) +{ + { + data_pool_destroy(NULL); + } + + { + MMDB_data_pool_s *const pool = data_pool_new(512); + ok(pool != NULL, "created pool"); + data_pool_destroy(pool); + } +} + +static void test_data_pool_alloc(void) +{ + { + MMDB_data_pool_s *const pool = data_pool_new(1); + ok(pool != NULL, "created pool"); + cmp_ok(pool->used, "==", 0, "used size starts at 0"); + + MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool); + ok(entry1 != NULL, "allocated first entry"); + // Arbitrary so that we can recognize it. + entry1->entry_data.offset = (uint32_t)123; + + cmp_ok(pool->size, "==", 1, "size is still 1"); + cmp_ok(pool->used, "==", 1, "used size is 1 after taking one"); + + MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool); + ok(entry2 != NULL, "got another entry"); + ok(entry1 != entry2, "second entry is different from first entry"); + + cmp_ok(pool->size, "==", 2, "size is 2 (new block)"); + cmp_ok(pool->used, "==", 1, "used size is 1 in current block"); + + ok(entry1->entry_data.offset == 123, + "accessing the original entry's memory is ok"); + + data_pool_destroy(pool); + } + + { + size_t const initial_size = 10; + MMDB_data_pool_s *const pool = data_pool_new(initial_size); + ok(pool != NULL, "created pool"); + + MMDB_entry_data_list_s *entry1 = NULL; + for (size_t i = 0; i < initial_size; i++) { + MMDB_entry_data_list_s *const entry = data_pool_alloc(pool); + ok(entry != NULL, "got an entry"); + // Give each a unique number so we can check it. + entry->entry_data.offset = (uint32_t)i; + if (i == 0) { + entry1 = entry; + } + } + + cmp_ok(pool->size, "==", initial_size, "size is the initial size"); + cmp_ok(pool->used, "==", initial_size, "used size is as expected"); + + MMDB_entry_data_list_s *const entry = data_pool_alloc(pool); + ok(entry != NULL, "got an entry"); + entry->entry_data.offset = (uint32_t)initial_size; + + cmp_ok(pool->size, "==", initial_size * 2, + "size is the initial size*2"); + cmp_ok(pool->used, "==", 1, "used size is as expected"); + + MMDB_entry_data_list_s *const list = data_pool_to_list(pool); + + MMDB_entry_data_list_s *element = list; + for (size_t i = 0; i < initial_size + 1; i++) { + ok( + element->entry_data.offset == (uint32_t)i, + "found offset %" PRIu32 ", should have %zu", + element->entry_data.offset, + i + ); + element = element->next; + } + + ok(entry1->entry_data.offset == (uint32_t)0, + "accessing entry1's original memory is ok after growing the pool"); + + data_pool_destroy(pool); + } +} + +static void test_data_pool_to_list(void) +{ + { + size_t const initial_size = 16; + MMDB_data_pool_s *const pool = data_pool_new(initial_size); + ok(pool != NULL, "created pool"); + + MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool); + ok(entry1 != NULL, "got an entry"); + + MMDB_entry_data_list_s *const list_one_element + = data_pool_to_list(pool); + ok(list_one_element != NULL, "got a list"); + ok(list_one_element == entry1, + "list's first element is the first we retrieved"); + ok(list_one_element->next == NULL, "list is one element in size"); + + MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool); + ok(entry2 != NULL, "got another entry"); + + MMDB_entry_data_list_s *const list_two_elements + = data_pool_to_list(pool); + ok(list_two_elements != NULL, "got a list"); + ok(list_two_elements == entry1, + "list's first element is the first we retrieved"); + ok(list_two_elements->next != NULL, "list has a second element"); + + MMDB_entry_data_list_s *const second_element = list_two_elements->next; + ok(second_element == entry2, + "second item in list is second we retrieved"); + ok(second_element->next == NULL, "list ends with the second element"); + + data_pool_destroy(pool); + } + + { + size_t const initial_size = 1; + MMDB_data_pool_s *const pool = data_pool_new(initial_size); + ok(pool != NULL, "created pool"); + + MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool); + ok(entry1 != NULL, "got an entry"); + + MMDB_entry_data_list_s *const list_one_element + = data_pool_to_list(pool); + ok(list_one_element != NULL, "got a list"); + ok(list_one_element == entry1, + "list's first element is the first we retrieved"); + ok(list_one_element->next == NULL, "list ends with this element"); + + data_pool_destroy(pool); + } + + { + size_t const initial_size = 2; + MMDB_data_pool_s *const pool = data_pool_new(initial_size); + ok(pool != NULL, "created pool"); + + MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool); + ok(entry1 != NULL, "got an entry"); + + MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool); + ok(entry2 != NULL, "got an entry"); + ok(entry1 != entry2, "second entry is different from the first"); + + MMDB_entry_data_list_s *const list_element1 = data_pool_to_list(pool); + ok(list_element1 != NULL, "got a list"); + ok(list_element1 == entry1, + "list's first element is the first we retrieved"); + + MMDB_entry_data_list_s *const list_element2 = list_element1->next; + ok(list_element2 == entry2, + "second element is the second we retrieved"); + ok(list_element2->next == NULL, "list ends with this element"); + + data_pool_destroy(pool); + } + + { + diag("starting test: fill one block save for one spot"); + ok( + create_and_check_list(3, 2), + "fill one block save for one spot" + ); + } + + { + diag("starting test: fill one block"); + ok( + create_and_check_list(3, 3), + "fill one block" + ); + } + + { + diag("starting test: fill one block and use one spot in the next block"); + ok( + create_and_check_list(3, 3 + 1), + "fill one block and use one spot in the next block" + ); + } + + { + diag("starting test: fill two blocks save for one spot"); + ok( + create_and_check_list(3, 3 + 3 * 2 - 1), + "fill two blocks save for one spot" + ); + } + + { + diag("starting test: fill two blocks"); + ok( + create_and_check_list(3, 3 + 3 * 2), + "fill two blocks" + ); + } + + { + diag("starting test: fill two blocks and use one spot in the next"); + ok( + create_and_check_list(3, 3 + 3 * 2 + 1), + "fill two blocks and use one spot in the next" + ); + } + + { + diag("starting test: fill three blocks save for one spot"); + ok( + create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2 - 1), + "fill three blocks save for one spot" + ); + } + + { + diag("starting test: fill three blocks"); + ok( + create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2), + "fill three blocks" + ); + } + + // It would be nice to have a larger number of these, but it's expensive to + // run many. We currently hardcode what this will be anyway, so varying + // this is not very interesting. + size_t const initial_sizes[] = { 1, 2, 32, 64, 128, 256 }; + + size_t const max_element_count = 4096; + + for (size_t i = 0; i < sizeof(initial_sizes) / sizeof(initial_sizes[0]); + i++) { + size_t const initial_size = initial_sizes[i]; + + for (size_t element_count = 0; element_count < max_element_count; + element_count++) { + assert(create_and_check_list(initial_size, element_count)); + } + } +} + +// Use assert() rather than libtap as libtap is significantly slower and we run +// this frequently. +static bool create_and_check_list(size_t const initial_size, + size_t const element_count) +{ + MMDB_data_pool_s *const pool = data_pool_new(initial_size); + assert(pool != NULL); + + assert(pool->used == 0); + + // Hold on to the pointers as we initially see them so that we can check + // they are still valid after building the list. + MMDB_entry_data_list_s **const entry_array + = calloc(element_count, sizeof(MMDB_entry_data_list_s *)); + assert(entry_array != NULL); + + for (size_t i = 0; i < element_count; i++) { + MMDB_entry_data_list_s *const entry = data_pool_alloc(pool); + assert(entry != NULL); + + entry->entry_data.offset = (uint32_t)i; + + entry_array[i] = entry; + } + + MMDB_entry_data_list_s *const list = data_pool_to_list(pool); + + if (element_count == 0) { + assert(list == NULL); + data_pool_destroy(pool); + free(entry_array); + return true; + } + + assert(list != NULL); + + MMDB_entry_data_list_s *element = list; + for (size_t i = 0; i < element_count; i++) { + assert(element->entry_data.offset == (uint32_t)i); + + assert(element == entry_array[i]); + + element = element->next; + } + assert(element == NULL); + + check_block_count(list, initial_size); + + data_pool_destroy(pool); + free(entry_array); + return true; +} + +// Use assert() rather than libtap as libtap is significantly slower and we run +// this frequently. +static void check_block_count(MMDB_entry_data_list_s const *const list, + size_t const initial_size) +{ + size_t got_block_count = 0; + size_t got_element_count = 0; + + MMDB_entry_data_list_s const *element = list; + while (element) { + got_element_count++; + + if (element->pool) { + got_block_count++; + } + + element = element->next; + } + + // Because = * 2^(number of blocks) + double const a = ceil((double)got_element_count / (double)initial_size); + double const b = log2(a); + size_t const expected_block_count = ((size_t)b) + 1; + + assert(got_block_count == expected_block_count); +} diff --git a/vendor/MaxmindDB/t/data_entry_list_t.c b/vendor/MaxmindDB/t/data_entry_list_t.c new file mode 100644 index 00000000..32bf9625 --- /dev/null +++ b/vendor/MaxmindDB/t/data_entry_list_t.c @@ -0,0 +1,353 @@ +#include "maxminddb_test_helper.h" + +MMDB_entry_data_list_s *test_array_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *array = entry_data_list = entry_data_list->next; + cmp_ok(array->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY, + "'array' key's value is an array"); + cmp_ok(array->entry_data.data_size, "==", 3, + "'array' key's value has 3 elements"); + + MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next; + cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "first array entry is a UINT32"); + cmp_ok(idx0->entry_data.uint32, "==", 1, "first array entry value is 1"); + + MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next; + cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "second array entry is a UINT32"); + cmp_ok(idx1->entry_data.uint32, "==", 2, "second array entry value is 2"); + + MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next; + cmp_ok(idx2->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "third array entry is a UINT32"); + cmp_ok(idx2->entry_data.uint32, "==", 3, "third array entry value is 3"); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_boolean_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_BOOLEAN, + "'boolean' key's value is a boolean"); + ok(value->entry_data.boolean, "'boolean' key's value is true"); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_bytes_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_BYTES, + "'bytes' key's value is bytes"); + uint8_t *bytes = malloc(value->entry_data.data_size); + if (NULL == bytes) { + BAIL_OUT("malloc failed"); + } + memcpy(bytes, value->entry_data.bytes, value->entry_data.data_size); + uint8_t expect[] = { 0x00, 0x00, 0x00, 0x2a }; + + ok(memcmp(bytes, expect, 4) == 0, "got expected value for bytes key"); + + free((void *)bytes); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_double_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_DOUBLE, + "'double' key's value is a double"); + + compare_double(value->entry_data.double_value, 42.123456); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_float_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_FLOAT, + "'float' key's value is a float"); + + compare_float(value->entry_data.float_value, 1.1F); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_int32_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_INT32, + "'int32' key's value is an int32"); + + int32_t expect = 1 << 28; + expect *= -1; + cmp_ok(value->entry_data.int32, "==", expect, + "got expected value for int32 key"); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_arrayX_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *arrayX = entry_data_list = entry_data_list->next; + cmp_ok(arrayX->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY, + "'map{mapX}{arrayX}' key's value is an array"); + cmp_ok(arrayX->entry_data.data_size, "==", 3, + "'map{mapX}{arrayX}' key's value has 3 elements"); + + MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next; + cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "first array entry is a UINT32"); + cmp_ok(idx0->entry_data.uint32, "==", 7, "first array entry value is 7"); + + MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next; + cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "second array entry is a UINT32"); + cmp_ok(idx1->entry_data.uint32, "==", 8, "second array entry value is 8"); + + MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next; + cmp_ok(idx2->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "third array entry is a UINT32"); + cmp_ok(idx2->entry_data.uint32, "==", 9, "third array entry value is 9"); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_mapX_key_value_pair(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *mapX_key = entry_data_list = entry_data_list->next; + cmp_ok(mapX_key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "found a map key in 'map{mapX}'"); + const char *mapX_key_name = dup_entry_string_or_bail(mapX_key->entry_data); + + if (strcmp(mapX_key_name, "utf8_stringX") == 0) { + MMDB_entry_data_list_s *mapX_value = + entry_data_list = entry_data_list->next; + cmp_ok(mapX_value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "'map{mapX}{utf8_stringX}' type is utf8_string"); + const char *utf8_stringX_value = dup_entry_string_or_bail( + mapX_value->entry_data); + ok(strcmp(utf8_stringX_value, "hello") == 0, + "map{mapX}{utf8_stringX} value is 'hello'"); + free((void *)utf8_stringX_value); + } else if (strcmp(mapX_key_name, "arrayX") == 0) { + entry_data_list = test_arrayX_value(entry_data_list); + } else { + ok(0, "unknown key found in map{mapX} - %s", mapX_key_name); + } + + free((void *)mapX_key_name); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_map_value(MMDB_entry_data_list_s *entry_data_list) +{ + MMDB_entry_data_list_s *map = entry_data_list = entry_data_list->next; + cmp_ok(map->entry_data.type, "==", MMDB_DATA_TYPE_MAP, + "'map' key's value is a map"); + cmp_ok(map->entry_data.data_size, "==", 1, + "'map' key's value has 1 key/value pair"); + + MMDB_entry_data_list_s *map_key_1 = entry_data_list = entry_data_list->next; + cmp_ok(map_key_1->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "found a map key in 'map'"); + const char *map_key_1_name = + dup_entry_string_or_bail(map_key_1->entry_data); + ok(strcmp(map_key_1_name, "mapX") == 0, "key name is mapX"); + free((void *)map_key_1_name); + + MMDB_entry_data_list_s *mapX = entry_data_list = entry_data_list->next; + cmp_ok(mapX->entry_data.type, "==", MMDB_DATA_TYPE_MAP, + "'map{mapX}' key's value is a map"); + cmp_ok(mapX->entry_data.data_size, "==", 2, + "'map' key's value has 2 key/value pairs"); + + entry_data_list = test_mapX_key_value_pair(entry_data_list); + entry_data_list = test_mapX_key_value_pair(entry_data_list); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_uint128_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT128, + "'uint128' key's value is an uint128"); + +#if MMDB_UINT128_IS_BYTE_ARRAY + uint8_t expect[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ok(memcmp(value->entry_data.uint128, expect, 16) == 0, + "uint128 field is 2**120"); +#else + mmdb_uint128_t expect = 1; + expect <<= 120; + cmp_ok(value->entry_data.uint128, "==", expect, "uint128 field is 2**120"); +#endif + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_uint16_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT16, + "'uint16' key's value is an uint16"); + uint16_t expect = 100; + ok(value->entry_data.uint16 == expect, "uint16 field is 100"); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_uint32_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "'uint32' key's value is an uint32"); + uint32_t expect = 1 << 28; + cmp_ok(value->entry_data.uint32, "==", expect, "uint32 field is 100"); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_uint64_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT64, + "'uint64' key's value is an uint64"); + uint64_t expect = 1; + expect <<= 60; + cmp_ok(value->entry_data.uint64, "==", expect, "uint64 field is 2**60"); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_utf8_string_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; + + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "'utf8_string' key's value is a string"); + const char *utf8_string = dup_entry_string_or_bail(value->entry_data); + // This is hex for "unicode! ☯ - ♫" as bytes + char expect[19] = + { 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x21, 0x20, 0xe2, 0x98, + 0xaf, 0x20, 0x2d, 0x20, 0xe2, 0x99, 0xab, 0x00 }; + + is(utf8_string, expect, "got expected value for utf8_string key"); + + free((void *)utf8_string); + + return entry_data_list; +} + +void run_tests(int mode, const char *description) +{ + const char *filename = "MaxMind-DB-test-decoder.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, description); + free((void *)path); + + char *ip = "1.1.1.1"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, description); + + MMDB_entry_data_list_s *entry_data_list, *first; + int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); + + if (MMDB_SUCCESS != status) { + BAIL_OUT("MMDB_get_entry_data_list failed with %s", + MMDB_strerror(status)); + } else { + cmp_ok(status, "==", MMDB_SUCCESS, + "MMDB_get_entry_data_list succeeded"); + } + + first = entry_data_list; + + cmp_ok(entry_data_list->entry_data.type, "==", MMDB_DATA_TYPE_MAP, + "first entry in entry data list is a map"); + cmp_ok(entry_data_list->entry_data.data_size, "==", 12, + "first map in entry data list has 12 k/v pairs"); + + while (1) { + MMDB_entry_data_list_s *key = entry_data_list = entry_data_list->next; + + if (!key) { + break; + } + + cmp_ok(key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "found a map key"); + + const char *key_name = dup_entry_string_or_bail(key->entry_data); + if (strcmp(key_name, "array") == 0) { + entry_data_list = test_array_value(entry_data_list); + } else if (strcmp(key_name, "boolean") == 0) { + entry_data_list = test_boolean_value(entry_data_list); + } else if (strcmp(key_name, "bytes") == 0) { + entry_data_list = test_bytes_value(entry_data_list); + } else if (strcmp(key_name, "double") == 0) { + entry_data_list = test_double_value(entry_data_list); + } else if (strcmp(key_name, "float") == 0) { + entry_data_list = test_float_value(entry_data_list); + } else if (strcmp(key_name, "int32") == 0) { + entry_data_list = test_int32_value(entry_data_list); + } else if (strcmp(key_name, "map") == 0) { + entry_data_list = test_map_value(entry_data_list); + } else if (strcmp(key_name, "uint128") == 0) { + entry_data_list = test_uint128_value(entry_data_list); + } else if (strcmp(key_name, "uint16") == 0) { + entry_data_list = test_uint16_value(entry_data_list); + } else if (strcmp(key_name, "uint32") == 0) { + entry_data_list = test_uint32_value(entry_data_list); + } else if (strcmp(key_name, "uint64") == 0) { + entry_data_list = test_uint64_value(entry_data_list); + } else if (strcmp(key_name, "utf8_string") == 0) { + entry_data_list = test_utf8_string_value(entry_data_list); + } else { + ok(0, "unknown key found in map - %s", key_name); + } + + free((void *)key_name); + } + + MMDB_free_entry_data_list(first); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/data_types_t.c b/vendor/MaxmindDB/t/data_types_t.c new file mode 100644 index 00000000..53d84bd8 --- /dev/null +++ b/vendor/MaxmindDB/t/data_types_t.c @@ -0,0 +1,439 @@ +#include "maxminddb_test_helper.h" + +void test_all_data_types(MMDB_lookup_result_s *result, const char *ip, + const char *UNUSED(filename), const char *mode_desc) +{ + { + char description[500]; + snprintf(description, 500, "utf8_string field for %s - %s", ip, + mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description, + "utf8_string", NULL); + const char *string = mmdb_strndup(data.utf8_string, data.data_size); + // This is hex for "unicode! ☯ - ♫" as bytes + char expect[19] = + { 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x21, 0x20, 0xe2, 0x98, + 0xaf, 0x20, 0x2d, 0x20, 0xe2, 0x99, 0xab, 0x00 }; + is(string, expect, "got expected utf8_string value"); + + free((char *)string); + } + + { + char description[500]; + snprintf(description, 500, "double field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_DOUBLE, description, "double", NULL); + + compare_double(data.double_value, 42.123456); + } + + { + char description[500]; + snprintf(description, 500, "float field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_FLOAT, description, "float", NULL); + + compare_float(data.float_value, 1.1F); + } + + { + char description[500]; + snprintf(description, 500, "bytes field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_BYTES, description, "bytes", NULL); + uint8_t expect[] = { 0x00, 0x00, 0x00, 0x2a }; + ok(memcmp((uint8_t *)data.bytes, expect, 4) == 0, + "bytes field has expected value"); + } + + { + char description[500]; + snprintf(description, 500, "uint16 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT16, description, "uint16", NULL); + uint16_t expect = 100; + ok(data.uint16 == expect, "uint16 field is 100"); + } + + { + char description[500]; + snprintf(description, 500, "uint32 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "uint32", NULL); + uint32_t expect = 1 << 28; + cmp_ok(data.uint32, "==", expect, "uint32 field is 2**28"); + } + + { + char description[500]; + snprintf(description, 500, "int32 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_INT32, description, "int32", NULL); + int32_t expect = 1 << 28; + expect *= -1; + cmp_ok(data.int32, "==", expect, "int32 field is -(2**28)"); + } + + { + char description[500]; + snprintf(description, 500, "uint64 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT64, description, "uint64", NULL); + uint64_t expect = 1; + expect <<= 60; + cmp_ok(data.uint64, "==", expect, "uint64 field is 2**60"); + } + + { + char description[500]; + snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT128, description, "uint128", + NULL); +#if MMDB_UINT128_IS_BYTE_ARRAY + uint8_t expect[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 2**120"); +#else + mmdb_uint128_t expect = 1; + expect <<= 120; + cmp_ok(data.uint128, "==", expect, "uint128 field is 2**120"); +#endif + } + + { + char description[500]; + snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean", + NULL); + cmp_ok(data.boolean, "==", true, "boolean field is true"); + } + + { + char description[500]; + snprintf(description, 500, "array field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_ARRAY, description, "array", NULL); + ok(data.data_size == 3, "array field has 3 elements"); + + snprintf(description, 500, "array[0] for %s - %s", ip, mode_desc); + data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "0", + NULL); + ok(data.uint32 == 1, "array[0] is 1"); + + snprintf(description, 500, "array[1] for %s - %s", ip, mode_desc); + data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "1", + NULL); + ok(data.uint32 == 2, "array[1] is 1"); + + snprintf(description, 500, "array[2] for %s - %s", ip, mode_desc); + data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "2", + NULL); + ok(data.uint32 == 3, "array[2] is 1"); + } + + { + char description[500]; + snprintf(description, 500, "map field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_MAP, description, "map", NULL); + ok(data.data_size == 1, "map field has 1 element"); + + snprintf(description, 500, "map{mapX} for %s - %s", ip, mode_desc); + + data = + data_ok(result, MMDB_DATA_TYPE_MAP, description, "map", "mapX", + NULL); + ok(data.data_size == 2, "map{mapX} field has 2 elements"); + + snprintf(description, 500, "map{mapX}{utf8_stringX} for %s - %s", ip, + mode_desc); + + data = + data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description, "map", + "mapX", "utf8_stringX", NULL); + const char *string = mmdb_strndup(data.utf8_string, data.data_size); + is(string, "hello", "map{mapX}{utf8_stringX} is 'hello'"); + free((char *)string); + + snprintf(description, 500, "map{mapX}{arrayX} for %s - %s", ip, + mode_desc); + data = + data_ok(result, MMDB_DATA_TYPE_ARRAY, description, "map", "mapX", + "arrayX", NULL); + ok(data.data_size == 3, "map{mapX}{arrayX} field has 3 elements"); + + snprintf(description, 500, "map{mapX}{arrayX}[0] for %s - %s", ip, + mode_desc); + data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX", + "arrayX", "0", NULL); + ok(data.uint32 == 7, "map{mapX}{arrayX}[0] is 7"); + + snprintf(description, 500, "map{mapX}{arrayX}[1] for %s - %s", ip, + mode_desc); + data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX", + "arrayX", "1", NULL); + ok(data.uint32 == 8, "map{mapX}{arrayX}[1] is 8"); + + snprintf(description, 500, "map{mapX}{arrayX}[2] for %s - %s", ip, + mode_desc); + data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX", + "arrayX", "2", NULL); + ok(data.uint32 == 9, "map{mapX}{arrayX}[2] is 9"); + } + +} + +void test_all_data_types_as_zero(MMDB_lookup_result_s *result, const char *ip, + const char *UNUSED( + filename), const char *mode_desc) +{ + { + char description[500]; + snprintf(description, 500, "utf8_string field for %s - %s", ip, + mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description, + "utf8_string", NULL); + is(data.utf8_string, "", "got expected utf8_string value (NULL)"); + } + + { + char description[500]; + snprintf(description, 500, "double field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_DOUBLE, description, "double", NULL); + + compare_double(data.double_value, 0.0); + } + + { + char description[500]; + snprintf(description, 500, "float field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_FLOAT, description, "float", NULL); + + compare_float(data.float_value, 0.0F); + } + + { + char description[500]; + snprintf(description, 500, "bytes field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_BYTES, description, "bytes", NULL); + ok(data.data_size == 0, "bytes field data_size is 0"); + /* In C does it makes sense to write something like this? + uint8_t expect[0] = {}; + ok(memcmp(data.bytes, expect, 0) == 0, "got expected bytes value (NULL)"); */ + } + + { + char description[500]; + snprintf(description, 500, "uint16 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT16, description, "uint16", NULL); + uint16_t expect = 0; + ok(data.uint16 == expect, "uint16 field is 0"); + } + + { + char description[500]; + snprintf(description, 500, "uint32 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT32, description, "uint32", NULL); + uint32_t expect = 0; + cmp_ok(data.uint32, "==", expect, "uint32 field is 0"); + } + + { + char description[500]; + snprintf(description, 500, "int32 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_INT32, description, "int32", NULL); + int32_t expect = 0; + expect *= -1; + cmp_ok(data.int32, "==", expect, "int32 field is 0"); + } + + { + char description[500]; + snprintf(description, 500, "uint64 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT64, description, "uint64", NULL); + uint64_t expect = 0; + cmp_ok(data.uint64, "==", expect, "uint64 field is 0"); + } + + { + char description[500]; + snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_UINT128, description, "uint128", + NULL); +#if MMDB_UINT128_IS_BYTE_ARRAY + uint8_t expect[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 0"); +#else + mmdb_uint128_t expect = 0; + cmp_ok(data.uint128, "==", expect, "uint128 field is 0"); +#endif + } + + { + char description[500]; + snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean", + NULL); + cmp_ok(data.boolean, "==", false, "boolean field is false"); + } + + { + char description[500]; + snprintf(description, 500, "array field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_ARRAY, description, "array", NULL); + ok(data.data_size == 0, "array field has 0 elements"); + } + + { + char description[500]; + snprintf(description, 500, "map field for %s - %s", ip, mode_desc); + + MMDB_entry_data_s data = + data_ok(result, MMDB_DATA_TYPE_MAP, description, "map", NULL); + ok(data.data_size == 0, "map field has 0 elements"); + } +} + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-decoder.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + + // All of the remaining tests require an open mmdb + if (NULL == mmdb) { + diag("could not open %s - skipping remaining tests", path); + return; + } + + free((void *)path); + + { + const char *ip = "not an ip"; + + int gai_error, mmdb_error; + MMDB_lookup_result_s result = + MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); + + cmp_ok(gai_error, "==", EAI_NONAME, + "MMDB_lookup populates getaddrinfo error properly - %s", ip); + + ok(!result.found_entry, + "no result entry struct returned for invalid IP address '%s'", ip); + } + + { + const char *ip = "e900::"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + ok( + !result.found_entry, + "no result entry struct returned for IP address not in the database - %s - %s - %s", + ip, filename, mode_desc); + } + + { + const char *ip = "::1.1.1.1"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + ok( + result.found_entry, + "got a result entry struct for IP address in the database - %s - %s - %s", + ip, filename, mode_desc); + + cmp_ok( + result.entry.offset, ">", 0, + "result.entry.offset > 0 for address in the database - %s - %s - %s", + ip, filename, mode_desc); + + test_all_data_types(&result, ip, filename, mode_desc); + } + + { + const char *ip = "::4.5.6.7"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + ok( + result.found_entry, + "got a result entry struct for IP address in the database - %s - %s - %s", + ip, filename, mode_desc); + + cmp_ok( + result.entry.offset, ">", 0, + "result.entry.offset > 0 for address in the database - %s - %s - %s", + ip, filename, mode_desc); + + test_all_data_types(&result, ip, filename, mode_desc); + } + + { + const char *ip = "::0.0.0.0"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + ok( + result.found_entry, + "got a result entry struct for IP address in the database - %s - %s - %s", + ip, filename, mode_desc); + + test_all_data_types_as_zero(&result, ip, filename, mode_desc); + } + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/dump_t.c b/vendor/MaxmindDB/t/dump_t.c new file mode 100644 index 00000000..45b3c0b4 --- /dev/null +++ b/vendor/MaxmindDB/t/dump_t.c @@ -0,0 +1,103 @@ +#define _XOPEN_SOURCE 700 +#include "maxminddb_test_helper.h" + +#ifdef HAVE_OPEN_MEMSTREAM +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-decoder.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const char *ip = "1.1.1.1"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + MMDB_entry_data_list_s *entry_data_list; + int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); + + ok(MMDB_SUCCESS == status, + "MMDB_get_entry_data_list is successful"); + + char *dump_output; + size_t dump_size; + FILE *stream = open_memstream(&dump_output, &dump_size); + status = MMDB_dump_entry_data_list(stream, entry_data_list, 0); + fclose(stream); + MMDB_free_entry_data_list(entry_data_list); + + ok(MMDB_SUCCESS == status, + "MMDB_dump_entry_data_list is successful - %s", + mode_desc); + + cmp_ok(dump_size, ">", 0, "MMDB_dump produced output - %s", mode_desc); + + char *expect[] = { + "{", + " \"array\": ", + " [", + " 1 ", + " 2 ", + " 3 ", + " ]", + " \"boolean\": ", + " true ", + " \"bytes\": ", + " 0000002A ", + " \"double\": ", + " 42.123456 ", + " \"float\": ", + " 1.100000 ", + " \"int32\": ", + " -268435456 ", + " \"map\": ", + " {", + " \"mapX\": ", + " {", + " \"arrayX\": ", + " [", + " 7 ", + " 8 ", + " 9 ", + " ]", + " \"utf8_stringX\": ", + " \"hello\" ", + " }", + " }", + " \"uint128\": ", + " 0x01000000000000000000000000000000 ", + " \"uint16\": ", + " 100 ", + " \"uint32\": ", + " 268435456 ", + " \"uint64\": ", + " 1152921504606846976 ", + " \"utf8_string\": ", + " \"unicode! ☯ - ♫\" ", + "}" + }; + + for (int i = 0; i < 42; i++) { + ok((strstr(dump_output, expect[i]) != NULL), + "dump output contains expected line (%s) - %s", expect[i], + mode_desc); + } + + free(dump_output); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} +#else +int main(void) +{ + plan(SKIP_ALL, "This test requires the open_memstream() function"); +} +#endif diff --git a/vendor/MaxmindDB/t/external_symbols_t.pl b/vendor/MaxmindDB/t/external_symbols_t.pl new file mode 100644 index 00000000..e1301e8e --- /dev/null +++ b/vendor/MaxmindDB/t/external_symbols_t.pl @@ -0,0 +1,106 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use FindBin qw( $Bin ); + +_skip_tests_if_required_modules_are_not_present(); +_skip_tests_if_nm_is_not_present(); +_test_libs_external_symbols(); + +done_testing(); + +sub _skip_tests_if_required_modules_are_not_present { + eval <<'EOF'; + use Test::More 0.88; + use IPC::Run3 qw( run3 ); +EOF + + if ($@) { + print + "1..0 # skip all tests skipped - these tests need the Test::More 0.88, IPC::Run3 modules:\n"; + print "$@"; + exit 0; + } +} + +sub _skip_tests_if_nm_is_not_present { + run3( + [ 'nm', '-V' ], + \undef, + \undef, + \undef, + ); + + my $exit_status = $? >> 8; + if ($exit_status) { + print + "1..0 # skipp all tests skipped - this test requires the command line utility `nm`.\n"; + exit 0; + } +} + +sub _test_libs_external_symbols { + my @libs = _libs_to_test(); + + if (@libs) { + for my $lib (@libs) { + _test_lib_external_symbols($lib); + } + } + else { + fail('No libs were found to test'); + } +} + +sub _libs_to_test { + my $lib_dir = "$Bin/../src/.libs"; + opendir my $dh, $lib_dir + or die "Failed to open the lib dir at $lib_dir for reading: $!\n"; + my @libs = map { $lib_dir . q{/} . $_ } + grep { $_ =~ m/\.so$/ } readdir $dh; + closedir $dh; + + return @libs; +} + +sub _test_lib_external_symbols { + my $lib = shift; + + my $stdout; + my $stderr; + run3( + [ 'nm', '-g', '--defined-only', $lib ], + \undef, + \$stdout, + \$stderr, + ); + + my $exit_status = $? >> 8; + ok( !$exit_status, 'nm returned a non-error status' ) + or diag($stderr); + + my @external_symbols = _extract_external_symbols($stdout); + is_deeply( + [ grep { $_ !~ m/^MMDB_/ } @external_symbols ], + [], + "$lib exports only MMDB_ symbols" + ); +} + +sub _extract_external_symbols { + my $nm_output = shift; + + my @lines = split /\r\n|\r|\n/, $nm_output; + + my @external_symbols; + for my $line (@lines) { + my @fields = split /\s+/, $line; + die "Unexpected nm output for line $line\n" + if @fields != 3; + push @external_symbols, $fields[2]; + } + + return @external_symbols; +} diff --git a/vendor/MaxmindDB/t/get_value_pointer_bug_t.c b/vendor/MaxmindDB/t/get_value_pointer_bug_t.c new file mode 100644 index 00000000..00fcc339 --- /dev/null +++ b/vendor/MaxmindDB/t/get_value_pointer_bug_t.c @@ -0,0 +1,67 @@ +#include "maxminddb_test_helper.h" + +/* This test exercises a bug found in MMDB_get_value for certain types of + * nested data structures which contain pointers. See + * https://github.com/maxmind/libmaxminddb/issues/2 and + * https://github.com/maxmind/libmaxminddb/issues/3. + * + * There is also the potential for a similar bug when looking up a value by + * path in an array. This is not tested (yet) as we don't have the right test + * data for it. + * + * These tests are somewhat fragile since they depend on a specific data + * layout in the database. Ideally the test would check that this layout + * exists before checking to see if the lookups are correct. + */ + +void test_one_ip(MMDB_s *mmdb, const char *filename, const char *mode_desc, + char *ip, char *country_code) +{ + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + MMDB_entry_data_s entry_data = + data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "country{iso_code}", + "country", "iso_code", NULL); + + if (ok(entry_data.has_data, "found data for country{iso_code}")) { + char *string = + mmdb_strndup(entry_data.utf8_string, entry_data.data_size); + if (!string) { + ok(0, "mmdb_strndup() call failed"); + exit(1); + } + if (!ok(strcmp(string, + country_code) == 0, "iso_code is %s", country_code)) { + diag(" value is %s", string); + } + free(string); + } +} + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "GeoIP2-City-Test.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + /* This exercises a bug where the entire top-level value is a pointer to + * another part of the data section. */ + test_one_ip(mmdb, filename, mode_desc, "2001:218::", "JP"); + /* This exercises a bug where one subnet's data shares part of the data + * with another subnet - in this case it is the "country" key (and others) + * in the top level map. We are testing that the "country" key's value is + * handled correctly. The value _should_ be a pointer to another map. */ + test_one_ip(mmdb, filename, mode_desc, "81.2.69.160", "GB"); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/get_value_t.c b/vendor/MaxmindDB/t/get_value_t.c new file mode 100644 index 00000000..080fe3d2 --- /dev/null +++ b/vendor/MaxmindDB/t/get_value_t.c @@ -0,0 +1,313 @@ +#include "maxminddb_test_helper.h" + +void test_array_0_result(int status, MMDB_entry_data_s entry_data, + char *function) +{ + cmp_ok(status, "==", MMDB_SUCCESS, + "status for %s() is MMDB_SUCCESS - array[0]", function); + ok(entry_data.has_data, "found a value for array[0]"); + cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "returned entry type is uint32 - array[0]"); + cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[0]"); +} + +void test_array_2_result(int status, MMDB_entry_data_s entry_data, + char *function) +{ + cmp_ok(status, "==", MMDB_SUCCESS, + "status for %s() is MMDB_SUCCESS - array[2]", function); + ok(entry_data.has_data, "found a value for array[2]"); + cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "returned entry type is uint32 - array[2]"); + cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[2]"); +} + +void test_array_minus_3_result(int status, MMDB_entry_data_s entry_data, + char *function) +{ + cmp_ok(status, "==", MMDB_SUCCESS, + "status for %s() is MMDB_SUCCESS - array[-3]", function); + ok(entry_data.has_data, "found a value for array[-3]"); + cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "returned entry type is uint32 - array[-3]"); + cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[-3]"); +} + +void test_array_minus_1_result(int status, MMDB_entry_data_s entry_data, + char *function) +{ + cmp_ok(status, "==", MMDB_SUCCESS, + "status for %s() is MMDB_SUCCESS - array[-1]", function); + ok(entry_data.has_data, "found a value for array[-1]"); + cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "returned entry type is uint32 - array[-1]"); + cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[-1]"); +} + + +int call_vget_value(MMDB_entry_s *entry, MMDB_entry_data_s *entry_data, ...) +{ + va_list keys; + va_start(keys, entry_data); + + int status = MMDB_vget_value(entry, entry_data, keys); + + va_end(keys); + + return status; +} + +void test_simple_structure(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-decoder.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const char *ip = "1.1.1.1"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = { "array", "0", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_array_0_result(status, entry_data, "MMDB_aget_value"); + + status = MMDB_get_value(&result.entry, &entry_data, "array", "0", NULL); + test_array_0_result(status, entry_data, "MMDB_get_value"); + + status = + call_vget_value(&result.entry, &entry_data, "array", "0", NULL); + test_array_0_result(status, entry_data, "MMDB_vget_value"); + } + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = { "array", "2", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_array_2_result(status, entry_data, "MMDB_aget_value"); + + status = MMDB_get_value(&result.entry, &entry_data, "array", "2", NULL); + test_array_2_result(status, entry_data, "MMDB_get_value"); + + status = + call_vget_value(&result.entry, &entry_data, "array", "2", NULL); + test_array_2_result(status, entry_data, "MMDB_vget_value"); + } + + + { + MMDB_entry_data_s entry_data; + int status = MMDB_get_value(&result.entry, &entry_data, "array", "zero", + NULL); + cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR, + "MMDB_get_value() returns error on non-integer array index"); + } + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = { "array", "-1", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_array_minus_1_result(status, entry_data, "MMDB_aget_value"); + + status = + MMDB_get_value(&result.entry, &entry_data, "array", "-1", NULL); + test_array_minus_1_result(status, entry_data, "MMDB_get_value"); + + status = + call_vget_value(&result.entry, &entry_data, "array", "-1", NULL); + test_array_minus_1_result(status, entry_data, "MMDB_vget_value"); + } + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = { "array", "-3", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_array_minus_3_result(status, entry_data, "MMDB_aget_value"); + + status = + MMDB_get_value(&result.entry, &entry_data, "array", "-3", NULL); + test_array_minus_3_result(status, entry_data, "MMDB_get_value"); + + status = + call_vget_value(&result.entry, &entry_data, "array", "-3", NULL); + test_array_minus_3_result(status, entry_data, "MMDB_vget_value"); + } + + { + MMDB_entry_data_s entry_data; + int status = MMDB_get_value(&result.entry, &entry_data, "array", "-4", + NULL); + cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR, + "MMDB_get_value() returns error on too large negative integer"); + } + + { + MMDB_entry_data_s entry_data; + int status = + MMDB_get_value(&result.entry, &entry_data, "array", + "-18446744073709551616", + NULL); + cmp_ok(status, "==", MMDB_INVALID_LOOKUP_PATH_ERROR, + "MMDB_get_value() returns error on integer smaller than LONG_MIN"); + } + + + { + MMDB_entry_data_s entry_data; + int status = + MMDB_get_value(&result.entry, &entry_data, "array", + "18446744073709551616", + NULL); + cmp_ok(status, "==", MMDB_INVALID_LOOKUP_PATH_ERROR, + "MMDB_get_value() returns error on integer larger than LONG_MAX"); + } + + MMDB_close(mmdb); + free(mmdb); +} + +void test_complex_map_a_result(int status, MMDB_entry_data_s entry_data, + char *function) +{ + cmp_ok(status, "==", MMDB_SUCCESS, + "status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{a}", + function); + ok(entry_data.has_data, + "found a value for map1{map2}{array}[0]{map3}{a}"); + cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "returned entry type is uint32 - map1{map2}{array}[0]{map3}{a}"); + cmp_ok(entry_data.uint32, "==", 1, + "entry value is 1 - map1{map2}{array}[0]{map3}{a}"); +} + +void test_complex_map_c_result(int status, MMDB_entry_data_s entry_data, + char *function) +{ + cmp_ok( + status, "==", MMDB_SUCCESS, + "status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{c}", + function); + ok(entry_data.has_data, + "found a value for map1{map2}{array}[0]{map3}{c}"); + cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, + "returned entry type is uint32 - map1{map2}{array}[0]{map3}{c}"); + cmp_ok(entry_data.uint32, "==", 3, + "entry value is 3 - map1{map2}{array}[0]{map3}{c}"); +} + +void test_no_result(int status, MMDB_entry_data_s entry_data, char *function, + char *path_description) +{ + cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR, + "status for %s() is MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR - %s", + function, path_description); + ok(!entry_data.has_data, "did not find a value for %s", path_description); +} + +void test_nested_structure(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-nested.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const char *ip = "1.1.1.1"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = + { "map1", "map2", "array", "0", "map3", "a", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_complex_map_a_result(status, entry_data, "MMDB_aget_value"); + + status = MMDB_get_value(&result.entry, &entry_data, + "map1", "map2", "array", "0", "map3", "a", + NULL); + test_complex_map_a_result(status, entry_data, "MMDB_get_value"); + + status = call_vget_value(&result.entry, &entry_data, + "map1", "map2", "array", "0", "map3", "a", + NULL); + test_complex_map_a_result(status, entry_data, "MMDB_vget_value"); + } + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = + { "map1", "map2", "array", "0", "map3", "c", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_complex_map_c_result(status, entry_data, "MMDB_aget_value"); + + status = MMDB_get_value(&result.entry, &entry_data, + "map1", "map2", "array", "0", "map3", "c", + NULL); + test_complex_map_c_result(status, entry_data, "MMDB_get_value"); + + status = call_vget_value(&result.entry, &entry_data, + "map1", "map2", "array", "0", "map3", "c", + NULL); + test_complex_map_c_result(status, entry_data, "MMDB_vget_value"); + } + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = + { "map1", "map42", "array", "0", "map3", "c", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_no_result(status, entry_data, "MMDB_aget_value", + "map1{map42}{array}[0]{map3}{c}"); + + status = MMDB_get_value(&result.entry, &entry_data, + "map1", "map42", "array", "0", "map3", "c", + NULL); + test_no_result(status, entry_data, "MMDB_get_value", + "map1{map42}{array}[0]{map3}{c}"); + + status = call_vget_value(&result.entry, &entry_data, + "map1", "map42", "array", "0", "map3", "c", + NULL); + test_no_result(status, entry_data, "MMDB_vget_value", + "map1{map42}{array}[0]{map3}{c}"); + } + + { + MMDB_entry_data_s entry_data; + const char *lookup_path[] = + { "map1", "map2", "array", "9", "map3", "c", NULL }; + int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); + test_no_result(status, entry_data, "MMDB_aget_value", + "map1{map42}{array}[9]{map3}{c}"); + + status = MMDB_get_value(&result.entry, &entry_data, + "map1", "map2", "array", "9", "map3", "c", + NULL); + test_no_result(status, entry_data, "MMDB_get_value", + "map1{map42}{array}[9]{map3}{c}"); + + status = call_vget_value(&result.entry, &entry_data, + "map1", "map2", "array", "9", "map3", "c", + NULL); + test_no_result(status, entry_data, "MMDB_vget_value", + "map1{map42}{array}[9]{map3}{c}"); + } + + MMDB_close(mmdb); + free(mmdb); +} + +void run_tests(int mode, const char *mode_desc) +{ + test_simple_structure(mode, mode_desc); + test_nested_structure(mode, mode_desc); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/ipv4_start_cache_t.c b/vendor/MaxmindDB/t/ipv4_start_cache_t.c new file mode 100644 index 00000000..a1119c5e --- /dev/null +++ b/vendor/MaxmindDB/t/ipv4_start_cache_t.c @@ -0,0 +1,36 @@ +#include "maxminddb_test_helper.h" + +void test_one_ip(MMDB_s *mmdb, const char *ip, const char *filename, + const char *mode_desc) +{ + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + ok( + result.found_entry, + "got a result for an IPv4 address included in a larger-than-IPv4 subnet - %s - %s", + ip, mode_desc); + + data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "string value for IP", NULL); +} + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-no-ipv4-search-tree.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + test_one_ip(mmdb, "1.1.1.1", filename, mode_desc); + test_one_ip(mmdb, "255.255.255.255", filename, mode_desc); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/ipv6_lookup_in_ipv4_t.c b/vendor/MaxmindDB/t/ipv6_lookup_in_ipv4_t.c new file mode 100644 index 00000000..97f7df54 --- /dev/null +++ b/vendor/MaxmindDB/t/ipv6_lookup_in_ipv4_t.c @@ -0,0 +1,48 @@ +#include "maxminddb_test_helper.h" + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-ipv4-28.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const char *ip = "::abcd"; + int gai_error, mmdb_error; + MMDB_lookup_result_s UNUSED(result) = + MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); + + cmp_ok( + mmdb_error, "==", MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR, + "MMDB_lookup_string sets mmdb_error to MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an IPv6 address in an IPv4-only database"); + + struct addrinfo hints = { + .ai_family = AF_INET6, + .ai_flags = AI_NUMERICHOST + }; + + struct addrinfo *addresses; + gai_error = getaddrinfo("2001:db8:85a3:0:0:8a2e:370:7334", NULL, + &hints, &addresses); + if (gai_error) { + BAIL_OUT("getaddrinfo failed: %s", gai_strerror(gai_error)); + } + + mmdb_error = 0; + MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); + + cmp_ok( + mmdb_error, "==", MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR, + "MMDB_lookup_sockaddr sets mmdb_error to MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an IPv6 address in an IPv4-only database"); + + freeaddrinfo(addresses); + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/maxminddb_test_helper.c b/vendor/MaxmindDB/t/maxminddb_test_helper.c new file mode 100644 index 00000000..fb0ad703 --- /dev/null +++ b/vendor/MaxmindDB/t/maxminddb_test_helper.c @@ -0,0 +1,233 @@ +#if HAVE_CONFIG_H +#include +#endif + +#define _POSIX_C_SOURCE 200112L +#include +#include +#include + +#include "maxminddb.h" +#include "maxminddb_test_helper.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +void for_all_record_sizes(const char *filename_fmt, + void (*tests)(int record_size, const char *filename, + const char *description)) +{ + int sizes[] = { 24, 28, 32 }; + for (int i = 0; i < 3; i++) { + int size = sizes[i]; + + char filename[500]; + snprintf(filename, 500, filename_fmt, size); + + char description[14]; + snprintf(description, 14, "%i bit record", size); + + tests(size, filename, description); + } +} + +void for_all_modes(void (*tests)(int mode, const char *description)) +{ + tests(MMDB_MODE_MMAP, "mmap mode"); +} + +const char *test_database_path(const char *filename) +{ + char *test_db_dir; +#ifdef _WIN32 + test_db_dir = "../t/maxmind-db/test-data"; +#else + char cwd[500]; + char *UNUSED(tmp) = getcwd(cwd, 500); + + if (strcmp(basename(cwd), "t") == 0) { + test_db_dir = "./maxmind-db/test-data"; + } else { + test_db_dir = "./t/maxmind-db/test-data"; + } +#endif + + char *path = malloc(500); + assert(NULL != path); + + snprintf(path, 500, "%s/%s", test_db_dir, filename); + + return (const char *)path; +} + +const char *dup_entry_string_or_bail(MMDB_entry_data_s entry_data) +{ + const char *string = mmdb_strndup(entry_data.utf8_string, + entry_data.data_size); + if (NULL == string) { + BAIL_OUT("mmdb_strndup failed"); + } + + return string; +} + +MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc) +{ + if (0 != access(db_file, R_OK)) { + BAIL_OUT( + "could not read the specified file - %s\nIf you are in a git checkout try running 'git submodule update --init'", + db_file); + } + + MMDB_s *mmdb = (MMDB_s *)calloc(1, sizeof(MMDB_s)); + + if (NULL == mmdb) { + BAIL_OUT("could not allocate memory for our MMDB_s struct"); + } + + int status = MMDB_open(db_file, mode, mmdb); + + int is_ok = ok(MMDB_SUCCESS == status, "open %s status is success - %s", + db_file, mode_desc); + + if (!is_ok) { + diag("open status code = %d (%s)", status, MMDB_strerror(status)); + free(mmdb); + return NULL; + } + + is_ok = ok(mmdb->file_size > 0, + "mmdb struct has been set for %s - %s", + db_file, mode_desc); + + if (!is_ok) { + free(mmdb); + return NULL; + } + + return mmdb; +} + +MMDB_lookup_result_s lookup_string_ok(MMDB_s *mmdb, const char *ip, + const char *file, const char *mode_desc) +{ + int gai_error, mmdb_error; + MMDB_lookup_result_s result = + MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); + + test_lookup_errors(gai_error, mmdb_error, "MMDB_lookup_string", ip, file, + mode_desc); + + return result; +} + +MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb, const char *ip, + const char *file, const char *mode_desc) +{ + int ai_flags = AI_NUMERICHOST; + struct addrinfo hints = { + .ai_socktype = SOCK_STREAM + }; + struct addrinfo *addresses = NULL; + + if (ip[0] == ':') { + hints.ai_flags = ai_flags; +#if defined AI_V4MAPPED && !defined __FreeBSD__ + hints.ai_flags |= AI_V4MAPPED; +#endif + hints.ai_family = AF_INET6; + } else { + hints.ai_flags = ai_flags; + hints.ai_family = AF_INET; + } + + int gai_error = getaddrinfo(ip, NULL, &hints, &addresses); + + int mmdb_error = 0; + MMDB_lookup_result_s result = { .found_entry = false }; + if (gai_error == 0) { + result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); + } + if (NULL != addresses) { + freeaddrinfo(addresses); + } + + test_lookup_errors(gai_error, mmdb_error, "MMDB_lookup_sockaddr", ip, file, + mode_desc); + + return result; +} + +void test_lookup_errors(int gai_error, int mmdb_error, + const char *function, const char *ip, + const char *file, const char *mode_desc) +{ + + int is_ok = ok(0 == gai_error, + "no getaddrinfo error in call to %s for %s - %s - %s", + function, ip, file, mode_desc); + + if (!is_ok) { + diag("error from call to getaddrinfo for %s - %s", + ip, gai_strerror(gai_error)); + } + + is_ok = ok(0 == mmdb_error, + "no MMDB error in call to %s for %s - %s - %s", + function, ip, file, mode_desc); + + if (!is_ok) { + diag("MMDB error - %s", MMDB_strerror(mmdb_error)); + } +} + +MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result, uint32_t expect_type, + const char *description, ...) +{ + va_list keys; + va_start(keys, description); + + MMDB_entry_data_s data; + int status = MMDB_vget_value(&result->entry, &data, keys); + + va_end(keys); + + if (cmp_ok(status, "==", MMDB_SUCCESS, + "no error from call to MMDB_vget_value - %s", description)) { + + if (!cmp_ok(data.type, "==", expect_type, + "got the expected data type - %s", description)) { + + diag(" data type value is %i but expected %i", data.type, + expect_type); + } + } else { + diag(" error from MMDB_vget_value - %s", MMDB_strerror(status)); + } + + return data; +} + +void compare_double(double got, double expect) +{ + double diff = fabs(got - expect); + int is_ok = ok(diff < 0.01, "double value was approximately %2.6f", expect); + if (!is_ok) { + diag(" got %2.6f but expected %2.6f (diff = %2.6f)", + got, expect, diff); + } +} + +void compare_float(float got, float expect) +{ + float diff = fabsf(got - expect); + int is_ok = ok(diff < 0.01, "float value was approximately %2.1f", expect); + if (!is_ok) { + diag(" got %2.4f but expected %2.1f (diff = %2.1f)", + got, expect, diff); + } +} diff --git a/vendor/MaxmindDB/t/maxminddb_test_helper.h b/vendor/MaxmindDB/t/maxminddb_test_helper.h new file mode 100644 index 00000000..a1b2b6da --- /dev/null +++ b/vendor/MaxmindDB/t/maxminddb_test_helper.h @@ -0,0 +1,66 @@ +/* Some test files may require something newer */ +#if !defined(_GNU_SOURCE) && !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200112L +#endif + +#if HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include "maxminddb.h" +#include "maxminddb-compat-util.h" +#include "libtap/tap.h" + +#ifdef _WIN32 +#include +#include + +#define R_OK 4 + +#else +#include +#endif + +#if (_MSC_VER && _MSC_VER < 1900) +/* _snprintf has security issues, but I don't think it is worth + worrying about for the unit tests. */ +#define snprintf _snprintf +#endif + +#ifndef MMDB_TEST_HELPER_C +#define MMDB_TEST_HELPER_C (1) + +#ifdef __GNUC__ +# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) +#else +# define UNUSED +#endif + +#define MAX_DESCRIPTION_LENGTH 500 + +extern void for_all_record_sizes(const char *filename_fmt, + void (*tests)(int record_size, + const char *filename, + const char *description)); +extern void for_all_modes(void (*tests)(int mode, const char *description)); +extern const char *test_database_path(const char *filename); +extern const char *dup_entry_string_or_bail(MMDB_entry_data_s entry_data); +extern MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc); +extern MMDB_lookup_result_s lookup_string_ok(MMDB_s *mmdb, const char *ip, + const char *file, + const char *mode_desc); +extern MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb, const char *ip, + const char *file, + const char *mode_desc); +extern void test_lookup_errors(int gai_error, int mmdb_error, + const char *function, const char *ip, + const char *file, const char *mode_desc); +extern MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result, + uint32_t expect_type, + const char *description, ...); +extern void compare_double(double got, double expect); +extern void compare_float(float got, float expect); + +#endif diff --git a/vendor/MaxmindDB/t/metadata_pointers_t.c b/vendor/MaxmindDB/t/metadata_pointers_t.c new file mode 100644 index 00000000..1c2e1489 --- /dev/null +++ b/vendor/MaxmindDB/t/metadata_pointers_t.c @@ -0,0 +1,32 @@ +#include "maxminddb_test_helper.h" + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-metadata-pointers.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + char *repeated_string = "Lots of pointers in metadata"; + + is(mmdb->metadata.database_type, repeated_string, + "decoded pointer database_type"); + + for (uint16_t i = 0; i < mmdb->metadata.description.count; i++) { + const char *language = + mmdb->metadata.description.descriptions[i]->language; + const char *description = + mmdb->metadata.description.descriptions[i]->description; + is(description, repeated_string, "%s description", language); + } + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/metadata_t.c b/vendor/MaxmindDB/t/metadata_t.c new file mode 100644 index 00000000..da9e1de7 --- /dev/null +++ b/vendor/MaxmindDB/t/metadata_t.c @@ -0,0 +1,226 @@ +#include "maxminddb_test_helper.h" + +void test_metadata(MMDB_s *mmdb, const char *mode_desc) +{ + cmp_ok(mmdb->metadata.node_count, "==", 37, "node_count is 37 - %s", + mode_desc); + cmp_ok(mmdb->metadata.record_size, "==", 24, "record_size is 24 - %s", + mode_desc); + cmp_ok(mmdb->metadata.ip_version, "==", 4, "ip_version is 4 - %s", + mode_desc); + is(mmdb->metadata.database_type, "Test", "database_type is Test - %s", + mode_desc); + // 2013-07-01T00:00:00Z + uint64_t expect_epoch = 1372636800; + int is_ok = + cmp_ok(mmdb->metadata.build_epoch, ">=", expect_epoch, + "build_epoch > %lli", expect_epoch); + if (!is_ok) { + diag(" epoch is %lli", mmdb->metadata.build_epoch); + } + + cmp_ok(mmdb->metadata.binary_format_major_version, "==", 2, + "binary_format_major_version is 2 - %s", mode_desc); + cmp_ok(mmdb->metadata.binary_format_minor_version, "==", 0, + "binary_format_minor_version is 0 - %s", mode_desc); + + cmp_ok(mmdb->metadata.languages.count, "==", 2, "found 2 languages - %s", + mode_desc); + is(mmdb->metadata.languages.names[0], "en", "first language is en - %s", + mode_desc); + is(mmdb->metadata.languages.names[1], "zh", "second language is zh - %s", + mode_desc); + + cmp_ok(mmdb->metadata.description.count, "==", 2, + "found 2 descriptions - %s", mode_desc); + for (uint16_t i = 0; i < mmdb->metadata.description.count; i++) { + const char *language = + mmdb->metadata.description.descriptions[i]->language; + const char *description = + mmdb->metadata.description.descriptions[i]->description; + if (strncmp(language, "en", 2) == 0) { + ok(1, "found en description"); + is(description, "Test Database", "en description"); + } else if (strncmp(language, "zh", 2) == 0) { + ok(1, "found zh description"); + is(description, "Test Database Chinese", "zh description"); + } else { + ok(0, "found unknown description in unexpected language - %s", + language); + } + } + + cmp_ok(mmdb->full_record_byte_size, "==", 6, + "full_record_byte_size is 6 - %s", mode_desc); +} + +MMDB_entry_data_list_s *test_languages_value(MMDB_entry_data_list_s + *entry_data_list) +{ + MMDB_entry_data_list_s *languages = entry_data_list = entry_data_list->next; + + cmp_ok(languages->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY, + "'languages' key's value is an array"); + cmp_ok(languages->entry_data.data_size, "==", 2, + "'languages' key's value has 2 elements"); + + MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next; + cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "first array entry is a UTF8_STRING"); + const char *lang0 = dup_entry_string_or_bail(idx0->entry_data); + is(lang0, "en", "first language is en"); + free((void *)lang0); + + MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next; + cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "second array entry is a UTF8_STRING"); + const char *lang1 = dup_entry_string_or_bail(idx1->entry_data); + is(lang1, "zh", "second language is zh"); + free((void *)lang1); + + return entry_data_list; +} + +MMDB_entry_data_list_s *test_description_value( + MMDB_entry_data_list_s *entry_data_list) +{ + MMDB_entry_data_list_s *description = entry_data_list = + entry_data_list->next; + cmp_ok(description->entry_data.type, "==", MMDB_DATA_TYPE_MAP, + "'description' key's value is a map"); + cmp_ok(description->entry_data.data_size, "==", 2, + "'description' key's value has 2 key/value pairs"); + + for (int i = 0; i < 2; i++) { + MMDB_entry_data_list_s *key = entry_data_list = + entry_data_list->next; + cmp_ok(key->entry_data.type, "==", + MMDB_DATA_TYPE_UTF8_STRING, + "found a map key in 'map'"); + const char *key_name = dup_entry_string_or_bail(key->entry_data); + + MMDB_entry_data_list_s *value = entry_data_list = + entry_data_list->next; + cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "map value is a UTF8_STRING"); + const char *description = + dup_entry_string_or_bail(value->entry_data); + + if (strcmp(key_name, "en") == 0) { + is(description, "Test Database", + "en description == 'Test Database'"); + } else if (strcmp(key_name, "zh") == 0) { + is(description, "Test Database Chinese", + "zh description == 'Test Database Chinese'"); + } else { + ok(0, "unknown key found in description map - %s", key_name); + } + + free((void *)key_name); + free((void *)description); + } + + return entry_data_list; +} + +void test_metadata_as_data_entry_list(MMDB_s * mmdb, + const char *mode_desc) +{ + MMDB_entry_data_list_s *entry_data_list, *first; + int status = + MMDB_get_metadata_as_entry_data_list(mmdb, &entry_data_list); + + first = entry_data_list; + + cmp_ok(status, "==", MMDB_SUCCESS, "get metadata as data_entry_list - %s", + mode_desc); + + cmp_ok(first->entry_data.data_size, "==", 9, + "metadata map has 9 key/value pairs"); + + while (1) { + MMDB_entry_data_list_s *key = entry_data_list = + entry_data_list->next; + + if (!key) { + break; + } + + cmp_ok(key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "found a map key"); + + const char *key_name = dup_entry_string_or_bail(key->entry_data); + if (strcmp(key_name, "node_count") == 0) { + MMDB_entry_data_list_s *value + = entry_data_list = entry_data_list->next; + cmp_ok(value->entry_data.uint32, "==", 37, "node_count == 37"); + } else if (strcmp(key_name, "record_size") == 0) { + MMDB_entry_data_list_s *value + = entry_data_list = entry_data_list->next; + cmp_ok(value->entry_data.uint16, "==", 24, "record_size == 24"); + } else if (strcmp(key_name, "ip_version") == 0) { + MMDB_entry_data_list_s *value + = entry_data_list = entry_data_list->next; + cmp_ok(value->entry_data.uint16, "==", 4, "ip_version == 4"); + } else if (strcmp(key_name, "binary_format_major_version") == 0) { + MMDB_entry_data_list_s *value + = entry_data_list = entry_data_list->next; + cmp_ok(value->entry_data.uint16, "==", 2, + "binary_format_major_version == 2"); + } else if (strcmp(key_name, "binary_format_minor_version") == 0) { + MMDB_entry_data_list_s *value + = entry_data_list = entry_data_list->next; + cmp_ok(value->entry_data.uint16, "==", 0, + "binary_format_minor_version == 0"); + } else if (strcmp(key_name, "build_epoch") == 0) { + MMDB_entry_data_list_s *value + = entry_data_list = entry_data_list->next; + ok(value->entry_data.uint64 > 1373571901, + "build_epoch > 1373571901"); + } else if (strcmp(key_name, "database_type") == 0) { + MMDB_entry_data_list_s *value + = entry_data_list = entry_data_list->next; + const char *type = dup_entry_string_or_bail(value->entry_data); + is(type, "Test", "type == Test"); + free((void *)type); + } else if (strcmp(key_name, "languages") == 0) { + entry_data_list = test_languages_value(entry_data_list); + } else if (strcmp(key_name, "description") == 0) { + entry_data_list = test_description_value(entry_data_list); + } else { + ok(0, "unknown key found in metadata map - %s", + key_name); + } + + free((void *)key_name); + } + + MMDB_free_entry_data_list(first); +} + +void run_tests(int mode, const char *mode_desc) +{ + const char *file = "MaxMind-DB-test-ipv4-24.mmdb"; + const char *path = test_database_path(file); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + + // All of the remaining tests require an open mmdb + if (NULL == mmdb) { + diag("could not open %s - skipping remaining tests", path); + return; + } + free((void *)path); + + test_metadata(mmdb, mode_desc); + test_metadata_as_data_entry_list(mmdb, mode_desc); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/mmdblookup_t.pl b/vendor/MaxmindDB/t/mmdblookup_t.pl new file mode 100644 index 00000000..236360c9 --- /dev/null +++ b/vendor/MaxmindDB/t/mmdblookup_t.pl @@ -0,0 +1,158 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use FindBin qw( $Bin ); + +eval <<'EOF'; +use Test::More 0.88; +use IPC::Run3 qw( run3 ); +EOF + +if ($@) { + print + "1..0 # skip all tests skipped - these tests need the Test::More 0.88, IPC::Run3 and Test::Output modules:\n"; + print "$@"; + exit 0; +} + +my $mmdblookup = "$Bin/../bin/mmdblookup"; +my $test_data_dir = "$Bin/maxmind-db/test-data"; + +{ + ok( -x $mmdblookup, 'mmdblookup script is executable' ); +} + +for my $arg (qw( -h -? --help )) { + _test_stdout( + [$arg], + qr{mmdblookup --file.+This application accepts the following options:}s, + 0, + "help output from $arg" + ); +} + +_test_both( + [], + qr{mmdblookup --file.+This application accepts the following options:}s, + qr{ERROR: You must provide a filename with --file}, + 1, + "help output with no CLI options" +); + +_test_stderr( + [qw( --file foo )], + qr{ERROR: You must provide an IP address with --ip}, + 1, + 'error when no --ip is given' +); + +_test_stdout( + [qw( --version )], + qr/mmdblookup version \d+\.\d+\.\d+/, + 0, + 'output for --version' +); + +_test_stdout( + ['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '2.125.160.216'], + qr/"en"\s*:\s*"Boxford"/, + 0, + 'output for 2.125.160.216' +); + +_test_stdout( + ['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '2.125.160.216', '--verbose'], + qr/Database metadata.+"en"\s*:\s*"Boxford"/s, + 0, + 'verbose output for 2.125.160.216' +); + +_test_stdout( + ['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '2.125.160.216', qw( location latitude )], + qr/^\s*51\.750000 \s*$/s, + 0, + 'output for 2.125.160.216 with lookup path' +); + +_test_stderr( + [ qw( --file this/path/better/not/exist.mmdb --ip 1.2.3.4 ) ], + qr{Can't open this/path/better/not/exist.mmdb}s, + 2, + 'error for file that does not exist' +); + +_test_stderr( + ['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', 'not-an-ip-address' ], + qr{Error from call to getaddrinfo for not-an-ip-address}s, + 3, + 'error for bad IP address' +); + +_test_stderr( + ['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '10.2.3.4' ], + qr{\QCould not find an entry for this IP address (10.2.3.4)}s, + 6, + 'error for bad PI address' +); + +done_testing(); + +sub _test_stdout { + my $args = shift; + my $expect_stdout = shift; + my $expect_status = shift; + my $desc = shift; + + _test_both( $args, $expect_stdout, q{}, $expect_status, $desc ); +} + +sub _test_stderr { + my $args = shift; + my $expect_stderr = shift; + my $expect_status = shift; + my $desc = shift; + + _test_both( $args, undef, $expect_stderr, $expect_status, $desc ); +} + +sub _test_both { + my $args = shift; + my $expect_stdout = shift; + my $expect_stderr = shift; + my $expect_status = shift; + my $desc = shift; + + my $stdout; + my $stderr; + run3( + [ $mmdblookup, @{$args} ], + \undef, + \$stdout, + \$stderr, + ); + + my $exit_status = $? >> 8; + + # We don't need to retest that the help output shows up for all errors + if ( defined $expect_stdout ) { + like( + $stdout, + $expect_stdout, + "stdout for mmdblookup @{$args}" + ); + } + + if ( ref $expect_stderr ) { + like( $stderr, $expect_stderr, "stderr for mmdblookup @{$args}" ); + } + else { + is( $stderr, $expect_stderr, "stderr for mmdblookup @{$args}" ); + } + + is( + $exit_status, $expect_status, + "exit status was $expect_status for mmdblookup @{$args}" + ); +} diff --git a/vendor/MaxmindDB/t/no_map_get_value_t.c b/vendor/MaxmindDB/t/no_map_get_value_t.c new file mode 100644 index 00000000..4bf825a0 --- /dev/null +++ b/vendor/MaxmindDB/t/no_map_get_value_t.c @@ -0,0 +1,32 @@ +#include "maxminddb_test_helper.h" + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-string-value-entries.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const char *ip = "1.1.1.1"; + MMDB_lookup_result_s result = + lookup_string_ok(mmdb, ip, filename, mode_desc); + + MMDB_entry_data_s entry_data; + int status = MMDB_get_value(&result.entry, &entry_data, NULL); + + cmp_ok(status, "==", MMDB_SUCCESS, + "status for MMDB_get_value() is MMDB_SUCCESS"); + ok(entry_data.has_data, "found a value when varargs list is just NULL"); + cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "returned entry type is utf8_string"); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/read_node_t.c b/vendor/MaxmindDB/t/read_node_t.c new file mode 100644 index 00000000..2ec00b72 --- /dev/null +++ b/vendor/MaxmindDB/t/read_node_t.c @@ -0,0 +1,157 @@ +#include "maxminddb_test_helper.h" + +void test_entry_data(MMDB_s *mmdb, MMDB_entry_s *entry, uint32_t node_number, + char * node_record) +{ + MMDB_entry_data_s entry_data; + int status = + MMDB_get_value(entry, &entry_data, "ip", + NULL); + cmp_ok(status, "==", MMDB_SUCCESS, + "successful data lookup for node"); + cmp_ok( + entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, + "returned entry type is UTF8_STRING for %s record of node %i", + node_record, node_number); +} + +void run_read_node_tests(MMDB_s *mmdb, const uint32_t tests[][5], + int test_count, + uint8_t record_size) +{ + for (int i = 0; i < test_count; i++) { + uint32_t node_number = tests[i][0]; + MMDB_search_node_s node; + int status = MMDB_read_node(mmdb, node_number, &node); + if (MMDB_SUCCESS == status) { + cmp_ok(node.left_record, "==", tests[i][1], + "left record for node %i is %i - %i bit DB", + node_number, tests[i][1], record_size); + cmp_ok(node.left_record_type, "==", tests[i][2], + "left record type for node %i is %i", node_number, + tests[i][2]); + if (node.left_record_type == MMDB_RECORD_TYPE_DATA) { + test_entry_data(mmdb, &node.left_record_entry, node_number, + "left"); + } + + cmp_ok(node.right_record, "==", tests[i][3], + "right record for node %i is %i - %i bit DB", + node_number, tests[i][3], record_size); + cmp_ok(node.right_record_type, "==", tests[i][4], + "right record type for node %i is %i", node_number, + tests[i][4]); + + if (node.right_record_type == MMDB_RECORD_TYPE_DATA) { + test_entry_data(mmdb, &node.right_record_entry, node_number, + "right"); + } + } else { + diag("call to MMDB_read_node for node %i failed - %i bit DB", + node_number, + record_size); + } + } +} + +void run_24_bit_record_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-mixed-24.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const uint32_t tests[7][5] = { + { 0, 1, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY }, + { 80, 81, MMDB_RECORD_TYPE_SEARCH_NODE, 197, + MMDB_RECORD_TYPE_SEARCH_NODE, }, + { 96, 97, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY, }, + { 103, 242, MMDB_RECORD_TYPE_EMPTY, 104, + MMDB_RECORD_TYPE_SEARCH_NODE, }, + { 127, 242, MMDB_RECORD_TYPE_EMPTY, 315, + MMDB_RECORD_TYPE_DATA, }, + { 132, 329, MMDB_RECORD_TYPE_DATA, 242, + MMDB_RECORD_TYPE_EMPTY, }, + { 241, 96, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY, } + }; + run_read_node_tests(mmdb, tests, 7, 24); + + MMDB_close(mmdb); + free(mmdb); +} + +void run_28_bit_record_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-mixed-28.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const uint32_t tests[7][5] = { + { 0, 1, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY }, + { 80, 81, MMDB_RECORD_TYPE_SEARCH_NODE, 197, + MMDB_RECORD_TYPE_SEARCH_NODE, }, + { 96, 97, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY, }, + { 103, 242, MMDB_RECORD_TYPE_EMPTY, 104, + MMDB_RECORD_TYPE_SEARCH_NODE, }, + { 127, 242, MMDB_RECORD_TYPE_EMPTY, 315, + MMDB_RECORD_TYPE_DATA, }, + { 132, 329, MMDB_RECORD_TYPE_DATA, 242, + MMDB_RECORD_TYPE_EMPTY, }, + { 241, 96, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY, } + }; + run_read_node_tests(mmdb, tests, 7, 28); + + MMDB_close(mmdb); + free(mmdb); +} + +void run_32_bit_record_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-mixed-32.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const uint32_t tests[7][5] = { + { 0, 1, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY }, + { 80, 81, MMDB_RECORD_TYPE_SEARCH_NODE, 197, + MMDB_RECORD_TYPE_SEARCH_NODE, }, + { 96, 97, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY, }, + { 103, 242, MMDB_RECORD_TYPE_EMPTY, 104, + MMDB_RECORD_TYPE_SEARCH_NODE, }, + { 127, 242, MMDB_RECORD_TYPE_EMPTY, 315, + MMDB_RECORD_TYPE_DATA, }, + { 132, 329, MMDB_RECORD_TYPE_DATA, 242, + MMDB_RECORD_TYPE_EMPTY, }, + { 241, 96, MMDB_RECORD_TYPE_SEARCH_NODE, 242, + MMDB_RECORD_TYPE_EMPTY, } + }; + + run_read_node_tests(mmdb, tests, 7, 32); + + MMDB_close(mmdb); + free(mmdb); +} + +void run_tests(int mode, const char *mode_desc) +{ + run_24_bit_record_tests(mode, mode_desc); + run_28_bit_record_tests(mode, mode_desc); + run_32_bit_record_tests(mode, mode_desc); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); +} diff --git a/vendor/MaxmindDB/t/threads_t.c b/vendor/MaxmindDB/t/threads_t.c new file mode 100644 index 00000000..219c99a4 --- /dev/null +++ b/vendor/MaxmindDB/t/threads_t.c @@ -0,0 +1,196 @@ +#include "maxminddb_test_helper.h" +#include + +typedef struct thread_arg { + int thread_id; + MMDB_s *mmdb; + const char *ip_to_lookup; +} thread_arg_s; + +typedef struct test_result { + const char *ip_looked_up; + int lookup_string_gai_error; + int lookup_string_mmdb_error; + int found_entry; + int get_value_status; + int data_type_ok; + char *data_value; +} test_result_s; + +void test_one_ip(MMDB_s *mmdb, const char *ip, test_result_s *test_result) +{ + + test_result->ip_looked_up = ip; + + int gai_error = 0; + int mmdb_error = 0; + MMDB_lookup_result_s result = + MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); + + test_result->lookup_string_gai_error = gai_error; + if (gai_error) { + return; + } + + test_result->lookup_string_mmdb_error = mmdb_error; + if (mmdb_error) { + return; + } + + test_result->found_entry = result.found_entry; + if (!result.found_entry) { + return; + } + + MMDB_entry_data_s data; + int status = MMDB_get_value(&result.entry, &data, "ip", NULL); + + test_result->get_value_status = status; + if (status) { + return; + } + + test_result->data_type_ok = data.type == MMDB_DATA_TYPE_UTF8_STRING; + if (!test_result->data_type_ok) { + return; + } + + test_result->data_value = mmdb_strndup(data.utf8_string, data.data_size); + + return; +} + +void *run_one_thread(void *arg) +{ + thread_arg_s *thread_arg = (thread_arg_s *)arg; + + MMDB_s *mmdb = thread_arg->mmdb; + const char *ip = thread_arg->ip_to_lookup; + + test_result_s *result = malloc(sizeof(test_result_s)); + test_one_ip(mmdb, ip, result); + + pthread_exit((void *)result); +} + +void process_result(test_result_s *result, const char *expect, + const char *mode_desc) +{ + int is_ok; + is_ok = + ok(!result->lookup_string_gai_error, "no getaddrinfo error for %s - %s", + result->ip_looked_up, mode_desc); + if (!is_ok) { + return; + } + + is_ok = ok(!result->lookup_string_mmdb_error, "no mmdb error for %s - %s", + result->ip_looked_up, mode_desc); + if (!is_ok) { + return; + } + + is_ok = ok(result->found_entry, "got a result for %s in the database - %s", + result->ip_looked_up, mode_desc); + if (!is_ok) { + return; + } + + is_ok = + ok(!result->get_value_status, + "no error from MMDB_get_value for %s - %s", + result->ip_looked_up, + mode_desc); + if (!is_ok) { + return; + } + + is_ok = ok(result->data_type_ok, + "MMDB_get_value found a utf8_string at 'ip' key for %s - %s", + result->ip_looked_up, mode_desc); + if (!is_ok) { + return; + } + + is(result->data_value, expect, + "found expected result for 'ip' key for %s - %s", + result->ip_looked_up, mode_desc); +} + +void run_ipX_tests(MMDB_s *mmdb, const char *pairs[][2], int pairs_rows, + int mode, const char *mode_desc) +{ + pthread_t threads[pairs_rows]; + struct thread_arg thread_args[pairs_rows]; + + for (int i = 0; i < pairs_rows; i += 1) { + thread_args[i].thread_id = i; + thread_args[i].mmdb = mmdb; + thread_args[i].ip_to_lookup = pairs[i][0]; + + int error = pthread_create(&threads[i], NULL, run_one_thread, + &thread_args[i]); + if (error) { + BAIL_OUT("pthread_create failed"); + } + } + + for (int i = 0; i < pairs_rows; i += 1) { + void *thread_return; + int error = pthread_join(threads[i], &thread_return); + if (error) { + BAIL_OUT("pthread_join failed"); + } + + test_result_s *test_result = (test_result_s *)thread_return; + if (NULL != test_result) { + process_result(test_result, pairs[i][1], mode_desc); + if (test_result->data_type_ok) { + free(test_result->data_value); + } + free(test_result); + } + } +} + +void run_tests(int mode, const char *mode_desc) +{ + const char *filename = "MaxMind-DB-test-mixed-32.mmdb"; + const char *path = test_database_path(filename); + MMDB_s *mmdb = open_ok(path, mode, mode_desc); + free((void *)path); + + const char *pairs[18][2] = { + { "1.1.1.1", "::1.1.1.1" }, + { "1.1.1.2", "::1.1.1.2" }, + { "1.1.1.3", "::1.1.1.2" }, + { "1.1.1.7", "::1.1.1.4" }, + { "1.1.1.9", "::1.1.1.8" }, + { "1.1.1.15", "::1.1.1.8" }, + { "1.1.1.17", "::1.1.1.16" }, + { "1.1.1.31", "::1.1.1.16" }, + { "1.1.1.32", "::1.1.1.32" }, + { "::1:ffff:ffff", "::1:ffff:ffff" }, + { "::2:0:0", "::2:0:0" }, + { "::2:0:1a", "::2:0:0" }, + { "::2:0:40", "::2:0:40" }, + { "::2:0:4f", "::2:0:40" }, + { "::2:0:50", "::2:0:50" }, + { "::2:0:52", "::2:0:50" }, + { "::2:0:58", "::2:0:58" }, + { "::2:0:59", "::2:0:58" }, + }; + + run_ipX_tests(mmdb, pairs, 18, mode, mode_desc); + + MMDB_close(mmdb); + free(mmdb); +} + +int main(void) +{ + plan(NO_PLAN); + for_all_modes(&run_tests); + done_testing(); + pthread_exit(NULL); +} diff --git a/vendor/MaxmindDB/t/version_t.c b/vendor/MaxmindDB/t/version_t.c new file mode 100644 index 00000000..9f286614 --- /dev/null +++ b/vendor/MaxmindDB/t/version_t.c @@ -0,0 +1,10 @@ +#include "maxminddb_test_helper.h" + +int main(void) +{ + const char *version = MMDB_lib_version(); + if (ok((version != NULL), "MMDB_lib_version exists")) { + is(version, PACKAGE_VERSION, "version is " PACKAGE_VERSION); + } + done_testing(); +}