~britco/nginx/master

« back to all changes in this revision

Viewing changes to src/http/modules/ngx_http_geo_module.c

  • Committer: Bazaar Package Importer
  • Author(s): Kartik Mistry, Kartik Mistry, Michael Lustfield
  • Date: 2010-11-27 21:04:02 UTC
  • mfrom: (1.3.8 upstream)
  • mto: This revision was merged to the branch mainline in revision 36.
  • Revision ID: james.westby@ubuntu.com-20101127210402-14sgjpe6r3jup8a9
Tags: 0.8.53-1
[Kartik Mistry]
* debian/control:
  + Added Michael Lustfield as co-maintainer
* nginx.conf:
  + No need to use regex in gzip_disable for msie6, Thanks to António P. P.
    Almeida <appa@perusio.net> (Closes: #592147)
* conf/sites-available/default:
  + Fixed typo for "include fastcgi", Thanks to Mostafa Ghadamyari
    <nginx@gigfa.com> (Closes: #593142, #593143)
* debian/patches/fix_reloading_ipv6.diff:
  + Removed, merged upstream
* debian/init.d:
  + Added fix to control nginx by user in a simple way by setting DAEMON
    variable to an invalid name in /etc/default/nginx. Patch by Toni Mueller
    <support@oeko.net> (Closes: #594598)
* debian/NEWS.Debian:
  + Updated news for 0.8.x as stable branch

[Michael Lustfield]
* New upstream release (Closes: #602970)
  + 0.8.x branch is declared stable by upstream now
* Add a UFW profile set:
  + debian/nginx.ufw.profile: Added.
  + debian/control: nginx: Suggests ufw.
  + debian/dirs: Add 'etc/ufw/applications.d'
  + debian/rules: Add install rule for the nginx UFW profile.
* Moved debian/dirs to debian/nginx.dirs
* Added types_hash_max_size to nginx.conf
* Install simple default index.html file (Closes: #581416)
  + debian/dirs: Add 'usr/share/nginx/www'.
  + debian/nginx.install: Add 'html/* usr/share/nginx/www'.
* debian/patches/nginx-echo.diff:
  + Added Echo module
* Added files for nginx.docs
  - /usr/share/doc/nginx/
    + debian/help/docs/fcgiwrap
    + debian/help/docs/php
    + debian/help/docs/support-irc
    + debian/help/docs/upstream
* Added files for nginx.examples
  - /usr/share/doc/nginx/examples/
    + debian/help/docs/drupal
    + debian/help/docs/http
    + debian/help/docs/mail
    + debian/help/docs/mailman
    + debian/help/docs/nginx.conf
    + debian/help/docs/virtual_hosts
    + debian/help/docs/wordpress
* debian/conf/:
  + Removed excess spaces
  + Added tabs where appropriate
  + Added SCRIPT_FILENAME to fastcgi_params

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
 
11
11
 
12
12
typedef struct {
 
13
    ngx_http_variable_value_t       *value;
13
14
    u_short                          start;
14
15
    u_short                          end;
15
 
    ngx_http_variable_value_t       *value;
16
16
} ngx_http_geo_range_t;
17
17
 
18
18
 
19
19
typedef struct {
20
 
    ngx_http_geo_range_t            *ranges;
21
 
    ngx_uint_t                       n;
22
 
} ngx_http_geo_low_ranges_t;
23
 
 
24
 
 
25
 
typedef struct {
26
 
    ngx_http_geo_low_ranges_t        low[0x10000];
 
20
    ngx_http_geo_range_t           **low;
27
21
    ngx_http_variable_value_t       *default_value;
28
22
} ngx_http_geo_high_ranges_t;
29
23
 
30
24
 
31
25
typedef struct {
 
26
    ngx_str_node_t                   sn;
 
27
    ngx_http_variable_value_t       *value;
 
28
    size_t                           offset;
 
29
} ngx_http_geo_variable_value_node_t;
 
30
 
 
31
 
 
32
typedef struct {
32
33
    ngx_http_variable_value_t       *value;
33
34
    ngx_str_t                       *net;
34
 
    ngx_http_geo_high_ranges_t      *high;
 
35
    ngx_http_geo_high_ranges_t       high;
35
36
    ngx_radix_tree_t                *tree;
36
37
    ngx_rbtree_t                     rbtree;
37
38
    ngx_rbtree_node_t                sentinel;
38
39
    ngx_array_t                     *proxies;
39
40
    ngx_pool_t                      *pool;
40
41
    ngx_pool_t                      *temp_pool;
 
42
 
 
43
    size_t                           data_size;
 
44
 
 
45
    ngx_str_t                        include_name;
 
46
    ngx_uint_t                       includes;
 
47
    ngx_uint_t                       entries;
 
48
 
 
49
    unsigned                         ranges:1;
 
50
    unsigned                         outside_entries:1;
 
51
    unsigned                         allow_binary_include:1;
 
52
    unsigned                         binary_include:1;
41
53
} ngx_http_geo_conf_ctx_t;
42
54
 
43
55
 
44
56
typedef struct {
45
57
    union {
46
58
        ngx_radix_tree_t            *tree;
47
 
        ngx_http_geo_high_ranges_t  *high;
 
59
        ngx_http_geo_high_ranges_t   high;
48
60
    } u;
49
61
 
50
62
    ngx_array_t                     *proxies;
73
85
    ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
74
86
static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
75
87
    ngx_cidr_t *cidr);
 
88
static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
 
89
    ngx_str_t *name);
 
90
static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
 
91
    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
 
92
static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
 
93
static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
 
94
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
76
95
 
77
96
 
78
97
static ngx_command_t  ngx_http_geo_commands[] = {
119
138
};
120
139
 
121
140
 
 
141
typedef struct {
 
142
    u_char    GEORNG[6];
 
143
    u_char    version;
 
144
    u_char    ptr_size;
 
145
    uint32_t  endianess;
 
146
    uint32_t  crc32;
 
147
} ngx_http_geo_header_t;
 
148
 
 
149
 
 
150
static ngx_http_geo_header_t  ngx_http_geo_header = {
 
151
    { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
 
152
};
 
153
 
 
154
 
122
155
/* AF_INET only */
123
156
 
124
157
static ngx_int_t
148
181
    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
149
182
 
150
183
    in_addr_t              addr;
151
 
    ngx_uint_t             i, n;
 
184
    ngx_uint_t             n;
152
185
    ngx_http_geo_range_t  *range;
153
186
 
154
 
    *v = *ctx->u.high->default_value;
 
187
    *v = *ctx->u.high.default_value;
155
188
 
156
189
    addr = ngx_http_geo_addr(r, ctx);
157
190
 
158
 
    range = ctx->u.high->low[addr >> 16].ranges;
159
 
 
160
 
    n = addr & 0xffff;
161
 
 
162
 
    for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) {
163
 
        if (n >= (ngx_uint_t) range[i].start
164
 
            && n <= (ngx_uint_t) range[i].end)
165
 
        {
166
 
            *v = *range[i].value;
167
 
        }
 
191
    range = ctx->u.high.low[addr >> 16];
 
192
 
 
193
    if (range) {
 
194
        n = addr & 0xffff;
 
195
        do {
 
196
            if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end)
 
197
            {
 
198
                *v = *range->value;
 
199
                break;
 
200
            }
 
201
        } while ((++range)->value);
168
202
    }
169
203
 
170
204
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
256
290
ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
257
291
{
258
292
    char                     *rv;
 
293
    void                    **p;
259
294
    size_t                    len;
260
295
    ngx_str_t                *value, name;
261
296
    ngx_uint_t                i;
302
337
        return NGX_CONF_ERROR;
303
338
    }
304
339
 
 
340
    ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
 
341
 
305
342
    ctx.temp_pool = ngx_create_pool(16384, cf->log);
306
343
    if (ctx.temp_pool == NULL) {
307
344
        return NGX_CONF_ERROR;
308
345
    }
309
346
 
310
 
    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
311
 
                    ngx_http_variable_value_rbtree_insert);
 
347
    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
312
348
 
313
 
    ctx.high = NULL;
314
 
    ctx.tree = NULL;
315
 
    ctx.proxies = NULL;
316
349
    ctx.pool = cf->pool;
 
350
    ctx.data_size = sizeof(ngx_http_geo_header_t)
 
351
                  + sizeof(ngx_http_variable_value_t)
 
352
                  + 0x10000 * sizeof(ngx_http_geo_range_t *);
 
353
    ctx.allow_binary_include = 1;
317
354
 
318
355
    save = *cf;
319
356
    cf->pool = pool;
327
364
 
328
365
    geo->proxies = ctx.proxies;
329
366
 
330
 
    if (ctx.high) {
331
 
 
332
 
        for (i = 0; i < 0x10000; i++) {
333
 
            a = (ngx_array_t *) ctx.high->low[i].ranges;
334
 
 
335
 
            if (a == NULL || a->nelts == 0) {
336
 
                continue;
337
 
            }
338
 
 
339
 
            ctx.high->low[i].n = a->nelts;
340
 
 
341
 
            len = a->nelts * sizeof(ngx_http_geo_range_t);
342
 
 
343
 
            ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
344
 
            if (ctx.high->low[i].ranges == NULL ){
345
 
                return NGX_CONF_ERROR;
346
 
            }
347
 
 
348
 
            ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
 
367
    if (ctx.high.low) {
 
368
 
 
369
        if (!ctx.binary_include) {
 
370
            for (i = 0; i < 0x10000; i++) {
 
371
                a = (ngx_array_t *) ctx.high.low[i];
 
372
 
 
373
                if (a == NULL || a->nelts == 0) {
 
374
                    continue;
 
375
                }
 
376
 
 
377
                len = a->nelts * sizeof(ngx_http_geo_range_t);
 
378
 
 
379
                ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
 
380
                if (ctx.high.low[i] == NULL) {
 
381
                    return NGX_CONF_ERROR;
 
382
                }
 
383
 
 
384
                p = (void **) ngx_cpymem(ctx.high.low[i], a->elts, len);
 
385
                *p = NULL;
 
386
                ctx.data_size += len + sizeof(void *);
 
387
            }
 
388
 
 
389
            if (ctx.allow_binary_include
 
390
                && !ctx.outside_entries
 
391
                && ctx.entries > 100000
 
392
                && ctx.includes == 1)
 
393
            {
 
394
                ngx_http_geo_create_binary_base(&ctx);
 
395
            }
349
396
        }
350
397
 
351
398
        geo->u.high = ctx.high;
353
400
        var->get_handler = ngx_http_geo_range_variable;
354
401
        var->data = (uintptr_t) geo;
355
402
 
 
403
        if (ctx.high.default_value == NULL) {
 
404
            ctx.high.default_value = &ngx_http_variable_null_value;
 
405
        }
 
406
 
356
407
        ngx_destroy_pool(ctx.temp_pool);
357
408
        ngx_destroy_pool(pool);
358
409
 
359
 
        if (ctx.high->default_value == NULL) {
360
 
            ctx.high->default_value = &ngx_http_variable_null_value;
361
 
        }
362
 
 
363
410
    } else {
364
411
        if (ctx.tree == NULL) {
365
412
            ctx.tree = ngx_radix_tree_create(cf->pool, -1);
396
443
ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
397
444
{
398
445
    char                     *rv;
399
 
    ngx_str_t                *value, file;
 
446
    ngx_str_t                *value;
400
447
    ngx_cidr_t                cidr;
401
448
    ngx_http_geo_conf_ctx_t  *ctx;
402
449
 
415
462
                goto failed;
416
463
            }
417
464
 
418
 
            ctx->high = ngx_pcalloc(ctx->pool,
419
 
                                    sizeof(ngx_http_geo_high_ranges_t));
420
 
            if (ctx->high == NULL) {
421
 
                goto failed;
422
 
            }
 
465
            ctx->ranges = 1;
423
466
 
424
467
            rv = NGX_CONF_OK;
425
468
 
435
478
 
436
479
    if (ngx_strcmp(value[0].data, "include") == 0) {
437
480
 
438
 
        file.len = value[1].len++;
439
 
 
440
 
        file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
441
 
        if (file.data == NULL) {
442
 
            goto failed;
443
 
        }
444
 
 
445
 
        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
446
 
            goto failed;
447
 
        }
448
 
 
449
 
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
450
 
 
451
 
        rv = ngx_conf_parse(cf, &file);
 
481
        rv = ngx_http_geo_include(cf, ctx, &value[1]);
452
482
 
453
483
        goto done;
454
484
 
463
493
        goto done;
464
494
    }
465
495
 
466
 
    if (ctx->high) {
 
496
    if (ctx->ranges) {
467
497
        rv = ngx_http_geo_range(cf, ctx, value);
468
498
 
469
499
    } else {
488
518
ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
489
519
    ngx_str_t *value)
490
520
{
491
 
    u_char                     *p, *last;
492
 
    in_addr_t                   start, end;
493
 
    ngx_str_t                  *net;
494
 
    ngx_uint_t                  del;
495
 
    ngx_http_variable_value_t  *old;
 
521
    u_char      *p, *last;
 
522
    in_addr_t    start, end;
 
523
    ngx_str_t   *net;
 
524
    ngx_uint_t   del;
496
525
 
497
526
    if (ngx_strcmp(value[0].data, "default") == 0) {
498
527
 
499
 
        old = ctx->high->default_value;
500
 
 
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;
504
 
        }
505
 
 
506
 
        if (old) {
 
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);
 
532
        }
 
533
 
 
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;
510
537
        }
511
538
 
512
539
        return NGX_CONF_OK;
513
540
    }
514
541
 
 
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;
 
547
    }
 
548
 
 
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;
 
554
        }
 
555
    }
 
556
 
 
557
    ctx->entries++;
 
558
    ctx->outside_entries = 1;
 
559
 
515
560
    if (ngx_strcmp(value[0].data, "delete") == 0) {
516
561
        net = &value[1];
517
562
        del = 1;
606
651
            e = 0xffff;
607
652
        }
608
653
 
609
 
        a = (ngx_array_t *) ctx->high->low[h].ranges;
 
654
        a = (ngx_array_t *) ctx->high.low[h];
610
655
 
611
656
        if (a == NULL) {
612
657
            a = ngx_array_create(ctx->temp_pool, 64,
615
660
                return NGX_CONF_ERROR;
616
661
            }
617
662
 
618
 
            ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
 
663
            ctx->high.low[h] = (ngx_http_geo_range_t *) a;
619
664
        }
620
665
 
621
666
        i = a->nelts;
803
848
            e = 0xffff;
804
849
        }
805
850
 
806
 
        a = (ngx_array_t *) ctx->high->low[h].ranges;
 
851
        a = (ngx_array_t *) ctx->high.low[h];
807
852
 
808
853
        if (a == NULL) {
809
854
            warn = 1;
929
974
ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
930
975
    ngx_str_t *value)
931
976
{
932
 
    uint32_t                         hash;
933
 
    ngx_http_variable_value_t       *val;
934
 
    ngx_http_variable_value_node_t  *vvn;
 
977
    uint32_t                             hash;
 
978
    ngx_http_variable_value_t           *val;
 
979
    ngx_http_geo_variable_value_node_t  *gvvn;
935
980
 
936
981
    hash = ngx_crc32_long(value->data, value->len);
937
982
 
938
 
    val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
 
983
    gvvn = (ngx_http_geo_variable_value_node_t *)
 
984
               ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
939
985
 
940
 
    if (val) {
941
 
        return val;
 
986
    if (gvvn) {
 
987
        return gvvn->value;
942
988
    }
943
989
 
944
990
    val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
956
1002
    val->no_cacheable = 0;
957
1003
    val->not_found = 0;
958
1004
 
959
 
    vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
960
 
    if (vvn == NULL) {
 
1005
    gvvn = ngx_palloc(ctx->temp_pool,
 
1006
                      sizeof(ngx_http_geo_variable_value_node_t));
 
1007
    if (gvvn == NULL) {
961
1008
        return NULL;
962
1009
    }
963
1010
 
964
 
    vvn->node.key = hash;
965
 
    vvn->len = val->len;
966
 
    vvn->value = val;
967
 
 
968
 
    ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
 
1011
    gvvn->sn.node.key = hash;
 
1012
    gvvn->sn.str.len = val->len;
 
1013
    gvvn->sn.str.data = val->data;
 
1014
    gvvn->value = val;
 
1015
    gvvn->offset = 0;
 
1016
 
 
1017
    ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
 
1018
 
 
1019
    ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
 
1020
                                sizeof(void *));
969
1021
 
970
1022
    return val;
971
1023
}
1030
1082
 
1031
1083
    return NGX_OK;
1032
1084
}
 
1085
 
 
1086
 
 
1087
static char *
 
1088
ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
 
1089
    ngx_str_t *name)
 
