Mercurial > hg > nginx
view src/http/modules/ngx_http_geo_module.c @ 3440:88741ec7731a stable-0.7
merge r3294, r3305:
Fix a bug introduced in r2032: After a child process has read a terminate
message from a channel, the process tries to read the channel again.
The kernel (at least FreeBSD) may preempt the process and sends a SIGIO
signal to a master process. The master process sends a new terminate message,
the kernel switches again to the the child process, and the child process
reads the messages instead of an EAGAIN error. And this may repeat over
and over. Being that the child process can not exit the cycle and test
the termination flag set by the message handler.
The fix disallow the master process to send a new terminate message on
SIGIO signal reception. It may send the message only on SIGALARM signal.
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Mon, 01 Feb 2010 15:49:36 +0000 |
parents | f678ea405737 |
children | 24e7bf1abbe5 |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> typedef struct { u_short start; u_short end; ngx_http_variable_value_t *value; } ngx_http_geo_range_t; typedef struct { ngx_http_geo_range_t *ranges; ngx_uint_t n; } ngx_http_geo_low_ranges_t; typedef struct { ngx_http_geo_low_ranges_t low[0x10000]; ngx_http_variable_value_t *default_value; } ngx_http_geo_high_ranges_t; typedef struct { ngx_http_variable_value_t *value; ngx_str_t *net; ngx_http_geo_high_ranges_t *high; ngx_radix_tree_t *tree; ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; ngx_array_t *proxies; ngx_pool_t *pool; ngx_pool_t *temp_pool; } ngx_http_geo_conf_ctx_t; typedef struct { union { ngx_radix_tree_t *tree; ngx_http_geo_high_ranges_t *high; } u; ngx_array_t *proxies; ngx_int_t index; } ngx_http_geo_ctx_t; static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx); static in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx); static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); static char *ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value); static char *ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr); static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr); static ngx_command_t ngx_http_geo_commands[] = { { ngx_string("geo"), NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, ngx_http_geo_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_geo_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */ }; ngx_module_t ngx_http_geo_module = { NGX_MODULE_V1, &ngx_http_geo_module_ctx, /* module context */ ngx_http_geo_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; /* AF_INET only */ static ngx_int_t ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; ngx_http_variable_value_t *vv; vv = (ngx_http_variable_value_t *) ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx)); *v = *vv; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo: %v", v); return NGX_OK; } static ngx_int_t ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; in_addr_t addr; ngx_uint_t i, n; ngx_http_geo_range_t *range; *v = *ctx->u.high->default_value; addr = ngx_http_geo_addr(r, ctx); range = ctx->u.high->low[addr >> 16].ranges; n = addr & 0xffff; for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) { if (n >= (ngx_uint_t) range[i].start && n <= (ngx_uint_t) range[i].end) { *v = *range[i].value; } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo: %v", v); return NGX_OK; } static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) { u_char *p, *ip; size_t len; in_addr_t addr; ngx_uint_t i, n; ngx_in_cidr_t *proxies; ngx_table_elt_t *xfwd; addr = ngx_http_geo_real_addr(r, ctx); xfwd = r->headers_in.x_forwarded_for; if (xfwd == NULL || ctx->proxies == NULL) { return addr; } proxies = ctx->proxies->elts; n = ctx->proxies->nelts; for (i = 0; i < n; i++) { if ((addr & proxies[i].mask) == proxies[i].addr) { len = xfwd->value.len; ip = xfwd->value.data; for (p = ip + len - 1; p > ip; p--) { if (*p == ' ' || *p == ',') { p++; len -= p - ip; ip = p; break; } } return ntohl(ngx_inet_addr(ip, len)); } } return addr; } static in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) { struct sockaddr_in *sin; ngx_http_variable_value_t *v; if (ctx->index == -1) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo started: %V", &r->connection->addr_text); if (r->connection->sockaddr->sa_family != AF_INET) { return 0; } sin = (struct sockaddr_in *) r->connection->sockaddr; return ntohl(sin->sin_addr.s_addr); } v = ngx_http_get_flushed_variable(r, ctx->index); if (v == NULL || v->not_found) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo not found"); return 0; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http geo started: %v", v); return ntohl(ngx_inet_addr(v->data, v->len)); } static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; size_t len; ngx_str_t *value, name; ngx_uint_t i; ngx_conf_t save; ngx_pool_t *pool; ngx_array_t *a; ngx_http_variable_t *var; ngx_http_geo_ctx_t *geo; ngx_http_geo_conf_ctx_t ctx; value = cf->args->elts; geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t)); if (geo == NULL) { return NGX_CONF_ERROR; } name = value[1]; name.len--; name.data++; if (cf->args->nelts == 3) { geo->index = ngx_http_get_variable_index(cf, &name); if (geo->index == NGX_ERROR) { return NGX_CONF_ERROR; } name = value[2]; name.len--; name.data++; } else { geo->index = -1; } var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); if (var == NULL) { return NGX_CONF_ERROR; } pool = ngx_create_pool(16384, cf->log); if (pool == NULL) { return NGX_CONF_ERROR; } ctx.temp_pool = ngx_create_pool(16384, cf->log); if (ctx.temp_pool == NULL) { return NGX_CONF_ERROR; } ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_http_variable_value_rbtree_insert); ctx.high = NULL; ctx.tree = NULL; ctx.proxies = NULL; ctx.pool = cf->pool; save = *cf; cf->pool = pool; cf->ctx = &ctx; cf->handler = ngx_http_geo; cf->handler_conf = conf; rv = ngx_conf_parse(cf, NULL); *cf = save; geo->proxies = ctx.proxies; if (ctx.high) { for (i = 0; i < 0x10000; i++) { a = (ngx_array_t *) ctx.high->low[i].ranges; if (a == NULL || a->nelts == 0) { continue; } ctx.high->low[i].n = a->nelts; len = a->nelts * sizeof(ngx_http_geo_range_t); ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); if (ctx.high->low[i].ranges == NULL ){ return NGX_CONF_ERROR; } ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); } geo->u.high = ctx.high; var->get_handler = ngx_http_geo_range_variable; var->data = (uintptr_t) geo; ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(pool); if (ctx.high->default_value == NULL) { ctx.high->default_value = &ngx_http_variable_null_value; } } else { if (ctx.tree == NULL) { ctx.tree = ngx_radix_tree_create(cf->pool, -1); if (ctx.tree == NULL) { return NGX_CONF_ERROR; } } geo->u.tree = ctx.tree; var->get_handler = ngx_http_geo_cidr_variable; var->data = (uintptr_t) geo; ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(pool); if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) { return rv; } if (ngx_radix32tree_insert(ctx.tree, 0, 0, (uintptr_t) &ngx_http_variable_null_value) == NGX_ERROR) { return NGX_CONF_ERROR; } } return rv; } static char * ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { char *rv; ngx_str_t *value, file; ngx_cidr_t cidr; ngx_http_geo_conf_ctx_t *ctx; ctx = cf->ctx; value = cf->args->elts; if (cf->args->nelts == 1) { if (ngx_strcmp(value[0].data, "ranges") == 0) { if (ctx->tree) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"ranges\" directive must be " "the first directive inside \"geo\" block"); goto failed; } ctx->high = ngx_pcalloc(ctx->pool, sizeof(ngx_http_geo_high_ranges_t)); if (ctx->high == NULL) { goto failed; } rv = NGX_CONF_OK; goto done; } } if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of the geo parameters"); goto failed; } if (ngx_strcmp(value[0].data, "include") == 0) { file.len = value[1].len++; file.data = ngx_pstrdup(ctx->temp_pool, &value[1]); if (file.data == NULL) { goto failed; } if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ goto failed; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); rv = ngx_conf_parse(cf, &file); goto done; } else if (ngx_strcmp(value[0].data, "proxy") == 0) { if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) { goto failed; } rv = ngx_http_geo_add_proxy(cf, ctx, &cidr); goto done; } if (ctx->high) { rv = ngx_http_geo_range(cf, ctx, value); } else { rv = ngx_http_geo_cidr(cf, ctx, value); } done: ngx_reset_pool(cf->pool); return rv; failed: ngx_reset_pool(cf->pool); return NGX_CONF_ERROR; } static char * ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value) { u_char *p, *last; in_addr_t start, end; ngx_str_t *net; ngx_uint_t del; ngx_http_variable_value_t *old; if (ngx_strcmp(value[0].data, "default") == 0) { old = ctx->high->default_value; ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); if (ctx->high->default_value == NULL) { return NGX_CONF_ERROR; } if (old) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", &value[0], ctx->high->default_value, old); } return NGX_CONF_OK; } if (ngx_strcmp(value[0].data, "delete") == 0) { net = &value[1]; del = 1; } else { net = &value[0]; del = 0; } last = net->data + net->len; p = ngx_strlchr(net->data, last, '-'); if (p == NULL) { goto invalid; } start = ngx_inet_addr(net->data, p - net->data); if (start == INADDR_NONE) { goto invalid; } start = ntohl(start); p++; end = ngx_inet_addr(p, last - p); if (end == INADDR_NONE) { goto invalid; } end = ntohl(end); if (start > end) { goto invalid; } if (del) { if (ngx_http_geo_delete_range(cf, ctx, start, end)) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "no address range \"%V\" to delete", net); } return NGX_CONF_OK; } ctx->value = ngx_http_geo_value(cf, ctx, &value[1]); if (ctx->value == NULL) { return NGX_CONF_ERROR; } ctx->net = net; return ngx_http_geo_add_range(cf, ctx, start, end); invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net); return NGX_CONF_ERROR; } /* the add procedure is optimized to add a growing up sequence */ static char * ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end) { in_addr_t n; ngx_uint_t h, i, s, e; ngx_array_t *a; ngx_http_geo_range_t *range; for (n = start; n <= end; n += 0x10000) { h = n >> 16; if (n == start) { s = n & 0xffff; } else { s = 0; } if ((n | 0xffff) > end) { e = end & 0xffff; } else { e = 0xffff; } a = (ngx_array_t *) ctx->high->low[h].ranges; if (a == NULL) { a = ngx_array_create(ctx->temp_pool, 64, sizeof(ngx_http_geo_range_t)); if (a == NULL) { return NGX_CONF_ERROR; } ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; } i = a->nelts; range = a->elts; while (i) { i--; if (e < (ngx_uint_t) range[i].start) { continue; } if (s > (ngx_uint_t) range[i].end) { /* add after the range */ range = ngx_array_push(a); if (range == NULL) { return NGX_CONF_ERROR; } range = a->elts; ngx_memcpy(&range[i + 2], &range[i + 1], (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) s; range[i + 1].end = (u_short) e; range[i + 1].value = ctx->value; goto next; } if (s == (ngx_uint_t) range[i].start && e == (ngx_uint_t) range[i].end) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", ctx->net, ctx->value, range[i].value); range[i].value = ctx->value; goto next; } if (s > (ngx_uint_t) range[i].start && e < (ngx_uint_t) range[i].end) { /* split the range and insert the new one */ range = ngx_array_push(a); if (range == NULL) { return NGX_CONF_ERROR; } range = ngx_array_push(a); if (range == NULL) { return NGX_CONF_ERROR; } range = a->elts; ngx_memcpy(&range[i + 3], &range[i + 1], (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t)); range[i + 2].start = (u_short) (e + 1); range[i + 2].end = range[i].end; range[i + 2].value = range[i].value; range[i + 1].start = (u_short) s; range[i + 1].end = (u_short) e; range[i + 1].value = ctx->value; range[i].end = (u_short) (s - 1); goto next; } if (s == (ngx_uint_t) range[i].start && e < (ngx_uint_t) range[i].end) { /* shift the range start and insert the new range */ range = ngx_array_push(a); if (range == NULL) { return NGX_CONF_ERROR; } range = a->elts; ngx_memcpy(&range[i + 1], &range[i], (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) (e + 1); range[i].start = (u_short) s; range[i].end = (u_short) e; range[i].value = ctx->value; goto next; } if (s > (ngx_uint_t) range[i].start && e == (ngx_uint_t) range[i].end) { /* shift the range end and insert the new range */ range = ngx_array_push(a); if (range == NULL) { return NGX_CONF_ERROR; } range = a->elts; ngx_memcpy(&range[i + 2], &range[i + 1], (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) s; range[i + 1].end = (u_short) e; range[i + 1].value = ctx->value; range[i].end = (u_short) (s - 1); goto next; } s = (ngx_uint_t) range[i].start; e = (ngx_uint_t) range[i].end; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"", ctx->net, h >> 8, h & 0xff, s >> 8, s & 0xff, h >> 8, h & 0xff, e >> 8, e & 0xff); return NGX_CONF_ERROR; } /* add the first range */ range = ngx_array_push(a); if (range == NULL) { return NGX_CONF_ERROR; } range->start = (u_short) s; range->end = (u_short) e; range->value = ctx->value; next: continue; } return NGX_CONF_OK; } static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end) { in_addr_t n; ngx_uint_t h, i, s, e, warn; ngx_array_t *a; ngx_http_geo_range_t *range; warn = 0; for (n = start; n <= end; n += 0x10000) { h = n >> 16; if (n == start) { s = n & 0xffff; } else { s = 0; } if ((n | 0xffff) > end) { e = end & 0xffff; } else { e = 0xffff; } a = (ngx_array_t *) ctx->high->low[h].ranges; if (a == NULL) { warn = 1; continue; } range = a->elts; for (i = 0; i < a->nelts; i++) { if (s == (ngx_uint_t) range[i].start && e == (ngx_uint_t) range[i].end) { ngx_memcpy(&range[i], &range[i + 1], (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); a->nelts--; break; } if (s != (ngx_uint_t) range[i].start && e != (ngx_uint_t) range[i].end) { continue; } warn = 1; } } return warn; } static char * ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value) { ngx_int_t rc, del; ngx_str_t *net; ngx_uint_t i; ngx_cidr_t cidr; ngx_http_variable_value_t *val, *old; if (ctx->tree == NULL) { ctx->tree = ngx_radix_tree_create(ctx->pool, -1); if (ctx->tree == NULL) { return NGX_CONF_ERROR; } } if (ngx_strcmp(value[0].data, "default") == 0) { cidr.u.in.addr = 0; cidr.u.in.mask = 0; net = &value[0]; } else { if (ngx_strcmp(value[0].data, "delete") == 0) { net = &value[1]; del = 1; } else { net = &value[0]; del = 0; } if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) { return NGX_CONF_ERROR; } if (del) { if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask) != NGX_OK) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "no network \"%V\" to delete", net); } return NGX_CONF_OK; } } val = ngx_http_geo_value(cf, ctx, &value[1]); if (val == NULL) { return NGX_CONF_ERROR; } for (i = 2; i; i--) { rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, cidr.u.in.mask, (uintptr_t) val); if (rc == NGX_OK) { return NGX_CONF_OK; } if (rc == NGX_ERROR) { return NGX_CONF_ERROR; } /* rc == NGX_BUSY */ old = (ngx_http_variable_value_t *) ngx_radix32tree_find(ctx->tree, cidr.u.in.addr & cidr.u.in.mask); ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", net, val, old); rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); return NGX_CONF_ERROR; } } return NGX_CONF_ERROR; } static ngx_http_variable_value_t * ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value) { uint32_t hash; ngx_http_variable_value_t *val; ngx_http_variable_value_node_t *vvn; hash = ngx_crc32_long(value->data, value->len); val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); if (val) { return val; } val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); if (val == NULL) { return NULL; } val->len = value->len; val->data = ngx_pstrdup(ctx->pool, value); if (val->data == NULL) { return NULL; } val->valid = 1; val->no_cacheable = 0; val->not_found = 0; vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); if (vvn == NULL) { return NULL; } vvn->node.key = hash; vvn->len = val->len; vvn->value = val; ngx_rbtree_insert(&ctx->rbtree, &vvn->node); return val; } static char * ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr) { ngx_in_cidr_t *c; if (ctx->proxies == NULL) { ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_in_cidr_t)); if (ctx->proxies == NULL) { return NGX_CONF_ERROR; } } c = ngx_array_push(ctx->proxies); if (c == NULL) { return NGX_CONF_ERROR; } c->addr = cidr->u.in.addr; c->mask = cidr->u.in.mask; return NGX_CONF_OK; } static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) { ngx_int_t rc; if (ngx_strcmp(net->data, "255.255.255.255") == 0) { cidr->u.in.addr = 0xffffffff; cidr->u.in.mask = 0xffffffff; return NGX_OK; } rc = ngx_ptocidr(net, cidr); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); return NGX_ERROR; } if (cidr->family != AF_INET) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"geo\" supports IPv4 only"); return NGX_ERROR; } if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "low address bits of %V are meaningless", net); } cidr->u.in.addr = ntohl(cidr->u.in.addr); cidr->u.in.mask = ntohl(cidr->u.in.mask); return NGX_OK; }