From dccd08f11ea17a76da363613e31c3a7add9dd193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8F=AD=E5=B0=BC=E7=89=9B?= <863647842@qq.com> Date: Mon, 25 May 2026 18:44:36 +0800 Subject: [PATCH 1/2] Geo: validate HTTP binary range bases before relocation. Malformed HTTP geo range base files were previously walked before the CRC check completed. During that pass, offsets from the file were converted to process pointers before target records were validated. Validate the binary layout first: file type and size, value records, range table offsets, range arrays, ordering, and CRC. Only then relocate offsets to pointers. Invalid bases now fall back to the text include path without out-of-bounds reads. --- src/http/modules/ngx_http_geo_module.c | 362 ++++++++++++++++++++++--- 1 file changed, 320 insertions(+), 42 deletions(-) diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c index 5018e1abb..868e3e777 100644 --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -106,6 +106,13 @@ static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name); static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name); +static ngx_int_t ngx_http_geo_validate_binary_base(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name, u_char *base, size_t size, + uint32_t *crc32, ngx_http_geo_range_t ***ranges); +static void ngx_http_geo_relocate_binary_base(u_char *base, size_t size, + ngx_http_geo_range_t **ranges); +static ngx_int_t ngx_http_geo_binary_search_offset(ngx_array_t *offsets, + uintptr_t offset); static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx); static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); @@ -1420,18 +1427,17 @@ ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name) { u_char *base, ch; + off_t file_size; time_t mtime; - size_t size, len; + size_t size; ssize_t n; uint32_t crc32; ngx_err_t err; ngx_int_t rc; - ngx_uint_t i; ngx_file_t file; ngx_file_info_t fi; - ngx_http_geo_range_t *range, **ranges; + ngx_http_geo_range_t **ranges; ngx_http_geo_header_t *header; - ngx_http_variable_value_t *vv; ngx_memzero(&file, sizeof(ngx_file_t)); file.name = *name; @@ -1470,13 +1476,31 @@ ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, goto failed; } - size = (size_t) ngx_file_size(&fi); + if (!ngx_is_file(&fi)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "binary geo range base \"%s\" is not a file", + name->data); + goto failed; + } + + file_size = ngx_file_size(&fi); + + if (file_size < (off_t) sizeof(ngx_http_geo_header_t) + || file_size > (off_t) NGX_MAX_SIZE_T_VALUE) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "incompatible binary geo range base \"%s\"", name->data); + goto failed; + } + + size = (size_t) file_size; mtime = ngx_file_mtime(&fi); ch = name->data[name->len - 4]; name->data[name->len - 4] = '\0'; if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { + name->data[name->len - 4] = ch; ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, ngx_file_info_n " \"%s\" failed", name->data); goto failed; @@ -1512,58 +1536,33 @@ ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, header = (ngx_http_geo_header_t *) base; - if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) { + if (ngx_memcmp(&ngx_http_geo_header, header, + sizeof(ngx_http_geo_header_t) - sizeof(uint32_t)) + != 0) + { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "incompatible binary geo range base \"%s\"", name->data); goto failed; } - ngx_crc32_init(crc32); - - vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t)); - - while (vv->data) { - len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len, - sizeof(void *)); - ngx_crc32_update(&crc32, (u_char *) vv, len); - vv->data += (size_t) base; - vv = (ngx_http_variable_value_t *) ((u_char *) vv + len); - } - ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t)); - vv++; - - ranges = (ngx_http_geo_range_t **) vv; - - for (i = 0; i < 0x10000; i++) { - ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *)); - if (ranges[i]) { - ranges[i] = (ngx_http_geo_range_t *) - ((u_char *) ranges[i] + (size_t) base); - } + rc = ngx_http_geo_validate_binary_base(cf, ctx, name, base, size, &crc32, + &ranges); + if (rc == NGX_ERROR) { + goto done; } - range = (ngx_http_geo_range_t *) &ranges[0x10000]; - - while ((u_char *) range < base + size) { - while (range->value) { - ngx_crc32_update(&crc32, (u_char *) range, - sizeof(ngx_http_geo_range_t)); - range->value = (ngx_http_variable_value_t *) - ((u_char *) range->value + (size_t) base); - range++; - } - ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *)); - range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *)); + if (rc != NGX_OK) { + goto failed; } - ngx_crc32_final(crc32); - if (crc32 != header->crc32) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "CRC32 mismatch in binary geo range base \"%s\"", name->data); goto failed; } + ngx_http_geo_relocate_binary_base(base, size, ranges); + ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, "using binary geo range base \"%s\"", name->data); @@ -1589,6 +1588,285 @@ done: } +static ngx_int_t +ngx_http_geo_validate_binary_base(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name, u_char *base, size_t size, + uint32_t *crc32, ngx_http_geo_range_t ***ranges) +{ + u_char *last, *p, *next, *range_base; + size_t len, table_size; + uintptr_t data, offset; + uintptr_t *entry; + ngx_uint_t i, n; + u_short previous; + ngx_array_t *values, *range_starts; + ngx_http_geo_range_t *range, **rs; + ngx_http_variable_value_t *vv; + + last = base + size; + p = base + sizeof(ngx_http_geo_header_t); + + values = ngx_array_create(ctx->temp_pool, 16, sizeof(uintptr_t)); + if (values == NULL) { + return NGX_ERROR; + } + + range_starts = ngx_array_create(ctx->temp_pool, 1024, + sizeof(uintptr_t)); + if (range_starts == NULL) { + return NGX_ERROR; + } + + ngx_crc32_init(*crc32); + + for ( ;; ) { + if ((uintptr_t) p % sizeof(void *) != 0 + || (size_t) (last - p) < sizeof(ngx_http_variable_value_t)) + { + goto invalid; + } + + vv = (ngx_http_variable_value_t *) p; + + if (vv->data == NULL) { + if (vv->len || vv->valid || vv->no_cacheable + || vv->not_found || vv->escape) + { + goto invalid; + } + + ngx_crc32_update(crc32, p, sizeof(ngx_http_variable_value_t)); + p += sizeof(ngx_http_variable_value_t); + break; + } + + if (vv->valid != 1 || vv->no_cacheable || vv->not_found + || vv->escape) + { + goto invalid; + } + + if (vv->len > (size_t) (last - p) + - sizeof(ngx_http_variable_value_t)) + { + goto invalid; + } + + data = (uintptr_t) vv->data; + offset = (uintptr_t) (p + sizeof(ngx_http_variable_value_t) - base); + + if (data != offset) { + goto invalid; + } + + next = ngx_align_ptr(p + sizeof(ngx_http_variable_value_t) + vv->len, + sizeof(void *)); + if (next > last) { + goto invalid; + } + + entry = ngx_array_push(values); + if (entry == NULL) { + return NGX_ERROR; + } + + *entry = (uintptr_t) (p - base); + + len = (size_t) (next - p); + ngx_crc32_update(crc32, p, len); + + p = next; + } + + table_size = 0x10000 * sizeof(ngx_http_geo_range_t *); + + if ((uintptr_t) p % sizeof(void *) != 0 + || (size_t) (last - p) < table_size) + { + goto invalid; + } + + rs = (ngx_http_geo_range_t **) p; + *ranges = rs; + range_base = p + table_size; + + for (i = 0; i < 0x10000; i++) { + ngx_crc32_update(crc32, (u_char *) &rs[i], sizeof(void *)); + + offset = (uintptr_t) rs[i]; + + if (offset == 0) { + continue; + } + + if (offset < (uintptr_t) (range_base - base) + || offset > size - sizeof(void *) + || offset % sizeof(void *) != 0) + { + goto invalid; + } + } + + p = range_base; + + while (p < last) { + if ((uintptr_t) p % sizeof(void *) != 0) { + goto invalid; + } + + entry = ngx_array_push(range_starts); + if (entry == NULL) { + return NGX_ERROR; + } + + *entry = (uintptr_t) (p - base); + + n = 0; + previous = 0; + + for ( ;; ) { + if ((size_t) (last - p) < sizeof(void *)) { + goto invalid; + } + + range = (ngx_http_geo_range_t *) p; + + if (range->value == NULL) { + if (n == 0) { + goto invalid; + } + + ngx_crc32_update(crc32, p, sizeof(void *)); + p += sizeof(void *); + break; + } + + if ((size_t) (last - p) < sizeof(ngx_http_geo_range_t)) { + goto invalid; + } + + offset = (uintptr_t) range->value; + + if (ngx_http_geo_binary_search_offset(values, offset) != NGX_OK) { + goto invalid; + } + + if (range->start > range->end + || (n != 0 && range->start <= previous)) + { + goto invalid; + } + + previous = range->end; + + ngx_crc32_update(crc32, p, sizeof(ngx_http_geo_range_t)); + p += sizeof(ngx_http_geo_range_t); + n++; + } + } + + if (p != last) { + goto invalid; + } + + for (i = 0; i < 0x10000; i++) { + offset = (uintptr_t) rs[i]; + + if (offset == 0) { + continue; + } + + if (ngx_http_geo_binary_search_offset(range_starts, offset) + != NGX_OK) + { + goto invalid; + } + } + + ngx_crc32_final(*crc32); + + return NGX_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "invalid binary geo range base \"%s\"", name->data); + + return NGX_DECLINED; +} + + +static void +ngx_http_geo_relocate_binary_base(u_char *base, size_t size, + ngx_http_geo_range_t **ranges) +{ + u_char *last; + size_t len; + ngx_uint_t i; + ngx_http_geo_range_t *range; + ngx_http_variable_value_t *vv; + + last = base + size; + + vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t)); + + while (vv->data) { + len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len, + sizeof(void *)); + vv->data = (u_char *) ((uintptr_t) vv->data + (uintptr_t) base); + vv = (ngx_http_variable_value_t *) ((u_char *) vv + len); + } + + for (i = 0; i < 0x10000; i++) { + if (ranges[i]) { + ranges[i] = (ngx_http_geo_range_t *) + ((uintptr_t) ranges[i] + (uintptr_t) base); + } + } + + range = (ngx_http_geo_range_t *) &ranges[0x10000]; + + while ((u_char *) range < last) { + while (range->value) { + range->value = (ngx_http_variable_value_t *) + ((uintptr_t) range->value + (uintptr_t) base); + range++; + } + + range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *)); + } +} + + +static ngx_int_t +ngx_http_geo_binary_search_offset(ngx_array_t *offsets, uintptr_t offset) +{ + ngx_uint_t i, left, right; + uintptr_t *elts; + + elts = offsets->elts; + left = 0; + right = offsets->nelts; + + while (left < right) { + i = left + (right - left) / 2; + + if (offset == elts[i]) { + return NGX_OK; + } + + if (offset < elts[i]) { + right = i; + + } else { + left = i + 1; + } + } + + return NGX_DECLINED; +} + + static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx) { From 773aeeadafaf09d0e7c2595bd1614064ef7a101d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8F=AD=E5=B0=BC=E7=89=9B?= <863647842@qq.com> Date: Mon, 25 May 2026 19:02:39 +0800 Subject: [PATCH 2/2] Stream: validate geo binary range bases before relocation. Malformed stream geo range base files were previously walked before the CRC check completed. During that pass, offsets from the file were converted to process pointers before target records were validated. Validate the binary layout first: file type and size, value records, range table offsets, range arrays, ordering, and CRC. Only then relocate offsets to pointers. Invalid bases now fall back to the text include path without out-of-bounds reads. --- src/stream/ngx_stream_geo_module.c | 367 +++++++++++++++++++++++++---- 1 file changed, 323 insertions(+), 44 deletions(-) diff --git a/src/stream/ngx_stream_geo_module.c b/src/stream/ngx_stream_geo_module.c index 316de9286..64995652b 100644 --- a/src/stream/ngx_stream_geo_module.c +++ b/src/stream/ngx_stream_geo_module.c @@ -100,6 +100,13 @@ static char *ngx_stream_geo_include(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name); static ngx_int_t ngx_stream_geo_include_binary_base(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name); +static ngx_int_t ngx_stream_geo_validate_binary_base(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name, u_char *base, + size_t size, uint32_t *crc32, ngx_stream_geo_range_t ***ranges); +static void ngx_stream_geo_relocate_binary_base(u_char *base, size_t size, + ngx_stream_geo_range_t **ranges); +static ngx_int_t ngx_stream_geo_binary_search_offset(ngx_array_t *offsets, + uintptr_t offset); static void ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx); static u_char *ngx_stream_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); @@ -1346,18 +1353,17 @@ ngx_stream_geo_include_binary_base(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name) { u_char *base, ch; + off_t file_size; time_t mtime; - size_t size, len; + size_t size; ssize_t n; uint32_t crc32; ngx_err_t err; ngx_int_t rc; - ngx_uint_t i; ngx_file_t file; ngx_file_info_t fi; - ngx_stream_geo_range_t *range, **ranges; + ngx_stream_geo_range_t **ranges; ngx_stream_geo_header_t *header; - ngx_stream_variable_value_t *vv; ngx_memzero(&file, sizeof(ngx_file_t)); file.name = *name; @@ -1396,13 +1402,31 @@ ngx_stream_geo_include_binary_base(ngx_conf_t *cf, goto failed; } - size = (size_t) ngx_file_size(&fi); + if (!ngx_is_file(&fi)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "binary geo range base \"%s\" is not a file", + name->data); + goto failed; + } + + file_size = ngx_file_size(&fi); + + if (file_size < (off_t) sizeof(ngx_stream_geo_header_t) + || file_size > (off_t) NGX_MAX_SIZE_T_VALUE) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "incompatible binary geo range base \"%s\"", name->data); + goto failed; + } + + size = (size_t) file_size; mtime = ngx_file_mtime(&fi); ch = name->data[name->len - 4]; name->data[name->len - 4] = '\0'; if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { + name->data[name->len - 4] = ch; ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, ngx_file_info_n " \"%s\" failed", name->data); goto failed; @@ -1438,60 +1462,33 @@ ngx_stream_geo_include_binary_base(ngx_conf_t *cf, header = (ngx_stream_geo_header_t *) base; - if (size < 16 || ngx_memcmp(&ngx_stream_geo_header, header, 12) != 0) { + if (ngx_memcmp(&ngx_stream_geo_header, header, + sizeof(ngx_stream_geo_header_t) - sizeof(uint32_t)) + != 0) + { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "incompatible binary geo range base \"%s\"", name->data); goto failed; } - ngx_crc32_init(crc32); - - vv = (ngx_stream_variable_value_t *) - (base + sizeof(ngx_stream_geo_header_t)); - - while (vv->data) { - len = ngx_align(sizeof(ngx_stream_variable_value_t) + vv->len, - sizeof(void *)); - ngx_crc32_update(&crc32, (u_char *) vv, len); - vv->data += (size_t) base; - vv = (ngx_stream_variable_value_t *) ((u_char *) vv + len); - } - ngx_crc32_update(&crc32, (u_char *) vv, - sizeof(ngx_stream_variable_value_t)); - vv++; - - ranges = (ngx_stream_geo_range_t **) vv; - - for (i = 0; i < 0x10000; i++) { - ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *)); - if (ranges[i]) { - ranges[i] = (ngx_stream_geo_range_t *) - ((u_char *) ranges[i] + (size_t) base); - } + rc = ngx_stream_geo_validate_binary_base(cf, ctx, name, base, size, &crc32, + &ranges); + if (rc == NGX_ERROR) { + goto done; } - range = (ngx_stream_geo_range_t *) &ranges[0x10000]; - - while ((u_char *) range < base + size) { - while (range->value) { - ngx_crc32_update(&crc32, (u_char *) range, - sizeof(ngx_stream_geo_range_t)); - range->value = (ngx_stream_variable_value_t *) - ((u_char *) range->value + (size_t) base); - range++; - } - ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *)); - range = (ngx_stream_geo_range_t *) ((u_char *) range + sizeof(void *)); + if (rc != NGX_OK) { + goto failed; } - ngx_crc32_final(crc32); - if (crc32 != header->crc32) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "CRC32 mismatch in binary geo range base \"%s\"", name->data); goto failed; } + ngx_stream_geo_relocate_binary_base(base, size, ranges); + ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, "using binary geo range base \"%s\"", name->data); @@ -1517,6 +1514,288 @@ done: } +static ngx_int_t +ngx_stream_geo_validate_binary_base(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name, u_char *base, + size_t size, uint32_t *crc32, ngx_stream_geo_range_t ***ranges) +{ + u_char *last, *p, *next, *range_base; + size_t len, table_size; + uintptr_t data, offset; + uintptr_t *entry; + ngx_uint_t i, n; + u_short previous; + ngx_array_t *values, *range_starts; + ngx_stream_geo_range_t *range, **rs; + ngx_stream_variable_value_t *vv; + + last = base + size; + p = base + sizeof(ngx_stream_geo_header_t); + + values = ngx_array_create(ctx->temp_pool, 16, sizeof(uintptr_t)); + if (values == NULL) { + return NGX_ERROR; + } + + range_starts = ngx_array_create(ctx->temp_pool, 1024, + sizeof(uintptr_t)); + if (range_starts == NULL) { + return NGX_ERROR; + } + + ngx_crc32_init(*crc32); + + for ( ;; ) { + if ((uintptr_t) p % sizeof(void *) != 0 + || (size_t) (last - p) < sizeof(ngx_stream_variable_value_t)) + { + goto invalid; + } + + vv = (ngx_stream_variable_value_t *) p; + + if (vv->data == NULL) { + if (vv->len || vv->valid || vv->no_cacheable + || vv->not_found || vv->escape) + { + goto invalid; + } + + ngx_crc32_update(crc32, p, sizeof(ngx_stream_variable_value_t)); + p += sizeof(ngx_stream_variable_value_t); + break; + } + + if (vv->valid != 1 || vv->no_cacheable || vv->not_found + || vv->escape) + { + goto invalid; + } + + if (vv->len > (size_t) (last - p) + - sizeof(ngx_stream_variable_value_t)) + { + goto invalid; + } + + data = (uintptr_t) vv->data; + offset = (uintptr_t) (p + sizeof(ngx_stream_variable_value_t) - base); + + if (data != offset) { + goto invalid; + } + + next = ngx_align_ptr(p + sizeof(ngx_stream_variable_value_t) + vv->len, + sizeof(void *)); + if (next > last) { + goto invalid; + } + + entry = ngx_array_push(values); + if (entry == NULL) { + return NGX_ERROR; + } + + *entry = (uintptr_t) (p - base); + + len = (size_t) (next - p); + ngx_crc32_update(crc32, p, len); + + p = next; + } + + table_size = 0x10000 * sizeof(ngx_stream_geo_range_t *); + + if ((uintptr_t) p % sizeof(void *) != 0 + || (size_t) (last - p) < table_size) + { + goto invalid; + } + + rs = (ngx_stream_geo_range_t **) p; + *ranges = rs; + range_base = p + table_size; + + for (i = 0; i < 0x10000; i++) { + ngx_crc32_update(crc32, (u_char *) &rs[i], sizeof(void *)); + + offset = (uintptr_t) rs[i]; + + if (offset == 0) { + continue; + } + + if (offset < (uintptr_t) (range_base - base) + || offset > size - sizeof(void *) + || offset % sizeof(void *) != 0) + { + goto invalid; + } + } + + p = range_base; + + while (p < last) { + if ((uintptr_t) p % sizeof(void *) != 0) { + goto invalid; + } + + entry = ngx_array_push(range_starts); + if (entry == NULL) { + return NGX_ERROR; + } + + *entry = (uintptr_t) (p - base); + + n = 0; + previous = 0; + + for ( ;; ) { + if ((size_t) (last - p) < sizeof(void *)) { + goto invalid; + } + + range = (ngx_stream_geo_range_t *) p; + + if (range->value == NULL) { + if (n == 0) { + goto invalid; + } + + ngx_crc32_update(crc32, p, sizeof(void *)); + p += sizeof(void *); + break; + } + + if ((size_t) (last - p) < sizeof(ngx_stream_geo_range_t)) { + goto invalid; + } + + offset = (uintptr_t) range->value; + + if (ngx_stream_geo_binary_search_offset(values, offset) + != NGX_OK) + { + goto invalid; + } + + if (range->start > range->end + || (n != 0 && range->start <= previous)) + { + goto invalid; + } + + previous = range->end; + + ngx_crc32_update(crc32, p, sizeof(ngx_stream_geo_range_t)); + p += sizeof(ngx_stream_geo_range_t); + n++; + } + } + + if (p != last) { + goto invalid; + } + + for (i = 0; i < 0x10000; i++) { + offset = (uintptr_t) rs[i]; + + if (offset == 0) { + continue; + } + + if (ngx_stream_geo_binary_search_offset(range_starts, offset) + != NGX_OK) + { + goto invalid; + } + } + + ngx_crc32_final(*crc32); + + return NGX_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "invalid binary geo range base \"%s\"", name->data); + + return NGX_DECLINED; +} + + +static void +ngx_stream_geo_relocate_binary_base(u_char *base, size_t size, + ngx_stream_geo_range_t **ranges) +{ + u_char *last; + size_t len; + ngx_uint_t i; + ngx_stream_geo_range_t *range; + ngx_stream_variable_value_t *vv; + + last = base + size; + + vv = (ngx_stream_variable_value_t *) + (base + sizeof(ngx_stream_geo_header_t)); + + while (vv->data) { + len = ngx_align(sizeof(ngx_stream_variable_value_t) + vv->len, + sizeof(void *)); + vv->data = (u_char *) ((uintptr_t) vv->data + (uintptr_t) base); + vv = (ngx_stream_variable_value_t *) ((u_char *) vv + len); + } + + for (i = 0; i < 0x10000; i++) { + if (ranges[i]) { + ranges[i] = (ngx_stream_geo_range_t *) + ((uintptr_t) ranges[i] + (uintptr_t) base); + } + } + + range = (ngx_stream_geo_range_t *) &ranges[0x10000]; + + while ((u_char *) range < last) { + while (range->value) { + range->value = (ngx_stream_variable_value_t *) + ((uintptr_t) range->value + (uintptr_t) base); + range++; + } + + range = (ngx_stream_geo_range_t *) ((u_char *) range + sizeof(void *)); + } +} + + +static ngx_int_t +ngx_stream_geo_binary_search_offset(ngx_array_t *offsets, uintptr_t offset) +{ + ngx_uint_t i, left, right; + uintptr_t *elts; + + elts = offsets->elts; + left = 0; + right = offsets->nelts; + + while (left < right) { + i = left + (right - left) / 2; + + if (offset == elts[i]) { + return NGX_OK; + } + + if (offset < elts[i]) { + right = i; + + } else { + left = i + 1; + } + } + + return NGX_DECLINED; +} + + static void ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx) {