1090
{
 
1091
    char       *rv;
 
1092
    ngx_str_t   file;
 
1093
 
 
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;
 
1098
    }
 
1099
 
 
1100
    ngx_sprintf(file.data, "%V.bin%Z", name);
 
1101
 
 
1102
    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
 
1103
        return NGX_CONF_ERROR;
 
1104
    }
 
1105
 
 
1106
    if (ctx->ranges) {
 
1107
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
 
1108
 
 
1109
        switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
 
1110
        case NGX_OK:
 
1111
            return NGX_CONF_OK;
 
1112
        case NGX_ERROR:
 
1113
            return NGX_CONF_ERROR;
 
1114
        default:
 
1115
            break;
 
1116
        }
 
1117
    }
 
1118
 
 
1119
    file.len -= 4;
 
1120
    file.data[file.len] = '\0';
 
1121
 
 
1122
    ctx->include_name = file;
 
1123
 
 
1124
    if (ctx->outside_entries) {
 
1125
        ctx->allow_binary_include = 0;
 
1126
    }
 
1127
 
 
1128
    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
 
1129
 
 
1130
    rv = ngx_conf_parse(cf, &file);
 
1131
 
 
1132
    ctx->includes++;
 
1133
    ctx->outside_entries = 0;
 
1134
 
 
1135
    return rv;
 
