├── .gitattributes ├── GeoLite2-City_20200519.tar.gz ├── GeoLite2-Country_20200519.tar.gz ├── README.md ├── libmaxminddb-1.3.2.tar.gz ├── ngx_http_geoip2_module ├── LICENSE ├── README.md ├── config ├── ngx_http_geoip2_module.c └── ngx_stream_geoip2_module.c └── tests ├── Dockerfile ├── html ├── a │ └── index.html └── b │ └── index.html └── nginx.conf /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* linguist-language=Shell 2 | -------------------------------------------------------------------------------- /GeoLite2-City_20200519.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ar414-com/nginx-geoip2/7700a9f6157c0f90ed687ce4c461899e3adb4950/GeoLite2-City_20200519.tar.gz -------------------------------------------------------------------------------- /GeoLite2-Country_20200519.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ar414-com/nginx-geoip2/7700a9f6157c0f90ed687ce4c461899e3adb4950/GeoLite2-Country_20200519.tar.gz -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Nginx Compile Install 2 | ![](https://img.shields.io/badge/build-passing-brightgreen) 3 | ![](https://img.shields.io/badge/coverage-100%25-green) 4 | 5 | ### install libmaxminddb 6 | ```bash 7 | $ wget https://github.com/maxmind/libmaxminddb/releases/download/1.3.2/libmaxminddb-1.3.2.tar.gz 8 | $ tar -zxvf libmaxminddb-1.3.2.tar.gz 9 | $ cd libmaxminddb-1.3.2 10 | $ ./configure && make && make install 11 | $ echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf 12 | $ ldconfig 13 | ``` 14 | 15 | ### download geoip2 data 16 | ``` 17 | $ git clone https://github.com/ar414-com/nginx-geoip2 18 | $ cd nginx-geoip2 19 | $ tar -zxvf GeoLite2-City_20200519.tar.gz 20 | $ mv ./GeoLite2-City_20200519/GeoLite2-City.mmdb /usr/share/GeoIP/ 21 | $ tar -zxvf GeoLite2-Country_20200519.tar.gz 22 | $ mv ./GeoLite2-Country_20200519/GeoLite2-Country.mmdb /usr/share/GeoIP/ 23 | ``` 24 | 25 | ### compile and install 26 | ``` 27 | $ cd ~ && git clone https://github.com/ar414-com/nginx-geoip2 28 | $ ./configure --user=www --group=www --prefix=/www/server/nginx \ 29 | --add-module=/root/nginx-geoip2/ngx_http_geoip2_module 30 | $ make && make install 31 | ``` 32 | 33 | ## Docker 34 | 35 | ### get docker image 36 | ``` 37 | $ docker pull ar414/nginx-geoip2 38 | ``` 39 | 40 | ### run 41 | ``` 42 | $ docker run -it -d -p 80:80 -p 443:443 --rm ar414/nginx-geoip2 43 | ``` 44 | 45 | ### test 46 | ``` 47 | $ curl -v http://127.0.0.1:80 48 | < rootpath: html/b 49 | < country: CN 50 | ``` 51 | 52 | ``` 53 | $ curl -v -x https://61.194.237.25:8080 http://127.0.0.1:80 54 | < rootpath: html/a 55 | < country: JP 56 | ``` 57 | 58 | -------------------------------------------------------------------------------- /libmaxminddb-1.3.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ar414-com/nginx-geoip2/7700a9f6157c0f90ed687ce4c461899e3adb4950/libmaxminddb-1.3.2.tar.gz -------------------------------------------------------------------------------- /ngx_http_geoip2_module/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Lee Valentine 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /ngx_http_geoip2_module/README.md: -------------------------------------------------------------------------------- 1 | Description 2 | =========== 3 | 4 | **ngx_http_geoip2_module** - creates variables with values from the maxmind geoip2 databases based on the client IP (default) or from a specific variable (supports both IPv4 and IPv6) 5 | 6 | The module now supports nginx streams and can be used in the same way the http module can be used. 7 | 8 | ## Installing 9 | First install [libmaxminddb](https://github.com/maxmind/libmaxminddb) as described in its [README.md 10 | file](https://github.com/maxmind/libmaxminddb/blob/master/README.md#installing-from-a-tarball). 11 | 12 | #### Download nginx source 13 | ``` 14 | wget http://nginx.org/download/nginx-VERSION.tar.gz 15 | tar zxvf nginx-VERSION.tar.gz 16 | cd nginx-VERSION 17 | ``` 18 | 19 | ##### To build as a dynamic module (nginx 1.9.11+): 20 | ``` 21 | ./configure --add-dynamic-module=/path/to/ngx_http_geoip2_module 22 | make 23 | make install 24 | ``` 25 | 26 | This will produce ```objs/ngx_http_geoip2_module.so```. It can be copied to your nginx module path manually if you wish. 27 | 28 | Add the following line to your nginx.conf: 29 | ``` 30 | load_module modules/ngx_http_geoip2_module.so; 31 | ``` 32 | 33 | ##### To build as a static module: 34 | ``` 35 | ./configure --add-module=/path/to/ngx_http_geoip2_module 36 | make 37 | make install 38 | ``` 39 | 40 | ## Download Maxmind GeoLite2 Database (optional) 41 | The free GeoLite2 databases are available from [Maxminds website](http://dev.maxmind.com/geoip/geoip2/geolite2/) (requires signing up) 42 | 43 | ## Example Usage: 44 | ``` 45 | http { 46 | ... 47 | geoip2 /etc/maxmind-country.mmdb { 48 | auto_reload 5m; 49 | $geoip2_metadata_country_build metadata build_epoch; 50 | $geoip2_data_country_code default=US source=$variable_with_ip country iso_code; 51 | $geoip2_data_country_name country names en; 52 | } 53 | 54 | geoip2 /etc/maxmind-city.mmdb { 55 | $geoip2_data_city_name default=London city names en; 56 | } 57 | .... 58 | 59 | fastcgi_param COUNTRY_CODE $geoip2_data_country_code; 60 | fastcgi_param COUNTRY_NAME $geoip2_data_country_name; 61 | fastcgi_param CITY_NAME $geoip2_data_city_name; 62 | .... 63 | } 64 | 65 | stream { 66 | ... 67 | geoip2 /etc/maxmind-country.mmdb { 68 | $geoip2_data_country_code default=US source=$remote_addr country iso_code; 69 | } 70 | ... 71 | } 72 | ``` 73 | 74 | ##### Metadata: 75 | Retrieve metadata regarding the geoip database. 76 | ``` 77 | $variable_name metadata 78 | ``` 79 | Available fields: 80 | - build_epoch: the build timestamp of the maxmind database. 81 | - last_check: the last time the database was checked for changes (when using auto_reload) 82 | - last_change: the last time the database was reloaded (when using auto_reload) 83 | 84 | ##### Autoreload (default: disabled): 85 | Enabling auto reload will have nginx check the modification time of the database at the specified 86 | interval and reload it if it has changed. 87 | ``` 88 | auto_reload 89 | ``` 90 | 91 | ##### GeoIP: 92 | ``` 93 | $variable_name [default= 109 | "iso_code": 110 | "US" 111 | "names": 112 | { 113 | "de": 114 | "USA" 115 | "en": 116 | "United States" 117 | } 118 | } 119 | } 120 | 121 | $ mmdblookup --file /usr/share/GeoIP/GeoIP2-Country.mmdb --ip 8.8.8.8 country names en 122 | 123 | "United States" 124 | ``` 125 | 126 | This translates to: 127 | 128 | ``` 129 | $country_name "default=United States" source=$remote_addr country names en 130 | ``` 131 | -------------------------------------------------------------------------------- /ngx_http_geoip2_module/config: -------------------------------------------------------------------------------- 1 | ngx_feature="MaxmindDB library" 2 | ngx_feature_name= 3 | ngx_feature_run=no 4 | ngx_feature_incs="#include " 5 | ngx_feature_libs=-lmaxminddb 6 | ngx_feature_test="MMDB_s mmdb" 7 | . auto/feature 8 | 9 | ngx_addon_name="ngx_geoip2_module" 10 | 11 | if [ $ngx_found = yes ]; then 12 | if test -n "$ngx_module_link"; then 13 | if [ $HTTP != NO ]; then 14 | ngx_module_type=HTTP 15 | ngx_module_name="ngx_http_geoip2_module" 16 | ngx_module_incs= 17 | ngx_module_deps= 18 | ngx_module_srcs="$ngx_addon_dir/ngx_http_geoip2_module.c" 19 | ngx_module_libs="$ngx_feature_libs" 20 | . auto/module 21 | fi 22 | 23 | nginx_version=`awk '/^#define nginx_version / {print $3}' src/core/nginx.h` 24 | if [ $STREAM != NO -a $nginx_version -gt 1011001 ]; then 25 | ngx_module_type=STREAM 26 | ngx_module_name="ngx_stream_geoip2_module" 27 | ngx_module_incs= 28 | ngx_module_deps= 29 | ngx_module_srcs="$ngx_addon_dir/ngx_stream_geoip2_module.c" 30 | ngx_module_libs="$ngx_feature_libs" 31 | . auto/module 32 | fi 33 | else 34 | HTTP_MODULES="$HTTP_MODULES ngx_http_geoip2_module" 35 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_geoip2_module.c" 36 | CORE_LIBS="$CORE_LIBS $ngx_feature_libs" 37 | fi 38 | else 39 | cat << END 40 | $0: error: the geoip2 module requires the maxminddb library. 41 | END 42 | exit 1 43 | fi 44 | -------------------------------------------------------------------------------- /ngx_http_geoip2_module/ngx_http_geoip2_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Lee Valentine 3 | * 4 | * Based on nginx's 'ngx_http_geoip_module.c' by Igor Sysoev 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | 15 | typedef struct { 16 | MMDB_s mmdb; 17 | MMDB_lookup_result_s result; 18 | time_t last_check; 19 | time_t last_change; 20 | time_t check_interval; 21 | #if (NGX_HAVE_INET6) 22 | uint8_t address[16]; 23 | #else 24 | unsigned long address; 25 | #endif 26 | ngx_queue_t queue; 27 | } ngx_http_geoip2_db_t; 28 | 29 | typedef struct { 30 | ngx_queue_t databases; 31 | ngx_array_t *proxies; 32 | ngx_flag_t proxy_recursive; 33 | } ngx_http_geoip2_conf_t; 34 | 35 | typedef struct { 36 | ngx_http_geoip2_db_t *database; 37 | const char **lookup; 38 | ngx_str_t default_value; 39 | ngx_http_complex_value_t source; 40 | } ngx_http_geoip2_ctx_t; 41 | 42 | typedef struct { 43 | ngx_http_geoip2_db_t *database; 44 | ngx_str_t metavalue; 45 | } ngx_http_geoip2_metadata_t; 46 | 47 | 48 | static ngx_int_t ngx_http_geoip2_variable(ngx_http_request_t *r, 49 | ngx_http_variable_value_t *v, uintptr_t data); 50 | static ngx_int_t ngx_http_geoip2_metadata(ngx_http_request_t *r, 51 | ngx_http_variable_value_t *v, uintptr_t data); 52 | static void *ngx_http_geoip2_create_conf(ngx_conf_t *cf); 53 | static char *ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf); 54 | static char *ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, 55 | void *conf); 56 | static char *ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, 57 | void *conf); 58 | static char *ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, 59 | void *conf); 60 | static char *ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf, 61 | ngx_http_geoip2_db_t *database); 62 | static char *ngx_http_geoip2_add_variable_metadata(ngx_conf_t *cf, 63 | ngx_http_geoip2_db_t *database); 64 | static char *ngx_http_geoip2_proxy(ngx_conf_t *cf, ngx_command_t *cmd, 65 | void *conf); 66 | static ngx_int_t ngx_http_geoip2_cidr_value(ngx_conf_t *cf, ngx_str_t *net, 67 | ngx_cidr_t *cidr); 68 | static void ngx_http_geoip2_cleanup(void *data); 69 | static ngx_int_t ngx_http_geoip2_init(ngx_conf_t *cf); 70 | 71 | 72 | #define FORMAT(fmt, ...) do { \ 73 | p = ngx_palloc(r->pool, NGX_OFF_T_LEN); \ 74 | if (p == NULL) { \ 75 | return NGX_ERROR; \ 76 | } \ 77 | v->len = ngx_sprintf(p, fmt, __VA_ARGS__) - p; \ 78 | v->data = p; \ 79 | } while (0) 80 | 81 | static ngx_command_t ngx_http_geoip2_commands[] = { 82 | 83 | { ngx_string("geoip2"), 84 | NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, 85 | ngx_http_geoip2, 86 | NGX_HTTP_MAIN_CONF_OFFSET, 87 | 0, 88 | NULL }, 89 | 90 | { ngx_string("geoip2_proxy"), 91 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, 92 | ngx_http_geoip2_proxy, 93 | NGX_HTTP_MAIN_CONF_OFFSET, 94 | 0, 95 | NULL }, 96 | 97 | { ngx_string("geoip2_proxy_recursive"), 98 | NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, 99 | ngx_conf_set_flag_slot, 100 | NGX_HTTP_MAIN_CONF_OFFSET, 101 | offsetof(ngx_http_geoip2_conf_t, proxy_recursive), 102 | NULL }, 103 | 104 | ngx_null_command 105 | }; 106 | 107 | 108 | static ngx_http_module_t ngx_http_geoip2_module_ctx = { 109 | NULL, /* preconfiguration */ 110 | ngx_http_geoip2_init, /* postconfiguration */ 111 | 112 | ngx_http_geoip2_create_conf, /* create main configuration */ 113 | ngx_http_geoip2_init_conf, /* init main configuration */ 114 | 115 | NULL, /* create server configuration */ 116 | NULL, /* merge server configuration */ 117 | 118 | NULL, /* create location configuration */ 119 | NULL /* merge location configuration */ 120 | }; 121 | 122 | 123 | ngx_module_t ngx_http_geoip2_module = { 124 | NGX_MODULE_V1, 125 | &ngx_http_geoip2_module_ctx, /* module context */ 126 | ngx_http_geoip2_commands, /* module directives */ 127 | NGX_HTTP_MODULE, /* module type */ 128 | NULL, /* init master */ 129 | NULL, /* init module */ 130 | NULL, /* init process */ 131 | NULL, /* init thread */ 132 | NULL, /* exit thread */ 133 | NULL, /* exit process */ 134 | NULL, /* exit master */ 135 | NGX_MODULE_V1_PADDING 136 | }; 137 | 138 | 139 | static ngx_int_t 140 | ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 141 | uintptr_t data) 142 | { 143 | ngx_http_geoip2_ctx_t *geoip2 = (ngx_http_geoip2_ctx_t *) data; 144 | ngx_http_geoip2_db_t *database = geoip2->database; 145 | int mmdb_error; 146 | MMDB_entry_data_s entry_data; 147 | ngx_http_geoip2_conf_t *gcf; 148 | ngx_addr_t addr; 149 | ngx_array_t *xfwd; 150 | u_char *p; 151 | ngx_str_t val; 152 | 153 | #if (NGX_HAVE_INET6) 154 | uint8_t address[16], *addressp = address; 155 | #else 156 | unsigned long address; 157 | #endif 158 | 159 | if (geoip2->source.value.len > 0) { 160 | if (ngx_http_complex_value(r, &geoip2->source, &val) != NGX_OK) { 161 | goto not_found; 162 | } 163 | 164 | if (ngx_parse_addr(r->pool, &addr, val.data, val.len) != NGX_OK) { 165 | goto not_found; 166 | } 167 | } else { 168 | gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip2_module); 169 | addr.sockaddr = r->connection->sockaddr; 170 | addr.socklen = r->connection->socklen; 171 | 172 | xfwd = &r->headers_in.x_forwarded_for; 173 | 174 | if (xfwd->nelts > 0 && gcf->proxies != NULL) { 175 | (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, 176 | gcf->proxies, gcf->proxy_recursive); 177 | } 178 | } 179 | 180 | switch (addr.sockaddr->sa_family) { 181 | case AF_INET: 182 | #if (NGX_HAVE_INET6) 183 | ngx_memset(addressp, 0, 12); 184 | ngx_memcpy(addressp + 12, &((struct sockaddr_in *) 185 | addr.sockaddr)->sin_addr.s_addr, 4); 186 | break; 187 | 188 | case AF_INET6: 189 | ngx_memcpy(addressp, &((struct sockaddr_in6 *) 190 | addr.sockaddr)->sin6_addr.s6_addr, 16); 191 | #else 192 | address = ((struct sockaddr_in *)addr.sockaddr)->sin_addr.s_addr; 193 | #endif 194 | break; 195 | 196 | default: 197 | goto not_found; 198 | } 199 | 200 | #if (NGX_HAVE_INET6) 201 | if (ngx_memcmp(&address, &database->address, sizeof(address)) 202 | != 0) { 203 | #else 204 | if (address != database->address) { 205 | #endif 206 | memcpy(&database->address, &address, sizeof(address)); 207 | database->result = MMDB_lookup_sockaddr(&database->mmdb, 208 | addr.sockaddr, &mmdb_error); 209 | 210 | if (mmdb_error != MMDB_SUCCESS) { 211 | goto not_found; 212 | } 213 | } 214 | 215 | if (!database->result.found_entry 216 | || MMDB_aget_value(&database->result.entry, &entry_data, 217 | geoip2->lookup) != MMDB_SUCCESS) { 218 | goto not_found; 219 | } 220 | 221 | if (!entry_data.has_data) { 222 | goto not_found; 223 | } 224 | 225 | switch (entry_data.type) { 226 | case MMDB_DATA_TYPE_BOOLEAN: 227 | FORMAT("%d", entry_data.boolean); 228 | break; 229 | case MMDB_DATA_TYPE_UTF8_STRING: 230 | v->len = entry_data.data_size; 231 | v->data = ngx_pnalloc(r->pool, v->len); 232 | if (v->data == NULL) { 233 | return NGX_ERROR; 234 | } 235 | ngx_memcpy(v->data, (u_char *) entry_data.utf8_string, v->len); 236 | break; 237 | case MMDB_DATA_TYPE_BYTES: 238 | v->len = entry_data.data_size; 239 | v->data = ngx_pnalloc(r->pool, v->len); 240 | if (v->data == NULL) { 241 | return NGX_ERROR; 242 | } 243 | ngx_memcpy(v->data, (u_char *) entry_data.bytes, v->len); 244 | break; 245 | case MMDB_DATA_TYPE_FLOAT: 246 | FORMAT("%.5f", entry_data.float_value); 247 | break; 248 | case MMDB_DATA_TYPE_DOUBLE: 249 | FORMAT("%.5f", entry_data.double_value); 250 | break; 251 | case MMDB_DATA_TYPE_UINT16: 252 | FORMAT("%uD", entry_data.uint16); 253 | break; 254 | case MMDB_DATA_TYPE_UINT32: 255 | FORMAT("%uD", entry_data.uint32); 256 | break; 257 | case MMDB_DATA_TYPE_INT32: 258 | FORMAT("%D", entry_data.int32); 259 | break; 260 | case MMDB_DATA_TYPE_UINT64: 261 | FORMAT("%uL", entry_data.uint64); 262 | break; 263 | case MMDB_DATA_TYPE_UINT128: ; 264 | #if MMDB_UINT128_IS_BYTE_ARRAY 265 | uint8_t *val = (uint8_t *)entry_data.uint128; 266 | FORMAT( "0x%02x%02x%02x%02x%02x%02x%02x%02x" 267 | "%02x%02x%02x%02x%02x%02x%02x%02x", 268 | val[0], val[1], val[2], val[3], 269 | val[4], val[5], val[6], val[7], 270 | val[8], val[9], val[10], val[11], 271 | val[12], val[13], val[14], val[15]); 272 | #else 273 | mmdb_uint128_t val = entry_data.uint128; 274 | FORMAT("0x%016uxL%016uxL", 275 | (uint64_t) (val >> 64), (uint64_t) val); 276 | #endif 277 | break; 278 | default: 279 | goto not_found; 280 | } 281 | 282 | v->valid = 1; 283 | v->no_cacheable = 0; 284 | v->not_found = 0; 285 | 286 | return NGX_OK; 287 | 288 | not_found: 289 | if (geoip2->default_value.len > 0) { 290 | v->data = geoip2->default_value.data; 291 | v->len = geoip2->default_value.len; 292 | 293 | v->valid = 1; 294 | v->no_cacheable = 0; 295 | v->not_found = 0; 296 | } else { 297 | v->not_found = 1; 298 | } 299 | 300 | return NGX_OK; 301 | } 302 | 303 | 304 | static ngx_int_t 305 | ngx_http_geoip2_metadata(ngx_http_request_t *r, ngx_http_variable_value_t *v, 306 | uintptr_t data) 307 | { 308 | ngx_http_geoip2_metadata_t *metadata = (ngx_http_geoip2_metadata_t *) data; 309 | ngx_http_geoip2_db_t *database = metadata->database; 310 | u_char *p; 311 | 312 | if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) { 313 | FORMAT("%uL", database->mmdb.metadata.build_epoch); 314 | } else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) { 315 | FORMAT("%T", database->last_check); 316 | } else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) { 317 | FORMAT("%T", database->last_change); 318 | } else { 319 | v->not_found = 1; 320 | return NGX_OK; 321 | } 322 | 323 | v->valid = 1; 324 | v->no_cacheable = 0; 325 | v->not_found = 0; 326 | 327 | return NGX_OK; 328 | } 329 | 330 | 331 | static void * 332 | ngx_http_geoip2_create_conf(ngx_conf_t *cf) 333 | { 334 | ngx_pool_cleanup_t *cln; 335 | ngx_http_geoip2_conf_t *conf; 336 | 337 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_conf_t)); 338 | if (conf == NULL) { 339 | return NULL; 340 | } 341 | 342 | conf->proxy_recursive = NGX_CONF_UNSET; 343 | 344 | cln = ngx_pool_cleanup_add(cf->pool, 0); 345 | if (cln == NULL) { 346 | return NULL; 347 | } 348 | 349 | ngx_queue_init(&conf->databases); 350 | 351 | cln->handler = ngx_http_geoip2_cleanup; 352 | cln->data = conf; 353 | 354 | return conf; 355 | } 356 | 357 | 358 | static char * 359 | ngx_http_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 360 | { 361 | ngx_http_geoip2_conf_t *gcf = conf; 362 | ngx_str_t *value; 363 | int status; 364 | ngx_http_geoip2_db_t *database; 365 | char *rv; 366 | ngx_conf_t save; 367 | ngx_queue_t *q; 368 | 369 | value = cf->args->elts; 370 | 371 | if (value[1].data && value[1].data[0] != '/') { 372 | if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { 373 | return NGX_CONF_ERROR; 374 | } 375 | } 376 | 377 | if (!ngx_queue_empty(&gcf->databases)) { 378 | for (q = ngx_queue_head(&gcf->databases); 379 | q != ngx_queue_sentinel(&gcf->databases); 380 | q = ngx_queue_next(q)) 381 | { 382 | database = ngx_queue_data(q, ngx_http_geoip2_db_t, queue); 383 | if (ngx_strcmp(value[1].data, database->mmdb.filename) == 0) { 384 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 385 | "Duplicate GeoIP2 mmdb - %V", &value[1]); 386 | return NGX_CONF_ERROR; 387 | } 388 | } 389 | } 390 | 391 | database = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_db_t)); 392 | if (database == NULL) { 393 | return NGX_CONF_ERROR; 394 | } 395 | 396 | ngx_queue_insert_tail(&gcf->databases, &database->queue); 397 | database->last_check = database->last_change = ngx_time(); 398 | 399 | status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb); 400 | 401 | if (status != MMDB_SUCCESS) { 402 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 403 | "MMDB_open(\"%V\") failed - %s", &value[1], 404 | MMDB_strerror(status)); 405 | return NGX_CONF_ERROR; 406 | } 407 | 408 | save = *cf; 409 | cf->handler = ngx_http_geoip2_parse_config; 410 | cf->handler_conf = (void *) database; 411 | 412 | rv = ngx_conf_parse(cf, NULL); 413 | *cf = save; 414 | return rv; 415 | } 416 | 417 | 418 | static char * 419 | ngx_http_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 420 | { 421 | ngx_http_geoip2_db_t *database; 422 | ngx_str_t *value; 423 | time_t interval; 424 | 425 | value = cf->args->elts; 426 | 427 | if (value[0].data[0] == '$') { 428 | return ngx_http_geoip2_add_variable(cf, dummy, conf); 429 | } 430 | 431 | if (value[0].len == 11 432 | && ngx_strncmp(value[0].data, "auto_reload", 11) == 0) { 433 | if ((int) cf->args->nelts != 2) { 434 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 435 | "invalid number of arguments for auto_reload"); 436 | return NGX_CONF_ERROR; 437 | } 438 | 439 | interval = ngx_parse_time(&value[1], true); 440 | 441 | if (interval == (time_t) NGX_ERROR) { 442 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 443 | "invalid interval for auto_reload \"%V\"", 444 | value[1]); 445 | return NGX_CONF_ERROR; 446 | } 447 | 448 | 449 | database = (ngx_http_geoip2_db_t *) conf; 450 | database->check_interval = interval; 451 | return NGX_CONF_OK; 452 | } 453 | 454 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 455 | "invalid setting \"%V\"", &value[0]); 456 | return NGX_CONF_ERROR; 457 | } 458 | 459 | 460 | static char * 461 | ngx_http_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 462 | { 463 | ngx_http_geoip2_db_t *database; 464 | ngx_str_t *value; 465 | int nelts; 466 | 467 | value = cf->args->elts; 468 | 469 | if (value[0].data[0] != '$') { 470 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 471 | "invalid variable name \"%V\"", &value[0]); 472 | return NGX_CONF_ERROR; 473 | } 474 | 475 | value[0].len--; 476 | value[0].data++; 477 | 478 | nelts = (int) cf->args->nelts; 479 | database = (ngx_http_geoip2_db_t *) conf; 480 | 481 | if (nelts > 0 && value[1].len == 8 && ngx_strncmp(value[1].data, "metadata", 8) == 0) { 482 | return ngx_http_geoip2_add_variable_metadata(cf, database); 483 | } 484 | 485 | return ngx_http_geoip2_add_variable_geodata(cf, database); 486 | } 487 | 488 | 489 | static char * 490 | ngx_http_geoip2_add_variable_metadata(ngx_conf_t *cf, ngx_http_geoip2_db_t *database) 491 | { 492 | ngx_http_geoip2_metadata_t *metadata; 493 | ngx_str_t *value, name; 494 | ngx_http_variable_t *var; 495 | 496 | metadata = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_metadata_t)); 497 | if (metadata == NULL) { 498 | return NGX_CONF_ERROR; 499 | } 500 | 501 | value = cf->args->elts; 502 | name = value[0]; 503 | 504 | metadata->database = database; 505 | metadata->metavalue = value[2]; 506 | 507 | var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); 508 | if (var == NULL) { 509 | return NGX_CONF_ERROR; 510 | } 511 | 512 | var->get_handler = ngx_http_geoip2_metadata; 513 | var->data = (uintptr_t) metadata; 514 | 515 | return NGX_CONF_OK; 516 | } 517 | 518 | 519 | static char * 520 | ngx_http_geoip2_add_variable_geodata(ngx_conf_t *cf, ngx_http_geoip2_db_t *database) 521 | { 522 | ngx_http_geoip2_ctx_t *geoip2; 523 | ngx_http_compile_complex_value_t ccv; 524 | ngx_str_t *value, name, source; 525 | ngx_http_variable_t *var; 526 | int i, nelts, idx; 527 | 528 | geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_ctx_t)); 529 | if (geoip2 == NULL) { 530 | return NGX_CONF_ERROR; 531 | } 532 | 533 | geoip2->database = database; 534 | ngx_str_null(&source); 535 | 536 | value = cf->args->elts; 537 | name = value[0]; 538 | 539 | nelts = (int) cf->args->nelts; 540 | idx = 1; 541 | 542 | if (nelts > idx) { 543 | for (i = idx; i < nelts; i++) { 544 | if (ngx_strnstr(value[idx].data, "=", value[idx].len) == NULL) { 545 | break; 546 | } 547 | 548 | if (value[idx].len > 8 && ngx_strncmp(value[idx].data, "default=", 8) == 0) { 549 | if (geoip2->default_value.len > 0) { 550 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 551 | "default has already been declared for \"$%V\"", &name); 552 | return NGX_CONF_ERROR; 553 | } 554 | 555 | geoip2->default_value.len = value[idx].len - 8; 556 | geoip2->default_value.data = value[idx].data + 8; 557 | } else if (value[idx].len > 7 && ngx_strncmp(value[idx].data, "source=", 7) == 0) { 558 | if (source.len > 0) { 559 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 560 | "source has already been declared for \"$%V\"", &name); 561 | return NGX_CONF_ERROR; 562 | } 563 | 564 | source.len = value[idx].len - 7; 565 | source.data = value[idx].data + 7; 566 | 567 | if (source.data[0] != '$') { 568 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 569 | "invalid source variable name \"%V\"", &source); 570 | return NGX_CONF_ERROR; 571 | } 572 | 573 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 574 | ccv.cf = cf; 575 | ccv.value = &source; 576 | ccv.complex_value = &geoip2->source; 577 | 578 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 579 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 580 | "unable to compile \"%V\" for \"$%V\"", &source, &name); 581 | return NGX_CONF_ERROR; 582 | } 583 | } else { 584 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 585 | "invalid setting \"%V\" for \"$%V\"", &value[idx], &name); 586 | return NGX_CONF_ERROR; 587 | } 588 | 589 | idx++; 590 | } 591 | } 592 | 593 | var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); 594 | if (var == NULL) { 595 | return NGX_CONF_ERROR; 596 | } 597 | 598 | geoip2->lookup = ngx_pcalloc(cf->pool, sizeof(const char *) * 599 | (cf->args->nelts - (idx - 1))); 600 | 601 | if (geoip2->lookup == NULL) { 602 | return NGX_CONF_ERROR; 603 | } 604 | 605 | for (i = idx; i < nelts; i++) { 606 | geoip2->lookup[i - idx] = (char *) value[i].data; 607 | } 608 | geoip2->lookup[i - idx] = NULL; 609 | 610 | var->get_handler = ngx_http_geoip2_variable; 611 | var->data = (uintptr_t) geoip2; 612 | 613 | return NGX_CONF_OK; 614 | } 615 | 616 | 617 | static char * 618 | ngx_http_geoip2_init_conf(ngx_conf_t *cf, void *conf) 619 | { 620 | ngx_http_geoip2_conf_t *gcf = conf; 621 | ngx_conf_init_value(gcf->proxy_recursive, 0); 622 | return NGX_CONF_OK; 623 | } 624 | 625 | 626 | static char * 627 | ngx_http_geoip2_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 628 | { 629 | ngx_http_geoip2_conf_t *gcf = conf; 630 | ngx_str_t *value; 631 | ngx_cidr_t cidr, *c; 632 | 633 | value = cf->args->elts; 634 | 635 | if (ngx_http_geoip2_cidr_value(cf, &value[1], &cidr) != NGX_OK) { 636 | return NGX_CONF_ERROR; 637 | } 638 | 639 | if (gcf->proxies == NULL) { 640 | gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t)); 641 | if (gcf->proxies == NULL) { 642 | return NGX_CONF_ERROR; 643 | } 644 | } 645 | 646 | c = ngx_array_push(gcf->proxies); 647 | if (c == NULL) { 648 | return NGX_CONF_ERROR; 649 | } 650 | 651 | *c = cidr; 652 | 653 | return NGX_CONF_OK; 654 | } 655 | 656 | 657 | static ngx_int_t 658 | ngx_http_geoip2_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) 659 | { 660 | ngx_int_t rc; 661 | 662 | if (ngx_strcmp(net->data, "255.255.255.255") == 0) { 663 | cidr->family = AF_INET; 664 | cidr->u.in.addr = 0xffffffff; 665 | cidr->u.in.mask = 0xffffffff; 666 | 667 | return NGX_OK; 668 | } 669 | 670 | rc = ngx_ptocidr(net, cidr); 671 | 672 | if (rc == NGX_ERROR) { 673 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 674 | "invalid network \"%V\"", net); 675 | return NGX_ERROR; 676 | } 677 | 678 | if (rc == NGX_DONE) { 679 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 680 | "low address bits of %V are meaningless", net); 681 | } 682 | 683 | return NGX_OK; 684 | } 685 | 686 | 687 | static void 688 | ngx_http_geoip2_cleanup(void *data) 689 | { 690 | ngx_http_geoip2_conf_t *gcf = data; 691 | ngx_queue_t *q; 692 | ngx_http_geoip2_db_t *database; 693 | 694 | while (!ngx_queue_empty(&gcf->databases)) { 695 | q = ngx_queue_head(&gcf->databases); 696 | ngx_queue_remove(q); 697 | database = ngx_queue_data(q, ngx_http_geoip2_db_t, queue); 698 | MMDB_close(&database->mmdb); 699 | } 700 | } 701 | 702 | 703 | static ngx_int_t 704 | ngx_http_geoip2_log_handler(ngx_http_request_t *r) 705 | { 706 | int status; 707 | MMDB_s tmpdb; 708 | ngx_queue_t *q; 709 | ngx_file_info_t fi; 710 | ngx_http_geoip2_db_t *database; 711 | ngx_http_geoip2_conf_t *gcf; 712 | 713 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 714 | "geoip2 http log handler"); 715 | 716 | gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip2_module); 717 | 718 | if (ngx_queue_empty(&gcf->databases)) { 719 | return NGX_OK; 720 | } 721 | 722 | for (q = ngx_queue_head(&gcf->databases); 723 | q != ngx_queue_sentinel(&gcf->databases); 724 | q = ngx_queue_next(q)) 725 | { 726 | database = ngx_queue_data(q, ngx_http_geoip2_db_t, queue); 727 | if (database->check_interval == 0) { 728 | continue; 729 | } 730 | 731 | if ((database->last_check + database->check_interval) 732 | > ngx_time()) 733 | { 734 | continue; 735 | } 736 | 737 | database->last_check = ngx_time(); 738 | 739 | if (ngx_file_info(database->mmdb.filename, &fi) == NGX_FILE_ERROR) { 740 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, 741 | ngx_file_info_n " \"%s\" failed", 742 | database->mmdb.filename); 743 | 744 | continue; 745 | } 746 | 747 | if (ngx_file_mtime(&fi) <= database->last_change) { 748 | continue; 749 | } 750 | 751 | /* do the reload */ 752 | 753 | ngx_memzero(&tmpdb, sizeof(MMDB_s)); 754 | status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb); 755 | 756 | if (status != MMDB_SUCCESS) { 757 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 758 | "MMDB_open(\"%s\") failed to reload - %s", 759 | database->mmdb.filename, MMDB_strerror(status)); 760 | 761 | continue; 762 | } 763 | 764 | database->last_change = ngx_file_mtime(&fi); 765 | MMDB_close(&database->mmdb); 766 | database->mmdb = tmpdb; 767 | 768 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 769 | "Reload MMDB \"%s\"", 770 | database->mmdb.filename); 771 | } 772 | 773 | return NGX_OK; 774 | } 775 | 776 | 777 | static ngx_int_t 778 | ngx_http_geoip2_init(ngx_conf_t *cf) 779 | { 780 | ngx_http_handler_pt *h; 781 | ngx_http_core_main_conf_t *cmcf; 782 | 783 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 784 | 785 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); 786 | if (h == NULL) { 787 | return NGX_ERROR; 788 | } 789 | 790 | *h = ngx_http_geoip2_log_handler; 791 | 792 | return NGX_OK; 793 | } 794 | -------------------------------------------------------------------------------- /ngx_http_geoip2_module/ngx_stream_geoip2_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Lee Valentine 3 | * Copyright (C) Andrei Belov 4 | * 5 | * Based on nginx's 'ngx_stream_geoip_module.c' by Igor Sysoev 6 | */ 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | 16 | typedef struct { 17 | MMDB_s mmdb; 18 | MMDB_lookup_result_s result; 19 | time_t last_check; 20 | time_t last_change; 21 | time_t check_interval; 22 | #if (NGX_HAVE_INET6) 23 | uint8_t address[16]; 24 | #else 25 | unsigned long address; 26 | #endif 27 | ngx_queue_t queue; 28 | } ngx_stream_geoip2_db_t; 29 | 30 | typedef struct { 31 | ngx_queue_t databases; 32 | } ngx_stream_geoip2_conf_t; 33 | 34 | typedef struct { 35 | ngx_stream_geoip2_db_t *database; 36 | const char **lookup; 37 | ngx_str_t default_value; 38 | ngx_stream_complex_value_t source; 39 | } ngx_stream_geoip2_ctx_t; 40 | 41 | typedef struct { 42 | ngx_stream_geoip2_db_t *database; 43 | ngx_str_t metavalue; 44 | } ngx_stream_geoip2_metadata_t; 45 | 46 | 47 | static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s, 48 | ngx_stream_variable_value_t *v, uintptr_t data); 49 | static ngx_int_t ngx_stream_geoip2_metadata(ngx_stream_session_t *s, 50 | ngx_stream_variable_value_t *v, uintptr_t data); 51 | static void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf); 52 | static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, 53 | void *conf); 54 | static char *ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, 55 | void *conf); 56 | static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, 57 | void *conf); 58 | static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, 59 | void *conf); 60 | static char *ngx_stream_geoip2_add_variable_geodata(ngx_conf_t *cf, 61 | ngx_stream_geoip2_db_t *database); 62 | static char *ngx_stream_geoip2_add_variable_metadata(ngx_conf_t *cf, 63 | ngx_stream_geoip2_db_t *database); 64 | static void ngx_stream_geoip2_cleanup(void *data); 65 | static ngx_int_t ngx_stream_geoip2_init(ngx_conf_t *cf); 66 | 67 | 68 | #define FORMAT(fmt, ...) do { \ 69 | p = ngx_palloc(s->connection->pool, NGX_OFF_T_LEN); \ 70 | if (p == NULL) { \ 71 | return NGX_ERROR; \ 72 | } \ 73 | v->len = ngx_sprintf(p, fmt, __VA_ARGS__) - p; \ 74 | v->data = p; \ 75 | } while (0) 76 | 77 | static ngx_command_t ngx_stream_geoip2_commands[] = { 78 | 79 | { ngx_string("geoip2"), 80 | NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, 81 | ngx_stream_geoip2, 82 | NGX_STREAM_MAIN_CONF_OFFSET, 83 | 0, 84 | NULL }, 85 | 86 | ngx_null_command 87 | }; 88 | 89 | 90 | static ngx_stream_module_t ngx_stream_geoip2_module_ctx = { 91 | NULL, /* preconfiguration */ 92 | ngx_stream_geoip2_init, /* postconfiguration */ 93 | 94 | ngx_stream_geoip2_create_conf, /* create main configuration */ 95 | NULL, /* init main configuration */ 96 | 97 | NULL, /* create server configuration */ 98 | NULL /* merge server configuration */ 99 | }; 100 | 101 | 102 | ngx_module_t ngx_stream_geoip2_module = { 103 | NGX_MODULE_V1, 104 | &ngx_stream_geoip2_module_ctx, /* module context */ 105 | ngx_stream_geoip2_commands, /* module directives */ 106 | NGX_STREAM_MODULE, /* module type */ 107 | NULL, /* init master */ 108 | NULL, /* init module */ 109 | NULL, /* init process */ 110 | NULL, /* init thread */ 111 | NULL, /* exit thread */ 112 | NULL, /* exit process */ 113 | NULL, /* exit master */ 114 | NGX_MODULE_V1_PADDING 115 | }; 116 | 117 | 118 | static ngx_int_t 119 | ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, 120 | uintptr_t data) 121 | { 122 | int mmdb_error; 123 | u_char *p; 124 | ngx_str_t val; 125 | ngx_addr_t addr; 126 | MMDB_entry_data_s entry_data; 127 | ngx_stream_geoip2_ctx_t *geoip2 = (ngx_stream_geoip2_ctx_t *) data; 128 | ngx_stream_geoip2_db_t *database = geoip2->database; 129 | 130 | #if (NGX_HAVE_INET6) 131 | uint8_t address[16], *addressp = address; 132 | #else 133 | unsigned long address; 134 | #endif 135 | 136 | if (geoip2->source.value.len > 0) { 137 | if (ngx_stream_complex_value(s, &geoip2->source, &val) != NGX_OK) { 138 | goto not_found; 139 | } 140 | 141 | if (ngx_parse_addr(s->connection->pool, &addr, val.data, val.len) != NGX_OK) { 142 | goto not_found; 143 | } 144 | } else { 145 | addr.sockaddr = s->connection->sockaddr; 146 | addr.socklen = s->connection->socklen; 147 | } 148 | 149 | switch (addr.sockaddr->sa_family) { 150 | case AF_INET: 151 | #if (NGX_HAVE_INET6) 152 | ngx_memset(addressp, 0, 12); 153 | ngx_memcpy(addressp + 12, &((struct sockaddr_in *) 154 | addr.sockaddr)->sin_addr.s_addr, 4); 155 | break; 156 | 157 | case AF_INET6: 158 | ngx_memcpy(addressp, &((struct sockaddr_in6 *) 159 | addr.sockaddr)->sin6_addr.s6_addr, 16); 160 | #else 161 | address = ((struct sockaddr_in *)addr.sockaddr)->sin_addr.s_addr; 162 | #endif 163 | break; 164 | 165 | default: 166 | goto not_found; 167 | } 168 | 169 | #if (NGX_HAVE_INET6) 170 | if (ngx_memcmp(&address, &database->address, sizeof(address)) != 0) { 171 | #else 172 | if (address != database->address) { 173 | #endif 174 | memcpy(&database->address, &address, sizeof(address)); 175 | database->result = MMDB_lookup_sockaddr(&database->mmdb, 176 | addr.sockaddr, &mmdb_error); 177 | 178 | if (mmdb_error != MMDB_SUCCESS) { 179 | goto not_found; 180 | } 181 | } 182 | 183 | if (!database->result.found_entry 184 | || MMDB_aget_value(&database->result.entry, &entry_data, geoip2->lookup) 185 | != MMDB_SUCCESS) 186 | { 187 | goto not_found; 188 | } 189 | 190 | if (!entry_data.has_data) { 191 | goto not_found; 192 | } 193 | 194 | switch (entry_data.type) { 195 | case MMDB_DATA_TYPE_BOOLEAN: 196 | FORMAT("%d", entry_data.boolean); 197 | break; 198 | case MMDB_DATA_TYPE_UTF8_STRING: 199 | v->len = entry_data.data_size; 200 | v->data = ngx_pnalloc(s->connection->pool, v->len); 201 | if (v->data == NULL) { 202 | return NGX_ERROR; 203 | } 204 | ngx_memcpy(v->data, (u_char *) entry_data.utf8_string, v->len); 205 | break; 206 | case MMDB_DATA_TYPE_BYTES: 207 | v->len = entry_data.data_size; 208 | v->data = ngx_pnalloc(s->connection->pool, v->len); 209 | if (v->data == NULL) { 210 | return NGX_ERROR; 211 | } 212 | ngx_memcpy(v->data, (u_char *) entry_data.bytes, v->len); 213 | break; 214 | case MMDB_DATA_TYPE_FLOAT: 215 | FORMAT("%.5f", entry_data.float_value); 216 | break; 217 | case MMDB_DATA_TYPE_DOUBLE: 218 | FORMAT("%.5f", entry_data.double_value); 219 | break; 220 | case MMDB_DATA_TYPE_UINT16: 221 | FORMAT("%uD", entry_data.uint16); 222 | break; 223 | case MMDB_DATA_TYPE_UINT32: 224 | FORMAT("%uD", entry_data.uint32); 225 | break; 226 | case MMDB_DATA_TYPE_INT32: 227 | FORMAT("%D", entry_data.int32); 228 | break; 229 | case MMDB_DATA_TYPE_UINT64: 230 | FORMAT("%uL", entry_data.uint64); 231 | break; 232 | case MMDB_DATA_TYPE_UINT128: ; 233 | #if MMDB_UINT128_IS_BYTE_ARRAY 234 | uint8_t *val = (uint8_t *) entry_data.uint128; 235 | FORMAT("0x%02x%02x%02x%02x%02x%02x%02x%02x" 236 | "%02x%02x%02x%02x%02x%02x%02x%02x", 237 | val[0], val[1], val[2], val[3], 238 | val[4], val[5], val[6], val[7], 239 | val[8], val[9], val[10], val[11], 240 | val[12], val[13], val[14], val[15]); 241 | #else 242 | mmdb_uint128_t val = entry_data.uint128; 243 | FORMAT("0x%016uxL%016uxL", 244 | (uint64_t) (val >> 64), (uint64_t) val); 245 | #endif 246 | break; 247 | default: 248 | goto not_found; 249 | } 250 | 251 | v->valid = 1; 252 | v->no_cacheable = 0; 253 | v->not_found = 0; 254 | 255 | return NGX_OK; 256 | 257 | not_found: 258 | if (geoip2->default_value.len > 0) { 259 | v->data = geoip2->default_value.data; 260 | v->len = geoip2->default_value.len; 261 | 262 | v->valid = 1; 263 | v->no_cacheable = 0; 264 | v->not_found = 0; 265 | 266 | return NGX_OK; 267 | } 268 | 269 | v->not_found = 1; 270 | 271 | return NGX_OK; 272 | } 273 | 274 | 275 | static ngx_int_t 276 | ngx_stream_geoip2_metadata(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, 277 | uintptr_t data) 278 | { 279 | ngx_stream_geoip2_metadata_t *metadata = (ngx_stream_geoip2_metadata_t *) data; 280 | ngx_stream_geoip2_db_t *database = metadata->database; 281 | u_char *p; 282 | 283 | if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) { 284 | FORMAT("%uL", database->mmdb.metadata.build_epoch); 285 | } else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) { 286 | FORMAT("%T", database->last_check); 287 | } else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) { 288 | FORMAT("%T", database->last_change); 289 | } else { 290 | v->not_found = 1; 291 | return NGX_OK; 292 | } 293 | 294 | v->valid = 1; 295 | v->no_cacheable = 0; 296 | v->not_found = 0; 297 | 298 | return NGX_OK; 299 | } 300 | 301 | 302 | static void * 303 | ngx_stream_geoip2_create_conf(ngx_conf_t *cf) 304 | { 305 | ngx_pool_cleanup_t *cln; 306 | ngx_stream_geoip2_conf_t *conf; 307 | 308 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_conf_t)); 309 | if (conf == NULL) { 310 | return NULL; 311 | } 312 | 313 | cln = ngx_pool_cleanup_add(cf->pool, 0); 314 | if (cln == NULL) { 315 | return NULL; 316 | } 317 | 318 | ngx_queue_init(&conf->databases); 319 | 320 | cln->handler = ngx_stream_geoip2_cleanup; 321 | cln->data = conf; 322 | 323 | return conf; 324 | } 325 | 326 | 327 | static char * 328 | ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 329 | { 330 | int status; 331 | char *rv; 332 | ngx_str_t *value; 333 | ngx_conf_t save; 334 | ngx_stream_geoip2_db_t *database; 335 | ngx_stream_geoip2_conf_t *gcf = conf; 336 | ngx_queue_t *q; 337 | 338 | value = cf->args->elts; 339 | 340 | if (value[1].data && value[1].data[0] != '/') { 341 | if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { 342 | return NGX_CONF_ERROR; 343 | } 344 | } 345 | 346 | if (!ngx_queue_empty(&gcf->databases)) { 347 | for (q = ngx_queue_head(&gcf->databases); 348 | q != ngx_queue_sentinel(&gcf->databases); 349 | q = ngx_queue_next(q)) 350 | { 351 | database = ngx_queue_data(q, ngx_stream_geoip2_db_t, queue); 352 | if (ngx_strcmp(value[1].data, database->mmdb.filename) == 0) { 353 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 354 | "Duplicate GeoIP2 mmdb - %V", &value[1]); 355 | return NGX_CONF_ERROR; 356 | } 357 | } 358 | } 359 | 360 | database = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_db_t)); 361 | if (database == NULL) { 362 | return NGX_CONF_ERROR; 363 | } 364 | 365 | ngx_queue_insert_tail(&gcf->databases, &database->queue); 366 | database->last_check = database->last_change = ngx_time(); 367 | 368 | status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb); 369 | 370 | if (status != MMDB_SUCCESS) { 371 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 372 | "MMDB_open(\"%V\") failed - %s", &value[1], 373 | MMDB_strerror(status)); 374 | return NGX_CONF_ERROR; 375 | } 376 | 377 | save = *cf; 378 | cf->handler = ngx_stream_geoip2_parse_config; 379 | cf->handler_conf = (void *) database; 380 | 381 | rv = ngx_conf_parse(cf, NULL); 382 | *cf = save; 383 | return rv; 384 | } 385 | 386 | 387 | static char * 388 | ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 389 | { 390 | ngx_stream_geoip2_db_t *database; 391 | ngx_str_t *value; 392 | time_t interval; 393 | 394 | value = cf->args->elts; 395 | 396 | if (value[0].data[0] == '$') { 397 | return ngx_stream_geoip2_add_variable(cf, dummy, conf); 398 | } 399 | 400 | if (value[0].len == 11 401 | && ngx_strncmp(value[0].data, "auto_reload", 11) == 0) { 402 | if ((int) cf->args->nelts != 2) { 403 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 404 | "invalid number of arguments for auto_reload"); 405 | return NGX_CONF_ERROR; 406 | } 407 | 408 | interval = ngx_parse_time(&value[1], true); 409 | 410 | if (interval == (time_t) NGX_ERROR) { 411 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 412 | "invalid interval for auto_reload \"%V\"", 413 | value[1]); 414 | return NGX_CONF_ERROR; 415 | } 416 | 417 | 418 | database = (ngx_stream_geoip2_db_t *) conf; 419 | database->check_interval = interval; 420 | return NGX_CONF_OK; 421 | } 422 | 423 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 424 | "invalid setting \"%V\"", &value[0]); 425 | return NGX_CONF_ERROR; 426 | } 427 | 428 | 429 | static char * 430 | ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 431 | { 432 | ngx_stream_geoip2_db_t *database; 433 | ngx_str_t *value; 434 | int nelts; 435 | 436 | value = cf->args->elts; 437 | 438 | if (value[0].data[0] != '$') { 439 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 440 | "invalid variable name \"%V\"", &value[0]); 441 | return NGX_CONF_ERROR; 442 | } 443 | 444 | value[0].len--; 445 | value[0].data++; 446 | 447 | nelts = (int) cf->args->nelts; 448 | database = (ngx_stream_geoip2_db_t *) conf; 449 | 450 | if (nelts > 0 && value[1].len == 8 && ngx_strncmp(value[1].data, "metadata", 8) == 0) { 451 | return ngx_stream_geoip2_add_variable_metadata(cf, database); 452 | } 453 | 454 | return ngx_stream_geoip2_add_variable_geodata(cf, database); 455 | } 456 | 457 | 458 | static char * 459 | ngx_stream_geoip2_add_variable_metadata(ngx_conf_t *cf, ngx_stream_geoip2_db_t *database) 460 | { 461 | ngx_stream_geoip2_metadata_t *metadata; 462 | ngx_str_t *value, name; 463 | ngx_stream_variable_t *var; 464 | 465 | metadata = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_metadata_t)); 466 | if (metadata == NULL) { 467 | return NGX_CONF_ERROR; 468 | } 469 | 470 | value = cf->args->elts; 471 | name = value[0]; 472 | 473 | metadata->database = database; 474 | metadata->metavalue = value[2]; 475 | 476 | var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); 477 | if (var == NULL) { 478 | return NGX_CONF_ERROR; 479 | } 480 | 481 | var->get_handler = ngx_stream_geoip2_metadata; 482 | var->data = (uintptr_t) metadata; 483 | 484 | return NGX_CONF_OK; 485 | } 486 | 487 | 488 | static char * 489 | ngx_stream_geoip2_add_variable_geodata(ngx_conf_t *cf, ngx_stream_geoip2_db_t *database) 490 | { 491 | ngx_stream_geoip2_ctx_t *geoip2; 492 | ngx_stream_compile_complex_value_t ccv; 493 | ngx_str_t *value, name, source; 494 | ngx_stream_variable_t *var; 495 | int i, nelts, idx; 496 | 497 | geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_ctx_t)); 498 | if (geoip2 == NULL) { 499 | return NGX_CONF_ERROR; 500 | } 501 | 502 | geoip2->database = database; 503 | ngx_str_null(&source); 504 | 505 | value = cf->args->elts; 506 | name = value[0]; 507 | 508 | nelts = (int) cf->args->nelts; 509 | idx = 1; 510 | 511 | if (nelts > idx) { 512 | for (i = idx; i < nelts; i++) { 513 | if (ngx_strnstr(value[idx].data, "=", value[idx].len) == NULL) { 514 | break; 515 | } 516 | 517 | if (value[idx].len > 8 && ngx_strncmp(value[idx].data, "default=", 8) == 0) { 518 | if (geoip2->default_value.len > 0) { 519 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 520 | "default has already been declared for \"$%V\"", &name); 521 | return NGX_CONF_ERROR; 522 | } 523 | 524 | geoip2->default_value.len = value[idx].len - 8; 525 | geoip2->default_value.data = value[idx].data + 8; 526 | 527 | } else if (value[idx].len > 7 && ngx_strncmp(value[idx].data, "source=", 7) == 0) { 528 | if (source.len > 0) { 529 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 530 | "source has already been declared for \"$%V\"", &name); 531 | return NGX_CONF_ERROR; 532 | } 533 | 534 | source.len = value[idx].len - 7; 535 | source.data = value[idx].data + 7; 536 | 537 | if (source.data[0] != '$') { 538 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 539 | "invalid source variable name \"%V\"", &source); 540 | return NGX_CONF_ERROR; 541 | } 542 | 543 | ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); 544 | ccv.cf = cf; 545 | ccv.value = &source; 546 | ccv.complex_value = &geoip2->source; 547 | 548 | if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { 549 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 550 | "unable to compile \"%V\" for \"$%V\"", &source, &name); 551 | return NGX_CONF_ERROR; 552 | } 553 | 554 | } else { 555 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 556 | "invalid setting \"%V\" for \"$%V\"", &value[idx], &name); 557 | return NGX_CONF_ERROR; 558 | } 559 | 560 | idx++; 561 | } 562 | } 563 | 564 | var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); 565 | if (var == NULL) { 566 | return NGX_CONF_ERROR; 567 | } 568 | 569 | geoip2->lookup = ngx_pcalloc(cf->pool, 570 | sizeof(const char *) * (cf->args->nelts - (idx - 1))); 571 | 572 | if (geoip2->lookup == NULL) { 573 | return NGX_CONF_ERROR; 574 | } 575 | 576 | for (i = idx; i < nelts; i++) { 577 | geoip2->lookup[i - idx] = (char *) value[i].data; 578 | } 579 | geoip2->lookup[i - idx] = NULL; 580 | 581 | var->get_handler = ngx_stream_geoip2_variable; 582 | var->data = (uintptr_t) geoip2; 583 | 584 | return NGX_CONF_OK; 585 | } 586 | 587 | 588 | static void 589 | ngx_stream_geoip2_cleanup(void *data) 590 | { 591 | ngx_queue_t *q; 592 | ngx_stream_geoip2_db_t *database; 593 | ngx_stream_geoip2_conf_t *gcf = data; 594 | 595 | while (!ngx_queue_empty(&gcf->databases)) { 596 | q = ngx_queue_head(&gcf->databases); 597 | ngx_queue_remove(q); 598 | database = ngx_queue_data(q, ngx_stream_geoip2_db_t, queue); 599 | MMDB_close(&database->mmdb); 600 | } 601 | } 602 | 603 | 604 | static ngx_int_t 605 | ngx_stream_geoip2_log_handler(ngx_stream_session_t *s) 606 | { 607 | int status; 608 | MMDB_s tmpdb; 609 | ngx_queue_t *q; 610 | ngx_file_info_t fi; 611 | ngx_stream_geoip2_db_t *database; 612 | ngx_stream_geoip2_conf_t *gcf; 613 | 614 | ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, 615 | "geoip2 stream log handler"); 616 | 617 | gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip2_module); 618 | 619 | if (ngx_queue_empty(&gcf->databases)) { 620 | return NGX_OK; 621 | } 622 | 623 | for (q = ngx_queue_head(&gcf->databases); 624 | q != ngx_queue_sentinel(&gcf->databases); 625 | q = ngx_queue_next(q)) 626 | { 627 | database = ngx_queue_data(q, ngx_stream_geoip2_db_t, queue); 628 | if (database->check_interval == 0) { 629 | continue; 630 | } 631 | 632 | if ((database->last_check + database->check_interval) 633 | > ngx_time()) 634 | { 635 | continue; 636 | } 637 | 638 | database->last_check = ngx_time(); 639 | 640 | if (ngx_file_info(database->mmdb.filename, &fi) == NGX_FILE_ERROR) { 641 | ngx_log_error(NGX_LOG_EMERG, s->connection->log, ngx_errno, 642 | ngx_file_info_n " \"%s\" failed", 643 | database->mmdb.filename); 644 | 645 | continue; 646 | } 647 | 648 | if (ngx_file_mtime(&fi) <= database->last_change) { 649 | continue; 650 | } 651 | 652 | /* do the reload */ 653 | 654 | ngx_memzero(&tmpdb, sizeof(MMDB_s)); 655 | status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb); 656 | 657 | if (status != MMDB_SUCCESS) { 658 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 659 | "MMDB_open(\"%s\") failed to reload - %s", 660 | database->mmdb.filename, MMDB_strerror(status)); 661 | 662 | continue; 663 | } 664 | 665 | database->last_change = ngx_file_mtime(&fi); 666 | MMDB_close(&database->mmdb); 667 | database->mmdb = tmpdb; 668 | 669 | ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, 670 | "Reload MMDB \"%s\"", 671 | database->mmdb.filename); 672 | } 673 | 674 | return NGX_OK; 675 | } 676 | 677 | 678 | static ngx_int_t 679 | ngx_stream_geoip2_init(ngx_conf_t *cf) 680 | { 681 | ngx_stream_handler_pt *h; 682 | ngx_stream_core_main_conf_t *cmcf; 683 | 684 | cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); 685 | 686 | h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers); 687 | if (h == NULL) { 688 | return NGX_ERROR; 689 | } 690 | 691 | *h = ngx_stream_geoip2_log_handler; 692 | 693 | return NGX_OK; 694 | } 695 | -------------------------------------------------------------------------------- /tests/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7.2.1511 2 | 3 | RUN yum -y update 4 | RUN yum -y install yum-plugin-ovl 5 | RUN yum -y install gcc gcc-c++ autoconf automake make libxslt-devel build-essential 6 | RUN yum -y install zlib zlib-devel openssl* pcre* wget lua-devel gd-devel 7 | RUN yum -y install git htop 8 | 9 | MAINTAINER ar414 root@ar414.com 10 | 11 | ADD http://nginx.org/download/nginx-1.14.2.tar.gz /tmp/nginx-1.14.2.tar.gz 12 | 13 | WORKDIR /tmp 14 | 15 | # download ngx-geoip2-module Related Data 16 | WORKDIR /tmp 17 | RUN git clone https://github.com/ar414-com/nginx-geoip2.git 18 | # install libmaxminddb 19 | WORKDIR /tmp/nginx-geoip2 20 | RUN mkdir /tmp/nginx-geoip2/libmaxminddb && tar xf libmaxminddb-1.3.2.tar.gz -C /tmp/nginx-geoip2/libmaxminddb --strip-components 1 21 | WORKDIR /tmp/nginx-geoip2/libmaxminddb 22 | RUN ./configure && make && make install 23 | RUN echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf 24 | RUN ldconfig 25 | # Initialize geoip data path 26 | WORKDIR /tmp/nginx-geoip2 27 | RUN mv ./ngx_http_geoip2_module /usr/local/src/ 28 | RUN tar -zxvf GeoLite2-City_20200519.tar.gz 29 | RUN mkdir -p /usr/local/share/GeoIP/ 30 | RUN cp /tmp/nginx-geoip2/GeoLite2-City_20200519/GeoLite2-City.mmdb /usr/local/share/GeoIP/ 31 | RUN tar -zxvf GeoLite2-Country_20200519.tar.gz 32 | RUN cp /tmp/nginx-geoip2/GeoLite2-Country_20200519/GeoLite2-Country.mmdb /usr/local/share/GeoIP/ 33 | # install nginx 34 | WORKDIR /tmp 35 | RUN useradd -M -s /sbin/nologin www 36 | RUN mkdir /tmp/nginx && tar xf nginx-1.14.2.tar.gz -C /tmp/nginx --strip-components 1 37 | RUN mkdir -p /usr/local/nginx 38 | WORKDIR /tmp/nginx 39 | RUN ./configure --user=www --group=www --prefix=/usr/local/nginx \ 40 | --add-module=/usr/local/src/ngx_http_geoip2_module \ 41 | && make && make install 42 | 43 | RUN /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf 44 | RUN ln -s /usr/local/nginx/sbin/* /usr/local/sbin/ 45 | 46 | RUN rm -rf /usr/local/nginx/conf/nginx.conf 47 | RUN cp /tmp/nginx-geoip2/tests/nginx.conf /usr/local/nginx/conf/ 48 | RUN mv /tmp/nginx-geoip2/tests/html/a /usr/local/nginx/html/ 49 | RUN mv /tmp/nginx-geoip2/tests/html/b /usr/local/nginx/html/ 50 | 51 | EXPOSE 80 443 52 | 53 | CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"] 54 | -------------------------------------------------------------------------------- /tests/html/a/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AHost 6 | 7 | 8 |

9 | AAAA Host 10 |

11 | 12 | -------------------------------------------------------------------------------- /tests/html/b/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BHost 6 | 7 | 8 |

9 | BBBB Host 10 |

11 | 12 | -------------------------------------------------------------------------------- /tests/nginx.conf: -------------------------------------------------------------------------------- 1 | #user nobody; 2 | worker_processes 1; 3 | 4 | #error_log logs/error.log; 5 | #error_log logs/error.log notice; 6 | #error_log logs/error.log info; 7 | 8 | #pid logs/nginx.pid; 9 | 10 | 11 | events { 12 | worker_connections 1024; 13 | } 14 | 15 | 16 | http { 17 | include mime.types; 18 | default_type application/octet-stream; 19 | 20 | #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 21 | # '$status $body_bytes_sent "$http_referer" ' 22 | # '"$http_user_agent" "$http_x_forwarded_for"'; 23 | 24 | #access_log logs/access.log main; 25 | 26 | sendfile on; 27 | #tcp_nopush on; 28 | 29 | #keepalive_timeout 0; 30 | keepalive_timeout 65; 31 | 32 | #gzip on; 33 | 34 | 35 | geoip2 /usr/local/share/GeoIP/GeoLite2-Country.mmdb { 36 | $geoip2_country_code country iso_code; 37 | } 38 | 39 | map $geoip2_country_code $is_jp_country { 40 | default no; 41 | JP yes; 42 | } 43 | 44 | server { 45 | listen 80; 46 | server_name localhost; 47 | 48 | #charset koi8-r; 49 | 50 | #access_log logs/host.access.log main; 51 | 52 | add_header country $geoip2_country_code; 53 | 54 | location / { 55 | set $rootpath html/a; 56 | if ($is_jp_country = no) { 57 | set $rootpath html/b; 58 | } 59 | 60 | add_header rootpath $rootpath; 61 | add_header country $geoip2_country_code; 62 | root $rootpath; 63 | 64 | index index.html index.htm; 65 | 66 | } 67 | 68 | #error_page 404 /404.html; 69 | 70 | # redirect server error pages to the static page /50x.html 71 | # 72 | error_page 500 502 503 504 /50x.html; 73 | location = /50x.html { 74 | root html; 75 | } 76 | 77 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 78 | # 79 | #location ~ \.php$ { 80 | # proxy_pass http://127.0.0.1; 81 | #} 82 | 83 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 84 | # 85 | #location ~ \.php$ { 86 | # root html; 87 | # fastcgi_pass 127.0.0.1:9000; 88 | # fastcgi_index index.php; 89 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 90 | # include fastcgi_params; 91 | #} 92 | 93 | # deny access to .htaccess files, if Apache's document root 94 | # concurs with nginx's one 95 | # 96 | #location ~ /\.ht { 97 | # deny all; 98 | #} 99 | } 100 | 101 | 102 | # another virtual host using mix of IP-, name-, and port-based configuration 103 | # 104 | #server { 105 | # listen 8000; 106 | # listen somename:8080; 107 | # server_name somename alias another.alias; 108 | 109 | # location / { 110 | # root html; 111 | # index index.html index.htm; 112 | # } 113 | #} 114 | 115 | 116 | # HTTPS server 117 | # 118 | #server { 119 | # listen 443 ssl; 120 | # server_name localhost; 121 | 122 | # ssl_certificate cert.pem; 123 | # ssl_certificate_key cert.key; 124 | 125 | # ssl_session_cache shared:SSL:1m; 126 | # ssl_session_timeout 5m; 127 | 128 | # ssl_ciphers HIGH:!aNULL:!MD5; 129 | # ssl_prefer_server_ciphers on; 130 | 131 | # location / { 132 | # root html; 133 | # index index.html index.htm; 134 | # } 135 | #} 136 | 137 | } 138 | --------------------------------------------------------------------------------