488
518
ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
489
519
ngx_str_t *value)
492
in_addr_t start, end;
495
ngx_http_variable_value_t *old;
522
in_addr_t start, end;
497
526
if (ngx_strcmp(value[0].data, "default") == 0) {
499
old = ctx->high->default_value;
501
ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
502
if (ctx->high->default_value == NULL) {
503
return NGX_CONF_ERROR;
528
if (ctx->high.default_value) {
507
529
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
508
"duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
509
&value[0], ctx->high->default_value, old);
530
"duplicate default geo range value: \"%V\", old value: \"%v\"",
531
&value[1], ctx->high.default_value);
534
ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
535
if (ctx->high.default_value == NULL) {
536
return NGX_CONF_ERROR;
512
539
return NGX_CONF_OK;
542
if (ctx->binary_include) {
543
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
544
"binary geo range base \"%s\" may not be mixed with usual entries",
545
ctx->include_name.data);
546
return NGX_CONF_ERROR;
549
if (ctx->high.low == NULL) {
550
ctx->high.low = ngx_pcalloc(ctx->pool,
551
0x10000 * sizeof(ngx_http_geo_range_t *));
552
if (ctx->high.low == NULL) {
553
return NGX_CONF_ERROR;
558
ctx->outside_entries = 1;
515
560
if (ngx_strcmp(value[0].data, "delete") == 0) {
1088
ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1094
file.len = name->len + 4;
1095
file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
1096
if (file.data == NULL) {
1097
return NGX_CONF_ERROR;
1100
ngx_sprintf(file.data, "%V.bin%Z", name);
1102
if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
1103
return NGX_CONF_ERROR;
1107
ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1109
switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
1113
return NGX_CONF_ERROR;
1120
file.data[file.len] = '\0';
1122
ctx->include_name = file;
1124
if (ctx->outside_entries) {
1125
ctx->allow_binary_include = 0;
1128
ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
1130
rv = ngx_conf_parse(cf, &file);
1133
ctx->outside_entries = 0;
1140
ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
1153
ngx_http_geo_range_t *range, **ranges;
1154
ngx_http_geo_header_t *header;
1155
ngx_http_variable_value_t *vv;
1157
ngx_memzero(&file, sizeof(ngx_file_t));
1161
file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
1162
if (file.fd == NGX_INVALID_FILE) {
1164
if (err != NGX_ENOENT) {
1165
ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
1166
ngx_open_file_n " \"%s\" failed", name->data);
1168
return NGX_DECLINED;
1171
if (ctx->outside_entries) {
1172
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1173
"binary geo range base \"%s\" may not be mixed with usual entries",
1179
if (ctx->binary_include) {
1180
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1181
"second binary geo range base \"%s\" may not be mixed with \"%s\"",
1182
name->data, ctx->include_name.data);
1187
if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
1188
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1189
ngx_fd_info_n " \"%s\" failed", name->data);
1193
size = (size_t) ngx_file_size(&fi);
1194
mtime = ngx_file_mtime(&fi);
1196
ch = name->data[name->len - 4];
1197
name->data[name->len - 4] = '\0';
1199
if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
1200
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1201
ngx_file_info_n " \"%s\" failed", name->data);
1205
name->data[name->len - 4] = ch;
1207
if (mtime < ngx_file_mtime(&fi)) {
1208
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1209
"stale binary geo range base \"%s\"", name->data);
1213
base = ngx_palloc(ctx->pool, size);
1218
n = ngx_read_file(&file, base, size, 0);
1220
if (n == NGX_ERROR) {
1221
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
1222
ngx_read_file_n " \"%s\" failed", name->data);
1226
if ((size_t) n != size) {
1227
ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
1228
ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
1229
name->data, n, size);
1233
header = (ngx_http_geo_header_t *) base;
1235
if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
1236
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1237
"incompatible binary geo range base \"%s\"", name->data);
1241
ngx_crc32_init(crc32);
1243
vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
1246
len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
1248
ngx_crc32_update(&crc32, (u_char *) vv, len);
1249
vv->data += (size_t) base;
1250
vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
1252
ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
1255
ranges = (ngx_http_geo_range_t **) vv;
1257
for (i = 0; i < 0x10000; i++) {
1258
ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
1260
ranges[i] = (ngx_http_geo_range_t *)
1261
((u_char *) ranges[i] + (size_t) base);
1265
range = (ngx_http_geo_range_t *) &ranges[0x10000];
1267
while ((u_char *) range < base + size) {
1268
while (range->value) {
1269
ngx_crc32_update(&crc32, (u_char *) range,
1270
sizeof(ngx_http_geo_range_t));
1271
range->value = (ngx_http_variable_value_t *)
1272
((u_char *) range->value + (size_t) base);
1275
ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
1276
range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
1279
ngx_crc32_final(crc32);
1281
if (crc32 != header->crc32) {
1282
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1283
"CRC32 mismatch in binary geo range base \"%s\"", name->data);
1287
ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
1288
"using binary geo range base \"%s\"", name->data);
1290
ctx->include_name = *name;
1291
ctx->binary_include = 1;
1292
ctx->high.low = ranges;
1303
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
1304
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
1305
ngx_close_file_n " \"%s\" failed", name->data);
1313
ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
1319
ngx_file_mapping_t fm;
1320
ngx_http_geo_range_t *r, *range, **ranges;
1321
ngx_http_geo_header_t *header;
1322
ngx_http_geo_variable_value_node_t *gvvn;
1324
fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
1325
if (fm.name == NULL) {
1329
ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
1331
fm.size = ctx->data_size;
1332
fm.log = ctx->pool->log;
1334
ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
1335
"creating binary geo range base \"%s\"", fm.name);
1337
if (ngx_create_file_mapping(&fm) != NGX_OK) {
1341
p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
1342
sizeof(ngx_http_geo_header_t));
1344
p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
1345
ctx->rbtree.sentinel);
1347
p += sizeof(ngx_http_variable_value_t);
1349
ranges = (ngx_http_geo_range_t **) p;
1351
p += 0x10000 * sizeof(ngx_http_geo_range_t *);
1353
for (i = 0; i < 0x10000; i++) {
1354
r = ctx->high.low[i];
1359
range = (ngx_http_geo_range_t *) p;
1360
ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
1363
s.len = r->value->len;
1364
s.data = r->value->data;
1365
hash = ngx_crc32_long(s.data, s.len);
1366
gvvn = (ngx_http_geo_variable_value_node_t *)
1367
ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
1369
range->value = (ngx_http_variable_value_t *) gvvn->offset;
1370
range->start = r->start;
1371
range->end = r->end;
1374
} while ((++r)->value);
1376
range->value = NULL;
1378
p = (u_char *) range + sizeof(void *);
1382
header->crc32 = ngx_crc32_long((u_char *) fm.addr
1383
+ sizeof(ngx_http_geo_header_t),
1384
fm.size - sizeof(ngx_http_geo_header_t));
1386
ngx_close_file_mapping(&fm);
1391
ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
1392
ngx_rbtree_node_t *sentinel)
1394
ngx_http_variable_value_t *vv;
1395
ngx_http_geo_variable_value_node_t *gvvn;
1397
if (node == sentinel) {
1401
gvvn = (ngx_http_geo_variable_value_node_t *) node;
1402
gvvn->offset = p - base;
1404
vv = (ngx_http_variable_value_t *) p;
1406
p += sizeof(ngx_http_variable_value_t);
1407
vv->data = (u_char *) (p - base);
1409
p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
1411
p = ngx_align_ptr(p, sizeof(void *));
1413
p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
1415
return ngx_http_geo_copy_values(base, p, node->right, sentinel);