1136
}
 
1137
 
 
1138
 
 
1139
static ngx_int_t
 
1140
ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
 
1141
    ngx_str_t *name)
 
1142
{
 
1143
    u_char                     *base, ch;
 
1144
    time_t                      mtime;
 
1145
    size_t                      size, len;
 
1146
    ssize_t                     n;
 
1147
    uint32_t                    crc32;
 
1148
    ngx_err_t                   err;
 
1149
    ngx_int_t                   rc;
 
1150
    ngx_uint_t                  i;
 
1151
    ngx_file_t                  file;
 
1152
    ngx_file_info_t             fi;
 
1153
    ngx_http_geo_range_t       *range, **ranges;
 
1154
    ngx_http_geo_header_t      *header;
 
1155
    ngx_http_variable_value_t  *vv;
 
1156
 
 
1157
    ngx_memzero(&file, sizeof(ngx_file_t));
 
1158
    file.name = *name;
 
1159
    file.log = cf->log;
 
1160
 
 
1161
    file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
 
1162
    if (file.fd == NGX_INVALID_FILE) {
 
1163
        err = ngx_errno;
 
1164
        if (err != NGX_ENOENT) {
 
1165
            ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
 
1166
                               ngx_open_file_n " \"%s\" failed", name->data);
 
1167
        }
 
1168
        return NGX_DECLINED;
 
1169
    }
 
