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; if (ip[0] == ':') { hints.ai_flags = ai_flags; #ifdef AI_V4MAPPED 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; if (gai_error == 0) { result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); } freeaddrinfo(addresses); test_lookup_errors(gai_error, mmdb_error, "MMDB_lookup_sockaddr", 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)); } }
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; }
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); }