LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address, const char **lookup_path, int lookup_path_length) { 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 (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 {
static void tf_geoip_maxminddb_call(LogTemplateFunction *self, gpointer s, const LogTemplateInvokeArgs *args, GString *result) { GString **argv = (GString **) args->bufs->pdata; TFMaxMindDBState *state = (TFMaxMindDBState *) s; int _gai_error, mmdb_error; MMDB_lookup_result_s mmdb_result = MMDB_lookup_string(state->database, argv[0]->str, &_gai_error, &mmdb_error); if (!mmdb_result.found_entry) { mmdb_problem_to_error(_gai_error, mmdb_error, "tflookup"); return; } MMDB_entry_data_s entry_data; mmdb_error = MMDB_aget_value(&mmdb_result.entry, &entry_data, (const char *const* const)state->entry_path); if (mmdb_error != MMDB_SUCCESS) { mmdb_problem_to_error(0, mmdb_error, "tfget_value"); return; } if (entry_data.has_data) append_mmdb_entry_data_to_gstring(result, &entry_data); return; }
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_close(mmdb); free(mmdb); }
// 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; }
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); } }
// ------------------------------------------------------------------------------------------------ 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; }
int geoip2_get_field(lookup_res_t ip_data, char *field, char *buf) { char *path_arr[MAX_PATH_DEPTH+1]; int i = 0; csv_record *path_list, *it; str field_str; MMDB_entry_data_s entry_data; int status; int len = 0; field_str.s = check_short_fields(field); if (!field_str.s) field_str.s = field; field_str.len = strlen(field_str.s); path_list = __parse_csv_record(&field_str, CSV_SIMPLE|CSV_DUP_FIELDS, FIELD_PATH_SEP); if (!path_list) { LM_ERR("bad field:'%s'\n", field); return -1; } for (it = path_list; it; it = it->next) { if (i == MAX_PATH_DEPTH) { LM_ERR("unknown field:'%s'\n", field); goto error; } path_arr[i++] = it->s.s; } path_arr[i] = NULL; status = MMDB_aget_value(&ip_data.entry, &entry_data, (const char *const *const)path_arr); if (status != MMDB_SUCCESS) { LM_ERR("Failed to get IP data field: %s\n", field); goto error; } if (!entry_data.has_data) { LM_ERR("No data for field:'%s'\n", field); goto error; } switch (entry_data.type) { case MMDB_DATA_TYPE_UTF8_STRING: if (entry_data.data_size > RES_BUF_LEN) { LM_ERR("string field to big\n"); goto error; } memcpy(buf, entry_data.utf8_string, entry_data.data_size); len = entry_data.data_size; break; case MMDB_DATA_TYPE_DOUBLE: len = sprintf(buf, "%f", entry_data.double_value); break; case MMDB_DATA_TYPE_BYTES: if (2*entry_data.data_size > RES_BUF_LEN) { LM_ERR("byte field to big\n"); goto error; } for (i = 0; i < entry_data.data_size; i++) sprintf(buf+2*i, "%x", entry_data.bytes[i]); len = 2*entry_data.data_size; break; case MMDB_DATA_TYPE_UINT16: len = sprintf(buf, "%hu", entry_data.uint16); break; case MMDB_DATA_TYPE_UINT32: len = sprintf(buf, "%u", entry_data.uint32); break; case MMDB_DATA_TYPE_INT32: len = sprintf(buf, "%d", entry_data.int32); break; case MMDB_DATA_TYPE_UINT64: len = sprintf(buf, "%lu", entry_data.uint64); break; case MMDB_DATA_TYPE_BOOLEAN: if (entry_data.boolean) len = sprintf(buf, "true"); else len = sprintf(buf, "false"); break; case MMDB_DATA_TYPE_FLOAT: len = sprintf(buf, "%f", entry_data.float_value); break; default: LM_ERR("Unsupported data type for field: '%s'\n", field); goto error; } free_csv_record(path_list); return len; error: free_csv_record(path_list); return -1; }
CAMLprim value mmdb_ml_lookup_path(value ip, value query_list, value mmdb) { CAMLparam3(ip, query_list, mmdb); CAMLlocal3(iter_count, caml_clean_result, query_r); int total_len = 0, copy_count = 0, gai_error = 0, mmdb_error = 0; char *clean_result; long int int_result; iter_count = query_list; unsigned int len = caml_string_length(ip); char *as_string = caml_strdup(String_val(ip)); if (strlen(as_string) != (size_t)len) { caml_failwith("Could not copy IP address properly"); } MMDB_s *as_mmdb = (MMDB_s*)Data_custom_val(mmdb); MMDB_lookup_result_s *result = caml_stat_alloc(sizeof(*result)); *result = MMDB_lookup_string(as_mmdb, as_string, &gai_error, &mmdb_error); check_error(gai_error, mmdb_error); caml_stat_free(as_string); while (iter_count != Val_emptylist) { total_len++; iter_count = Field(iter_count, 1); } char **query = caml_stat_alloc(sizeof(char *) * (total_len + 1)); while (query_list != Val_emptylist) { query[copy_count] = caml_strdup(String_val(Field(query_list, 0))); copy_count++; query_list = Field(query_list, 1); } query[total_len] = NULL; MMDB_entry_data_s entry_data; int status = MMDB_aget_value(&result->entry, &entry_data, (const char *const *const)query); check_status(status); check_data(entry_data); caml_stat_free(result); for (int i = 0; i < copy_count; caml_stat_free(query[i]), i++); caml_stat_free(query); query_r = caml_alloc(2, 0); as_mmdb = NULL; switch (entry_data.type) { case MMDB_DATA_TYPE_BYTES: clean_result = caml_stat_alloc(entry_data.data_size + 1); memcpy(clean_result, entry_data.bytes, entry_data.data_size); caml_clean_result = caml_copy_string(clean_result); caml_stat_free(clean_result); goto string_finish; case MMDB_DATA_TYPE_UTF8_STRING: clean_result = strndup(entry_data.utf8_string, entry_data.data_size); caml_clean_result = caml_copy_string(clean_result); free(clean_result); goto string_finish; case MMDB_DATA_TYPE_FLOAT: Store_field(query_r, 0, polymorphic_variants.poly_float); Store_field(query_r, 1, caml_copy_double(entry_data.float_value)); goto finish; case MMDB_DATA_TYPE_BOOLEAN: Store_field(query_r, 0, polymorphic_variants.poly_bool); Store_field(query_r, 1, Val_true ? entry_data.boolean : Val_false); goto finish; case MMDB_DATA_TYPE_DOUBLE: Store_field(query_r, 0, polymorphic_variants.poly_float); Store_field(query_r, 1, caml_copy_double(entry_data.double_value)); goto finish; case MMDB_DATA_TYPE_UINT16: Store_field(query_r, 0, polymorphic_variants.poly_int); int_result = Val_long(entry_data.uint16); goto int_finish; case MMDB_DATA_TYPE_UINT32: Store_field(query_r, 0, polymorphic_variants.poly_int); int_result = Val_long(entry_data.uint32); goto int_finish; case MMDB_DATA_TYPE_UINT64: Store_field(query_r, 0, polymorphic_variants.poly_int); int_result = Val_long(entry_data.uint32); goto int_finish; // look at /usr/bin/sed -n 1380,1430p src/maxminddb.c case MMDB_DATA_TYPE_ARRAY: case MMDB_DATA_TYPE_MAP: caml_failwith("Can't return a Map or Array yet"); } string_finish: Store_field(query_r, 0, polymorphic_variants.poly_string); Store_field(query_r, 1, caml_clean_result); CAMLreturn(query_r); int_finish: Store_field(query_r, 1, int_result); CAMLreturn(query_r); finish: CAMLreturn(query_r); }
static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_geoip2_ctx_t *geoip2 = (ngx_http_geoip2_ctx_t *) data; ngx_http_geoip2_db_t *database = geoip2->database; int mmdb_error; MMDB_entry_data_s entry_data; ngx_http_geoip2_conf_t *gcf; ngx_addr_t addr; ngx_array_t *xfwd; u_char *p; #if (NGX_HAVE_INET6) uint8_t address[16], *addressp = address; #else unsigned long address; #endif gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip2_module); addr.sockaddr = r->connection->sockaddr; addr.socklen = r->connection->socklen; xfwd = &r->headers_in.x_forwarded_for; if (xfwd->nelts > 0 && gcf->proxies != NULL) { (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, gcf->proxies, gcf->proxy_recursive); } switch (addr.sockaddr->sa_family) { case AF_INET: #if (NGX_HAVE_INET6) ngx_memset(addressp, 0, 12); ngx_memcpy(addressp + 12, &((struct sockaddr_in *) addr.sockaddr)->sin_addr.s_addr, 4); break; case AF_INET6: ngx_memcpy(addressp, &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr.s6_addr, 16); #else address = ((struct sockaddr_in *)addr.sockaddr)->sin_addr.s_addr; #endif break; default: goto not_found; } #if (NGX_HAVE_INET6) if (ngx_memcmp(&address, &database->address, sizeof(address)) != 0) { #else if (address != database->address) { #endif memcpy(&database->address, &address, sizeof(address)); database->result = MMDB_lookup_sockaddr(&database->mmdb, addr.sockaddr, &mmdb_error); if (mmdb_error != MMDB_SUCCESS) { goto not_found; } } if (!database->result.found_entry || MMDB_aget_value(&database->result.entry, &entry_data, geoip2->lookup) != MMDB_SUCCESS) { goto not_found; } if (!entry_data.has_data) { goto not_found; } switch (entry_data.type) { case MMDB_DATA_TYPE_UTF8_STRING: v->data = (u_char *) entry_data.utf8_string; v->len = entry_data.data_size; break; case MMDB_DATA_TYPE_UINT32: p = ngx_palloc(r->pool, NGX_OFF_T_LEN); if (p == NULL) { return NGX_ERROR; } v->len = ngx_sprintf(p, "%O", entry_data.uint32) - p; v->data = p; break; default: goto not_found; } v->valid = 1; v->no_cacheable = 0; v->not_found = 0; return NGX_OK; not_found: if (geoip2->default_value.len > 0) { v->data = geoip2->default_value.data; v->len = geoip2->default_value.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; } else { v->not_found = 1; } return NGX_OK; } static void * ngx_http_geoip2_create_conf(ngx_conf_t *cf) { ngx_pool_cleanup_t *cln; ngx_http_geoip2_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_conf_t)); if (conf == NULL) { return NULL; } conf->proxy_recursive = NGX_CONF_UNSET; cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NULL; } cln->handler = ngx_http_geoip2_cleanup; cln->data = conf; return conf; }
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); }
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); }