1170
 
 
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",
 
1174
            name->data);
 
1175
        rc = NGX_ERROR;
 
1176
        goto done;
 
1177
    }
 
1178
 
 
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);
 
1183
        rc = NGX_ERROR;
 
1184
        goto done;
 
1185
    }
 
1186
 
 
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);
 
1190
        goto failed;
 
1191
    }
 
1192
 
 
1193
    size = (size_t) ngx_file_size(&fi);
 
1194
    mtime = ngx_file_mtime(&fi);
 
1195
 
 
1196
    ch = name->data[name->len - 4];
 
1197
    name->data[name->len - 4] = '\0';
 
1198
 
 
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);
 
1202
        goto failed;
 
1203
    }
 
1204
 
 
1205
    name->data[name->len - 4] = ch;
 
1206
 
 
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);
 
1210
        goto failed;
 
1211
    }
 
1212
 
 
1213
    base = ngx_palloc(ctx->pool, size);
 
1214
    if (base == NULL) {
 
1215
        goto failed;
 
1216
    }
 
1217
 
 
1218
    n = ngx_read_file(&file, base, size, 0);
 
1219
 
 
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);
 
1223
        goto failed;
 
1224
    }
 
1225
 
 
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);
 
1230
        goto failed;
 
1231
    }
 
