void check_error(int gai_error, int mmdb_error) { if (gai_error) { caml_failwith((const char *)MMDB_strerror(gai_error)); } if (mmdb_error) { caml_failwith((const char *)MMDB_strerror(mmdb_error)); } }
vmod_geoip2__init(VRT_CTX, struct vmod_geoip2_geoip2 **vpp, const char *vcl_name, VCL_STRING filename) { struct vmod_geoip2_geoip2 *vp; MMDB_s mmdb; int error; (void)vcl_name; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(vpp); AZ(*vpp); VSL(SLT_Debug, 0, "geoip2.geoip2: Using maxminddb %s", MMDB_lib_version()); error = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb); if (error != MMDB_SUCCESS) { VSL(SLT_Error, 0, "geoip2.geoip2: %s", MMDB_strerror(error)); return; } ALLOC_OBJ(vp, VMOD_GEOIP2_MAGIC); AN(vp); *vpp = vp; vp->mmdb = mmdb; }
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 (!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; }
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(NULL != mmdb, "returned mmdb struct is not null for %s - %s", db_file, mode_desc); if (!is_ok) { free(mmdb); return NULL; } return mmdb; }
lookup_res_t geoip2_lookup_ip(char *ip, int *status) { int gai_error, mmdb_error; MMDB_lookup_result_s result = MMDB_lookup_string(&mmdb, ip, &gai_error, &mmdb_error); if (gai_error) { LM_ERR("getaddrinfo() error\n"); goto error; } if (mmdb_error != MMDB_SUCCESS) { LM_ERR("libmaxminddb error: %s\n", MMDB_strerror(mmdb_error)); goto error; } if (!result.found_entry) { LM_DBG("IP: %s not found\n", ip); goto error; } *status = 0; return result; error: *status = -1; 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)); } }
static char * ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_geoip2_conf_t *gcf = conf; ngx_str_t *value; int status, nelts, i; ngx_http_geoip2_db_t *database; char *rv; ngx_conf_t save; value = cf->args->elts; if (gcf->databases == NULL) { gcf->databases = ngx_array_create(cf->pool, 2, sizeof(ngx_http_geoip2_db_t)); if (gcf->databases == NULL) { return NGX_CONF_ERROR; } } else { nelts = (int) gcf->databases->nelts; database = gcf->databases->elts; for (i = 0; i < nelts; i++) { if (ngx_strcmp(value[1].data, database[i].mmdb.filename) == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Duplicate GeoIP2 mmdb - %V", &value[1]); return NGX_CONF_ERROR; } } } database = ngx_array_push(gcf->databases); if (database == NULL) { return NGX_CONF_ERROR; } status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb); if (status != MMDB_SUCCESS) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "MMDB_open(\"%V\") failed - %s", &value[1], MMDB_strerror(status)); return NGX_CONF_ERROR; } #if (NGX_HAVE_INET6) ngx_memset(&database->address, 0, sizeof(database->address)); #else database->address = 0; #endif save = *cf; cf->handler = ngx_http_geoip2_add_variable; cf->handler_conf = (void *) database; rv = ngx_conf_parse(cf, NULL); *cf = save; return rv; }
bool Init( void ) { if ( handle ) { return true; } handle = new MMDB_s{}; int status = -1; char fs_gameString[MAX_CVAR_VALUE_STRING]; trap->Cvar_VariableStringBuffer( "fs_game", fs_gameString, sizeof(fs_gameString) ); const char *sPath = va( "%s/GeoLite2-Country.mmdb", fs_gameString ); trap->Print( "Loading %s\n", sPath ); fileHandle_t f = NULL_FILE; unsigned int len = trap->FS_Open( sPath, &f, FS_READ ); // no file if ( !f ) { return false; } // empty file if ( !len || len == -1 ) { trap->FS_Close( f ); return false; } // alloc memory for buffer char *buf = (char *)malloc( len + 1 ); if ( !buf ) { return false; } trap->FS_Read( buf, len, f ); trap->FS_Close( f ); buf[len] = '\0'; // pass it off to the json reader char *tmpFilePath = nullptr; trap->Print( "writing to temporary file\n" ); if ( WriteToTemporaryFile( buf, len, &tmpFilePath ) ) { trap->Print( "loading from temporary file %s\n", tmpFilePath ); if ( (status = MMDB_open( tmpFilePath, MMDB_MODE_MMAP, handle ) ) != MMDB_SUCCESS ) { trap->Print( "Error occured while initialising MaxMind GeoIP: \"%s\"\n", MMDB_strerror( status ) ); delete handle; handle = nullptr; return false; } } free( buf ); return true; }
void mmdb_problem_to_error(const int _gai_error, const int _mmdb_error, gchar *where) { if (0 != _gai_error) msg_error("Error from call to getaddrinfo", evt_tag_str("gai_error", gai_strerror(_gai_error)), evt_tag_str("where", where)); if (MMDB_SUCCESS != _mmdb_error) msg_error("maxminddb_error", evt_tag_str("error", MMDB_strerror(_mmdb_error)), evt_tag_str("where", where)); }
void Worker( GeoIPData *data ) { int error = -1, gai_error = -1; MMDB_lookup_result_s result = MMDB_lookup_string( handle, data->getIp().c_str(), &gai_error, &error ); if ( error != MMDB_SUCCESS || gai_error != 0 ) { std::string *str = data->getData(); *str = MMDB_strerror( error ); data->setStatus( 0 ); // error return; } if ( result.found_entry ) { MMDB_entry_s entry = result.entry; MMDB_entry_data_s entry_data; if ( (error = MMDB_get_value( &entry, &entry_data, "names", "en", NULL )) != MMDB_SUCCESS ) { std::string *str = data->getData(); *str = MMDB_strerror( error ); data->setStatus( 0 ); // error return; } if ( entry_data.has_data ) { std::string *str = data->getData(); *str = entry_data.utf8_string; data->setStatus( 1 ); return; } else { *data->getData() = "Unknown"; data->setStatus( 1 ); return; } } else { *data->getData() = "Unknown"; data->setStatus( 1 ); return; } }
static VALUE geoip2_compat_initialize(VALUE self, VALUE path) { Check_Type(path, T_STRING); char* db = StringValuePtr(path); MMDB_s *mmdb; Data_Get_Struct(self, MMDB_s, mmdb); int status = MMDB_open(db, MMDB_MODE_MMAP, mmdb); if (status != MMDB_SUCCESS) { rb_raise(egeoip2_compat_Exception, "GeoIP2Compat - %s: %s", MMDB_strerror(status), db ); } return self; }
// ------------------------------------------------------------------------------------------------ DbHnd::DbHnd(CSStr filepath, Uint32 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 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)); } }
F_NONNULL static void geoip2_list_xlate_recurse(geoip2_t* db, nlist_t* nl, struct in6_addr ip, unsigned depth, const uint32_t node_count, const uint32_t node_num) { dmn_assert(db); dmn_assert(nl); dmn_assert(depth < 129U); if(!depth) { log_err("plugin_geoip: map '%s': GeoIP2 database '%s': Error while traversing tree nodes: depth too low", db->map_name, db->pathname); siglongjmp(db->jbuf, 1); } // skip v4-like spaces other than canonical compat area if( (depth == 32 && ( !memcmp(ip.s6_addr, start_v4mapped, 12U) || !memcmp(ip.s6_addr, start_siit, 12U) || !memcmp(ip.s6_addr, start_wkp, 12U) )) || (depth == 96U && !memcmp(ip.s6_addr, start_teredo, 4U)) || (depth == 112U && !memcmp(ip.s6_addr, start_6to4, 2U)) ) return; MMDB_search_node_s node; int read_rv = MMDB_read_node(&db->mmdb, node_num, &node); if(read_rv != MMDB_SUCCESS) { log_err("plugin_geoip: map '%s': GeoIP2 database '%s': Error while traversing tree nodes: %s", db->map_name, db->pathname, MMDB_strerror(read_rv)); siglongjmp(db->jbuf, 1); } const uint32_t zero_node_num = node.left_record; const uint32_t one_node_num = node.right_record; const unsigned new_depth = depth - 1U; const unsigned mask = 128U - new_depth; if(zero_node_num >= node_count) nlist_append(nl, ip.s6_addr, mask, geoip2_get_dclist_cached(db, zero_node_num - node_count)); else geoip2_list_xlate_recurse(db, nl, ip, new_depth, node_count, zero_node_num); SETBIT_v6(ip.s6_addr, mask - 1U); if(one_node_num >= node_count) nlist_append(nl, ip.s6_addr, mask, geoip2_get_dclist_cached(db, one_node_num - node_count)); else geoip2_list_xlate_recurse(db, nl, ip, new_depth, node_count, one_node_num); }
// lookup an ip address using the maxmind db and return the value // lookup_path described in this doc: http://maxmind.github.io/MaxMind-DB/ const char * geo_lookup(MMDB_s *const mmdb_handle, const char *ipstr, const char **lookup_path) { char *data = NULL; // Lookup IP in the DB int gai_error, mmdb_error; MMDB_lookup_result_s result = MMDB_lookup_string(mmdb_handle, ipstr, &gai_error, &mmdb_error); if (0 != gai_error) { #if DEBUG fprintf(stderr, "[INFO] Error from getaddrinfo for %s - %s\n\n", ipstr, gai_strerror(gai_error)); #endif return NULL; } if (MMDB_SUCCESS != mmdb_error) { #if DEBUG fprintf(stderr, "[ERROR] Got an error from libmaxminddb: %s\n\n", MMDB_strerror(mmdb_error)); #endif return NULL; } // Parse results MMDB_entry_data_s entry_data; int exit_code = 0; if (result.found_entry) { int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); if (MMDB_SUCCESS != status) { #if DEBUG fprintf( stderr, "[WARN] Got an error looking up the entry data. Make sure \ the lookup_path is correct. %s\n", MMDB_strerror(status)); #endif exit_code = 4; return NULL; }
// ------------------------------------------------------------------------------------------------ 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)); }
// Open the maxmind db file int open_mmdb(MMDB_s *mmdb_handle) { int mmdb_baddb = MMDB_open(MMDB_CITY_PATH, MMDB_MODE_MMAP, mmdb_handle); if (mmdb_baddb != MMDB_SUCCESS) { #if DEBUG fprintf(stderr, "[ERROR] open_mmdb: Can't open %s - %s\n", MMDB_CITY_PATH, MMDB_strerror(mmdb_baddb)); if (MMDB_IO_ERROR == mmdb_baddb) { fprintf(stderr, "[ERROR] open_mmdb: IO error: %s\n", strerror(mmdb_baddb)); } #endif mmdb_handle = NULL; return 1; } return 0; }
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; }
static gboolean _mmdb_load_entry_data_list(GeoIPParser *self, const gchar *input, MMDB_entry_data_list_s **entry_data_list) { int _gai_error, mmdb_error; MMDB_lookup_result_s result = MMDB_lookup_string(self->database, input, &_gai_error, &mmdb_error); if (!result.found_entry) { mmdb_problem_to_error(_gai_error, mmdb_error, "lookup"); return FALSE; } mmdb_error = MMDB_get_entry_data_list(&result.entry, entry_data_list); if (MMDB_SUCCESS != mmdb_error) { msg_debug("GeoIP2: MMDB_get_entry_data_list", evt_tag_str("error", MMDB_strerror(mmdb_error))); return FALSE; } return TRUE; }
static VALUE geoip2_compat_lookup(VALUE self, VALUE ip) { int status, gai_error, mmdb_error; Check_Type(ip, T_STRING); char* ip_addr = StringValuePtr(ip); MMDB_s *mmdb; Data_Get_Struct(self, MMDB_s, mmdb); MMDB_lookup_result_s result = MMDB_lookup_string(mmdb, ip_addr, &gai_error, &mmdb_error); if (mmdb_error != MMDB_SUCCESS) { rb_raise(egeoip2_compat_Exception, "geoip2_compat - lookup failed: %s", MMDB_strerror(mmdb_error) ); } if (gai_error != 0) { rb_raise(egeoip2_compat_Exception, "geoip2_compat - getaddrinfo failed: %s", gai_strerror(gai_error) ); } if (!result.found_entry) return Qnil; VALUE hash = rb_hash_new(); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_COUNTRY_CODE, "country", "iso_code", NULL); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_COUNTRY_NAME, "country", "names", "en", NULL); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_REGION, "subdivisions", "0", "iso_code", NULL); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_REGION_NAME, "subdivisions", "0", "names", "en", NULL); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_CITY, "city", "names", "en", NULL); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_POSTAL_CODE, "postal", "code", NULL); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_LATITUDE, "location", "latitude", NULL); geoip2_compat_lookup_internal(&result, hash, geoip2_compat_LONGITUDE, "location", "longitude", NULL); return hash; }
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)); } }
static PyObject *Reader_get(PyObject *self, PyObject *args) { char *ip_address = NULL; Reader_obj *mmdb_obj = (Reader_obj *)self; if (!PyArg_ParseTuple(args, "s", &ip_address)) { return NULL; } MMDB_s *mmdb = mmdb_obj->mmdb; if (NULL == mmdb) { PyErr_SetString(PyExc_ValueError, "Attempt to read from a closed MaxMind DB."); return NULL; } int gai_error = 0; int mmdb_error = MMDB_SUCCESS; MMDB_lookup_result_s result = MMDB_lookup_string(mmdb, ip_address, &gai_error, &mmdb_error); if (0 != gai_error) { PyErr_Format(PyExc_ValueError, "'%s' does not appear to be an IPv4 or IPv6 address.", ip_address); return NULL; } if (MMDB_SUCCESS != mmdb_error) { PyObject *exception; if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) { exception = PyExc_ValueError; } else { exception = MaxMindDB_error; } PyErr_Format(exception, "Error looking up %s. %s", ip_address, MMDB_strerror(mmdb_error)); return NULL; } if (!result.found_entry) { Py_RETURN_NONE; } MMDB_entry_data_list_s *entry_data_list = NULL; int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); if (MMDB_SUCCESS != status) { PyErr_Format(MaxMindDB_error, "Error while looking up data for %s. %s", ip_address, MMDB_strerror(status)); MMDB_free_entry_data_list(entry_data_list); return NULL; } MMDB_entry_data_list_s *original_entry_data_list = entry_data_list; PyObject *py_obj = from_entry_data_list(&entry_data_list); MMDB_free_entry_data_list(original_entry_data_list); return py_obj; }
static void geoip2_dcmap_cb(void* data, char* lookup, const unsigned level) { dmn_assert(data); geoip2_dcmap_cb_data_t* state = data; // Explicit out-of-data set from below if(state->out_of_data) return; MMDB_entry_data_s val; if(!level) { mmdb_lookup_utf8_(GEOIP2_PATH_CONTINENT); return; } if(level == 1U) { mmdb_lookup_utf8_(GEOIP2_PATH_COUNTRY); // No further data for Country-level databases if(!state->db->is_city) state->out_of_data = true; return; } if(state->db->city_no_region) { mmdb_lookup_utf8_(GEOIP2_PATH_CITY); state->out_of_data = true; return; } // We only allow for up to 8-9 subdivision levels // (9 will function correctly, but then we won't bother // matching city data after. 8 levels will fully function // and do the city-level at the end if there's not a 9th // level in the database). // If any country actually needs more, we'll have to change // the simplistic '0' + subd_level magic below for lookup strings. if(level > 11U) { state->out_of_data = true; return; } // used to search/fetch subdivision array elements dmn_assert(level >= 2U && level <= 11U); const unsigned subd_level = level - 2U; const char idx[2] = { '0' + subd_level, '\0' }; const char* path_subd[] = { "subdivisions", &idx[0], "iso_code", NULL }; // fetch this level of subdivision data if possible int mmrv = MMDB_aget_value(&state->entry, &val, path_subd); if(mmrv == MMDB_SUCCESS && val.has_data && val.type == MMDB_DATA_TYPE_UTF8_STRING && val.utf8_string) { if(lookup) { memcpy(lookup, val.utf8_string, val.data_size); lookup[val.data_size] = '\0'; } } else if(mmrv == MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) { // no subdivision data left, or at all to begin with, // switch to city and signal end of data depth mmdb_lookup_utf8_(GEOIP2_PATH_CITY); state->out_of_data = true; } else { dmn_log_err("plugin_geoip: map %s: Unexpected error fetching GeoIP2City subdivision data (%s)", state->db->map_name, MMDB_strerror(mmrv)); siglongjmp(state->db->jbuf, 1); } }
static geoip2_t* geoip2_new(const char* pathname, const char* map_name, dclists_t* dclists, const dcmap_t* dcmap, const bool city_auto_mode, const bool city_no_region) { dmn_assert(pathname); dmn_assert(map_name); dmn_assert(dclists); geoip2_t* db = xcalloc(1, sizeof(*db)); int status = MMDB_open(pathname, MMDB_MODE_MMAP, &db->mmdb); if(status != MMDB_SUCCESS) { dmn_log_err("plugin_geoip: map '%s': Failed to open GeoIP2 database '%s': %s", map_name, pathname, MMDB_strerror(status)); free(db); return NULL; } MMDB_metadata_s* meta = &db->mmdb.metadata; if(!geoip2_mmdb_log_meta(meta, map_name, pathname)) { geoip2_destroy(db); return NULL; } // The database format spec indicates that minor version bumps // should be backwards compatible, so we only need to check // the major version here. if(meta->binary_format_major_version != 2U) { dmn_log_err("plugin_geoip: map '%s': GeoIP2 database '%s' has" " unsupported binfmt major version %" PRIu16, map_name, pathname, meta->binary_format_major_version ); geoip2_destroy(db); return NULL; } // Both our own code and the current libmaxminddb seem to have // built-in assumptions based on record_size of 32 or less, // yet the spec allows for larger in the future. if(meta->record_size > 32U) { dmn_log_err("plugin_geoip: map '%s': GeoIP2 database '%s' has" " unsupported record_size %" PRIu16, map_name, pathname, meta->record_size ); geoip2_destroy(db); return NULL; } if(meta->ip_version != 4U && meta->ip_version != 6U) { dmn_log_err("plugin_geoip: map '%s': GeoIP2 database '%s' has" " unsupported ip_version %" PRIu16, map_name, pathname, meta->ip_version ); geoip2_destroy(db); return NULL; } // The check for /City/ is how the official Perl API detects // the City-level data model, so it's probably a reliable bet. // We assume anything that didn't match /City/ is a Country-level // database. This will technically "work" for GeoIP2 if there is no // Country-level info, but everything will default. So, warn about the // Country defaulting if the database_type does not match /Country/. db->is_city = !!strstr(meta->database_type, "City"); if(!db->is_city) { if(city_auto_mode) { dmn_log_err("plugin_geoip: map '%s': GeoIP2 DB '%s' is not a City-level" " database and this map uses auto_dc_coords", map_name, pathname); geoip2_destroy(db); return NULL; } if(!strstr(meta->database_type, "Country")) dmn_log_warn("plugin_geoip: map '%s': Assuming GeoIP2 database '%s'" " has standard MaxMind Country data, but type is actually '%s'", map_name, pathname, meta->database_type ); } db->is_v4 = meta->ip_version == 4U; db->city_auto_mode = city_auto_mode; db->city_no_region = city_no_region; db->pathname = strdup(pathname); db->map_name = strdup(map_name); db->dclists = dclists; db->dcmap = dcmap; return db; }
// ------------------------------------------------------------------------------------------------ SQInteger LookupResult::GetValue(HSQUIRRELVM vm) { const Int32 top = sq_gettop(vm); // The lookup result instance LookupResult * lookup = nullptr; // Attempt to extract the lookup result instance try { lookup = Var< LookupResult * >(vm, 1).value; } catch (const Sqrat::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, false); // Did we fail to extract the argument value? if (SQ_FAILED(arglist.back().mRes)) { return arglist.back().mRes; // Propagate the error } } typedef std::vector< CSStr > 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_throwerror(vm, ToStrF("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 Sqrat::Exception & e) { return sq_throwerror(vm, e.what()); } // Specify that we returned a value return 1; }
vmod_geoip2_lookup(VRT_CTX, struct vmod_geoip2_geoip2 *vp, VCL_STRING path, VCL_IP addr) { MMDB_lookup_result_s res; MMDB_entry_data_s data; const struct sockaddr *sa; socklen_t addrlen; const char **ap, *arrpath[COMPONENT_MAX]; char buf[LOOKUP_PATH_MAX]; char *p, *last; uint32_t i; int error; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(addr); if (!vp) { VSLb(ctx->vsl, SLT_Error, "geoip2.lookup: Database not open"); return (NULL); } if (!path || !*path || strlen(path) >= sizeof(buf)) { VSLb(ctx->vsl, SLT_Error, "geoip2.lookup: Invalid or missing path (%s)", path ? path : "NULL"); return (NULL); } sa = VSA_Get_Sockaddr(addr, &addrlen); AN(sa); res = MMDB_lookup_sockaddr(&vp->mmdb, sa, &error); if (error != MMDB_SUCCESS) { VSLb(ctx->vsl, SLT_Error, "geoip2.lookup: MMDB_lookup_sockaddr: %s", MMDB_strerror(error)); return (NULL); } if (!res.found_entry) { VSLb(ctx->vsl, SLT_Debug, "geoip2.lookup: No entry for this IP address (%s)", VRT_IP_string(ctx, addr)); return (NULL); } strncpy(buf, path, sizeof(buf)); last = NULL; for (p = buf, ap = arrpath; ap < &arrpath[COMPONENT_MAX - 1] && (*ap = strtok_r(p, "/", &last)) != NULL; p = NULL) { if (**ap != '\0') ap++; } *ap = NULL; error = MMDB_aget_value(&res.entry, &data, arrpath); if (error != MMDB_SUCCESS && error != MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) { VSLb(ctx->vsl, SLT_Error, "geoip2.lookup: MMDB_aget_value: %s", MMDB_strerror(error)); return (NULL); } if (!data.has_data) { VSLb(ctx->vsl, SLT_Debug, "geoip2.lookup: No data for this path (%s)", path); return (NULL); } switch (data.type) { case MMDB_DATA_TYPE_BOOLEAN: p = WS_Printf(ctx->ws, "%s", data.boolean ? "true" : "false"); break; case MMDB_DATA_TYPE_BYTES: p = WS_Alloc(ctx->ws, data.data_size * 2 + 1); if (p) for (i = 0; i < data.data_size; i++) sprintf(&p[i * 2], "%02X", data.bytes[i]); break; case MMDB_DATA_TYPE_DOUBLE: p = WS_Printf(ctx->ws, "%f", data.double_value); break; case MMDB_DATA_TYPE_FLOAT: p = WS_Printf(ctx->ws, "%f", data.float_value); break; case MMDB_DATA_TYPE_INT32: p = WS_Printf(ctx->ws, "%i", data.int32); break; case MMDB_DATA_TYPE_UINT16: p = WS_Printf(ctx->ws, "%u", data.uint16); break; case MMDB_DATA_TYPE_UINT32: p = WS_Printf(ctx->ws, "%u", data.uint32); break; case MMDB_DATA_TYPE_UINT64: p = WS_Printf(ctx->ws, "%ju", (uintmax_t)data.uint64); break; case MMDB_DATA_TYPE_UTF8_STRING: p = WS_Alloc(ctx->ws, data.data_size + 1); if (p) { memcpy(p, data.utf8_string, data.data_size); p[data.data_size] = '\0'; } break; default: VSLb(ctx->vsl, SLT_Error, "geoip2.lookup: Unsupported data type (%d)", data.type); return (NULL); } if (!p) VSLb(ctx->vsl, SLT_Error, "geoip2.lookup: Out of workspace"); return (p); }
void OnGeoipCommand() { const auto cmd = CMD_ARGV(1); if (!strcmp(cmd, "version")) { if (!HandleDB.filename) { MF_PrintSrvConsole("\n Database is not loaded.\n"); return; } const auto 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]; strftime(date, sizeof date, "%Y-%m-%d %H:%M:%S UTC", gmtime(reinterpret_cast<const time_t *>(&HandleDB.metadata.build_epoch))); fprintf(stdout, meta_dump, HandleDB.metadata.node_count, HandleDB.metadata.record_size, HandleDB.metadata.ip_version, HandleDB.metadata.binary_format_major_version, HandleDB.metadata.binary_format_minor_version, HandleDB.metadata.build_epoch, date, HandleDB.metadata.database_type); for (size_t i = 0; i < HandleDB.metadata.languages.count; ++i) { fprintf(stdout, "%s", HandleDB.metadata.languages.names[i]); if (i < HandleDB.metadata.languages.count - 1) { fprintf(stdout, " "); } } fprintf(stdout, "\n"); fprintf(stdout, " Description:\n"); for (size_t i = 0; i < HandleDB.metadata.description.count; ++i) { fprintf(stdout, " %s: %s\n", HandleDB.metadata.description.descriptions[i]->language, HandleDB.metadata.description.descriptions[i]->description); } fprintf(stdout, "\n"); } else if (!strcmp(cmd, "dump")) { if (!HandleDB.filename) { MF_PrintSrvConsole("\n Database is not loaded.\n\n"); return; } const auto num_args = CMD_ARGC(); if (num_args < 3) { MF_PrintSrvConsole("\n An IP address must be provided.\n\n"); return; } const auto ip = stripPort(const_cast<char *>(CMD_ARGV(2))); auto gai_error = 0; auto mmdb_error = 0; auto result = MMDB_lookup_string(&HandleDB, ip, &gai_error, &mmdb_error); if (gai_error != 0 || mmdb_error != MMDB_SUCCESS || !result.found_entry) { MF_PrintSrvConsole("\n Either look up failed or no found result.\n\n"); return; } MMDB_entry_data_list_s *entry_data_list = nullptr; int status; if ((status = MMDB_get_entry_data_list(&result.entry, &entry_data_list)) != MMDB_SUCCESS || entry_data_list == nullptr) { MF_PrintSrvConsole("\n Could not retrieve data list - %s.\n\n", MMDB_strerror(status)); return; } const char *file = nullptr; FILE *fp = nullptr; if (num_args > 3) { file = CMD_ARGV(3); fp = fopen(MF_BuildPathname("%s", file), "w"); } if (!fp) { file = nullptr; fp = stdout; } fprintf(fp, "\n"); MMDB_dump_entry_data_list(fp, entry_data_list, 2); fprintf(fp, "\n"); if (file) { fclose(fp); } MMDB_free_entry_data_list(entry_data_list); } else if (!strcmp(cmd, "reload")) { const auto isDatabaseLoaded = HandleDB.filename != nullptr; if (isDatabaseLoaded) { MMDB_close(&HandleDB); } if (loadDatabase() && !NativesRegistered) { MF_AddNatives(GeoipNatives); NativesRegistered = true; } } else { MF_PrintSrvConsole("\n"); MF_PrintSrvConsole(" Usage: geoip <command> [argument]\n"); MF_PrintSrvConsole(" Commands:\n"); MF_PrintSrvConsole(" version - display geoip database metadata\n"); MF_PrintSrvConsole(" reload - reload geoip database\n"); MF_PrintSrvConsole(" dump <ip> [output file] - dump all data from an IP address formatted in a JSON-ish fashion.\n"); MF_PrintSrvConsole(" An output file is mod-based and if not provided, it will print in the console.\n"); MF_PrintSrvConsole("\n"); } }
bool loadDatabase() { const auto isDatabaseLoaded = HandleDB.filename != nullptr; if (isDatabaseLoaded) { return true; } const char *databases[] = { "City", "Country" // Is the default shipped database with AMXX. }; const auto modName = MF_GetModname(); const auto dataDir = MF_GetLocalInfo("amxx_datadir", "addons/amxmodx/data"); char file[260]; auto status = -1; for (auto& database : databases) { // MF_BuildPathname not used because backslash // makes CreateFileMapping failing under windows. ke::SafeSprintf(file, sizeof file, "%s/%s/GeoLite2-%s.mmdb", modName, dataDir, database); status = MMDB_open(file, MMDB_MODE_MMAP, &HandleDB); if (status == MMDB_SUCCESS) { break; } if (status != MMDB_FILE_OPEN_ERROR) { MF_Log("Could not open %s - %s", file, MMDB_strerror(status)); if (status == MMDB_IO_ERROR) { MF_Log(" IO error: %s", strerror(errno)); } } } if (status != MMDB_SUCCESS) { MF_Log("Could not find GeoIP2 databases. Disabled natives."); return false; } MF_Log("Database info: %s %i.%i", HandleDB.metadata.description.descriptions[0]->description, HandleDB.metadata.binary_format_major_version, HandleDB.metadata.binary_format_minor_version); // Retrieve supported languages. for (size_t i = 0; i < HandleDB.metadata.languages.count; i++) { LangList.append(ke::AString(HandleDB.metadata.languages.names[i])); } return true; }
void check_status(int status) { if (MMDB_SUCCESS != status) { caml_invalid_argument((const char *)MMDB_strerror(status)); } }
bool Init( void ) { if ( handle ) { return true; } handle = new MMDB_s{}; int status = -1; const char *sPath = "GeoLite2-Country.mmdb"; trap->Print( "Loading %s\n", sPath ); fileHandle_t f = NULL_FILE; unsigned int len = trap->FS_Open( sPath, &f, FS_READ ); // no file if ( !f ) { return false; } // empty file if ( !len || len == -1 ) { trap->FS_Close( f ); return false; } // alloc memory for buffer char *buf = (char *)malloc( len + 1 ); if ( !buf ) { return false; } trap->FS_Read( buf, len, f ); trap->FS_Close( f ); buf[len] = '\0'; const char *extension = nullptr; for ( const char *p = sPath + strlen(sPath); p != sPath; p-- ) { if ( *p == '.' ) { extension = p; break; } } char *tmpFilePath = nullptr; if ( WriteToTemporaryFile( buf, len, &tmpFilePath, extension ) ) { trap->Print( "Failed to create temporary file\n" ); free( buf ); return false; } trap->Print( "loading from temporary file %s\n", tmpFilePath ); status = MMDB_open( tmpFilePath, MMDB_MODE_MMAP, handle ); if ( status != MMDB_SUCCESS ) { trap->Print( "Error occured while initialising MaxMind GeoIP: \"%s\"\n", MMDB_strerror( status ) ); delete handle; handle = nullptr; free( tmpFilePath ); free( buf ); return false; } free( tmpFilePath ); free( buf ); return true; }