1232
 
 
1233
    header = (ngx_http_geo_header_t *) base;
 
1234
 
 
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);
 
1238
        goto failed;
 
1239
    }
 
1240
 
 
1241
    ngx_crc32_init(crc32);
 
1242
 
 
1243
    vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
 
1244
 
 
1245
    while(vv->data) {
 
1246
        len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
 
1247
                        sizeof(void *));
 
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);
 
1251
    }
 
1252
    ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
 
1253
    vv++;
 
1254
 
 
1255
    ranges = (ngx_http_geo_range_t **) vv;
 
1256
 
 
1257
    for (i = 0; i < 0x10000; i++) {
 
1258
        ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
 
1259
        if (ranges[i]) {
 
1260
            ranges[i] = (ngx_http_geo_range_t *)
 
1261
                            ((u_char *) ranges[i] + (size_t) base);
 
1262
        }
 
1263
    }
 
1264
 
 
1265
    range = (ngx_http_geo_range_t *) &ranges[0x10000];
 
1266
 
 
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);
 
1273
            range++;
 
1274
        }
 
1275
        ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
 
1276
        range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
 
1277
    }
 
1278
 
 
1279
    ngx_crc32_final(crc32);
 
1280
 
 
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);
 
1284
        goto failed;
 
1285
    }
 
1286
 
 
1287
    ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
 
1288
                       "using binary geo range base \"%s\"", name->data);
 
1289
 
 
1290
    ctx->include_name = *name;
 
1291
    ctx->binary_include = 1;
 
1292
    ctx->high.low = ranges;
 
1293
    rc = NGX_OK;
 
1294
 
 
1295
    goto done;
 
1296
 
 
1297
failed:
 
1298
 
 
1299
    rc = NGX_DECLINED;
 
1300
 
 
1301
done:
 
1302
 
 
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);
 
1306
    }
 
1307
 
 
1308
    return rc;
 
1309
}
 
1310
 
 
1311
 
 
1312
static void
 
1313
ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
 
1314
{
 
1315
    u_char                              *p;
 
1316
    uint32_t                             hash;
 
1317
    ngx_str_t                            s;
 
1318
    ngx_uint_t                           i;
 
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;
 
1323
 
 
1324
    fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
 
1325
    if (fm.name == NULL) {
 
1326
        return;
 
1327
    }
 
1328
 
 
1329
    ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
 
1330
 
 
1331
    fm.size = ctx->data_size;
 
1332
    fm.log = ctx->pool->log;
 
1333
 
 
1334
    ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
 
1335
                  "creating binary geo range base \"%s\"", fm.name);
 
1336
 
 
1337
    if (ngx_create_file_mapping(&fm) != NGX_OK) {
 
1338
        return;
 
1339
    }
 
1340
 
 
1341
    p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
 
1342
                   sizeof(ngx_http_geo_header_t));
 
1343
 
 
1344
    p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
 
1345
                                 ctx->rbtree.sentinel);
 
1346
 
 
1347
    p += sizeof(ngx_http_variable_value_t);
 
1348
 
 
1349
    ranges = (ngx_http_geo_range_t **) p;
 
1350
 
 
1351
    p += 0x10000 * sizeof(ngx_http_geo_range_t *);
 
1352
 
 
1353
    for (i = 0; i < 0x10000; i++) {
 
1354
        r = ctx->high.low[i];
 
1355
        if (r == NULL) {
 
1356
            continue;
 
1357
        }
 
1358
 
 
1359
        range = (ngx_http_geo_range_t *) p;
 
1360
        ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
 
1361
 
 
1362
        do {
 
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);
 
1368
 
 
1369
            range->value = (ngx_http_variable_value_t *) gvvn->offset;
 
1370
            range->start = r->start;
 
1371
            range->end = r->end;
 
1372
            range++;
 
1373
 
 
1374
        } while ((++r)->value);
 
1375
 
 
1376
        range->value = NULL;
 
1377
 
 
1378
        p = (u_char *) range + sizeof(void *);
 
1379
    }
 
1380
 
 
1381
    header = fm.addr;
 
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));
 
1385
 
 
1386
    ngx_close_file_mapping(&fm);
 
1387
}
 
1388
 
 
1389
 
 
1390
static u_char *
 
1391
ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
 
1392
    ngx_rbtree_node_t *sentinel)
 
1393
{
 
1394
    ngx_http_variable_value_t           *vv;
 
1395
    ngx_http_geo_variable_value_node_t  *gvvn;
 
1396
 
 
1397
    if (node == sentinel) {
 
1398
        return p;
 
1399
    }
 
1400
 
 
1401
    gvvn = (ngx_http_geo_variable_value_node_t *) node;
 
1402
    gvvn->offset = p - base;
 
1403
 
 
1404
    vv = (ngx_http_variable_value_t *) p;
 
1405
    *vv = *gvvn->value;
 
1406
    p += sizeof(ngx_http_variable_value_t);
 
1407
    vv->data = (u_char *) (p - base);
 
1408
 
 
1409
    p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
 
1410
 
 
1411
    p = ngx_align_ptr(p, sizeof(void *));
 
1412
 
 
1413
    p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
 
1414
 
 
1415
    return ngx_http_geo_copy_values(base, p, node->right, sentinel);
 
1416
}