├── LICENSE ├── README.md ├── config ├── ngx_http_ip_blocker_module.c ├── ngx_http_ip_blocker_rwlock.c └── ngx_ip_blocker_shm.h /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Tom Thorogood. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Tom Thorogood nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | ---- Portions of the source code are also covered by the following license: ---- 27 | 28 | Copyright (c) 2012 The Go Authors. All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without 31 | modification, are permitted provided that the following conditions are 32 | met: 33 | 34 | * Redistributions of source code must retain the above copyright 35 | notice, this list of conditions and the following disclaimer. 36 | * Redistributions in binary form must reproduce the above 37 | copyright notice, this list of conditions and the following disclaimer 38 | in the documentation and/or other materials provided with the 39 | distribution. 40 | * Neither the name of Google Inc. nor the names of its 41 | contributors may be used to endorse or promote products derived from 42 | this software without specific prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 45 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 46 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 47 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 48 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 49 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 50 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 51 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 52 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 53 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 54 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-ip-blocker 2 | 3 | nginx-ip-blocker is the other half of 4 | [tmthrgd/ip-blocker-agent](https://github.com/tmthrgd/ip-blocker-agent). 5 | 6 | ## Install 7 | 8 | ``` 9 | wget http://nginx.org/download/nginx-1.9.15.tar.gz 10 | tar -xzvf nginx-1.9.15.tar.gz 11 | cd nginx-1.9.15/ 12 | 13 | # Here we assume Nginx is to be installed under /opt/nginx/. 14 | ./configure --prefix=/opt/nginx \ 15 | --add-module=/path/to/nginx-ip-blocker 16 | 17 | make -j2 18 | make install 19 | ``` 20 | 21 | ## Directives 22 | 23 | ### ip_blocker 24 | 25 | **syntax:** *ip_blocker <name-of-shared-memory> [whitelist code=xxx] | off* 26 | 27 | **default:** *ip_blocker off* 28 | 29 | **context:** *http, server, server if, location, location if* 30 | 31 | Blocks (or whitelists) IP address specified in the named shared memory 32 | (see [tmthrgd/ip-blocker-agent](https://github.com/tmthrgd/ip-blocker-agent)). 33 | 34 | The whitelist flag causes matches to be accepted rather than denied. 35 | 36 | The code flag allows the HTTP code returned for a match to be specified. 37 | 38 | The directive may be specified multiple times to specify multiple blocklists. The exact behaviour depends 39 | on the value of the [satisfy directive](http://nginx.org/en/docs/http/ngx_http_core_module.html#satisfy). 40 | 41 | ## License 42 | 43 | Unless otherwise noted, the nginx-ip-blocker source files are distributed under the Modified BSD License 44 | found in the LICENSE file. 45 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Tom Thorogood. All rights reserved. 2 | # Use of this source code is governed by a 3 | # Modified BSD License license that can be found in 4 | # the LICENSE file. 5 | 6 | ngx_addon_name=ngx_http_ip_blocker 7 | 8 | ngx_module_type=HTTP 9 | ngx_module_name=ngx_http_ip_blocker_module 10 | ngx_module_incs= 11 | ngx_module_deps= 12 | ngx_module_srcs="$ngx_addon_dir/ngx_http_ip_blocker_module.c $ngx_addon_dir/ngx_http_ip_blocker_rwlock.c" 13 | ngx_module_libs="-lrt" 14 | 15 | . auto/module 16 | 17 | # -*- mode: shell;-*- 18 | -------------------------------------------------------------------------------- /ngx_http_ip_blocker_module.c: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Tom Thorogood. All rights reserved. 2 | // Use of this source code is governed by a 3 | // Modified BSD License license that can be found in 4 | // the LICENSE file. 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ngx_ip_blocker_shm.h" 11 | 12 | #include // For O_* constants 13 | #include // For mode constants 14 | #include // For shm_* 15 | 16 | #if NGX_PTR_SIZE == 8 17 | # define NGX_HTTP_IP_BLOCKER_VERSION 0x80000001 18 | #else 19 | # define NGX_HTTP_IP_BLOCKER_VERSION 0x00000001 20 | #endif 21 | 22 | typedef struct { 23 | ngx_str_t name; 24 | 25 | ngx_int_t code; 26 | ngx_flag_t whitelist; 27 | } ngx_http_ip_blocker_directive_st; 28 | 29 | typedef struct { 30 | int enabled; 31 | 32 | ngx_array_t *directives; 33 | 34 | ngx_array_t rules; 35 | } ngx_http_ip_blocker_loc_conf_st; 36 | 37 | typedef struct { 38 | ngx_int_t code; 39 | ngx_flag_t whitelist; 40 | 41 | int fd; 42 | 43 | /* fd may have been truncated behind our backs, be warned */ 44 | ngx_ip_blocker_shm_st *addr; 45 | size_t size; 46 | 47 | uint32_t revision; 48 | } ngx_http_ip_blocker_ruleset_st; 49 | 50 | static ngx_int_t ngx_http_ip_blocker_init(ngx_conf_t *cf); 51 | 52 | static char *ngx_http_ip_blocker_set_directive_slot(ngx_conf_t *cf, ngx_command_t *cmd, 53 | void *conf); 54 | 55 | static void *ngx_http_ip_blocker_create_loc_conf(ngx_conf_t *cf); 56 | static char *ngx_http_ip_blocker_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); 57 | 58 | static void ngx_http_ip_blocker_cleanup(void *data); 59 | 60 | static ngx_inline ngx_int_t ngx_http_ip_blocker_remap(ngx_http_ip_blocker_ruleset_st *rule, 61 | ngx_log_t *log); 62 | static ngx_inline ngx_int_t ngx_http_ip_blocker_check_shm(ngx_http_ip_blocker_ruleset_st *rule); 63 | 64 | static ngx_int_t ngx_http_ip_blocker_access_handler(ngx_http_request_t *r); 65 | static ngx_int_t ngx_ip_blocker_process_rule(ngx_http_request_t *r, 66 | ngx_http_ip_blocker_ruleset_st *rule); 67 | 68 | static int ngx_http_ip_blocker_ip4_compare(const void *a, const void *b); 69 | #if NGX_HAVE_INET6 70 | static int ngx_http_ip_blocker_ip6_compare(const void *a, const void *b); 71 | static int ngx_http_ip_blocker_ip6route_compare(const void *a, const void *b); 72 | #endif /* NGX_HAVE_INET6 */ 73 | 74 | ngx_int_t ngx_ip_blocker_rwlock_rlock(ngx_ip_blocker_rwlock_st *rw); 75 | ngx_int_t ngx_ip_blocker_rwlock_runlock(ngx_ip_blocker_rwlock_st *rw); 76 | 77 | static ngx_command_t ngx_http_ip_blocker_module_commands[] = { 78 | { ngx_string("ip_blocker"), 79 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_1MORE, 80 | ngx_http_ip_blocker_set_directive_slot, 81 | NGX_HTTP_LOC_CONF_OFFSET, 82 | offsetof(ngx_http_ip_blocker_loc_conf_st, directives), 83 | NULL }, 84 | 85 | ngx_null_command 86 | }; 87 | 88 | static ngx_http_module_t ngx_http_ip_blocker_module_ctx = { 89 | NULL, /* preconfiguration */ 90 | ngx_http_ip_blocker_init, /* postconfiguration */ 91 | 92 | NULL, /* create main configuration */ 93 | NULL, /* init main configuration */ 94 | 95 | NULL, /* create server configuration */ 96 | NULL, /* merge server configuration */ 97 | 98 | ngx_http_ip_blocker_create_loc_conf, /* create location configuration */ 99 | ngx_http_ip_blocker_merge_loc_conf /* merge location configuration */ 100 | }; 101 | 102 | ngx_module_t ngx_http_ip_blocker_module = { 103 | NGX_MODULE_V1, 104 | &ngx_http_ip_blocker_module_ctx, /* module context */ 105 | ngx_http_ip_blocker_module_commands, /* module directives */ 106 | NGX_HTTP_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 | static ngx_int_t ngx_http_ip_blocker_init(ngx_conf_t *cf) 118 | { 119 | ngx_http_handler_pt *h; 120 | ngx_http_core_main_conf_t *cmcf; 121 | 122 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 123 | 124 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); 125 | if (!h) { 126 | return NGX_ERROR; 127 | } 128 | 129 | *h = ngx_http_ip_blocker_access_handler; 130 | return NGX_OK; 131 | } 132 | 133 | static char *ngx_http_ip_blocker_set_directive_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 134 | { 135 | char *p = conf; 136 | ngx_str_t *value; 137 | ngx_http_ip_blocker_directive_st *dir; 138 | ngx_array_t **a; 139 | ngx_conf_post_t *post; 140 | ngx_uint_t n; 141 | 142 | a = (ngx_array_t **)(p + cmd->offset); 143 | 144 | if (*a == NGX_CONF_UNSET_PTR) { 145 | *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_ip_blocker_directive_st)); 146 | if (!*a) { 147 | return NGX_CONF_ERROR; 148 | } 149 | } 150 | 151 | dir = ngx_array_push(*a); 152 | if (!dir) { 153 | return NGX_CONF_ERROR; 154 | } 155 | 156 | ngx_memzero(dir, sizeof(ngx_http_ip_blocker_directive_st)); 157 | 158 | value = cf->args->elts; 159 | 160 | dir->name = value[1]; 161 | 162 | for (n = 2; n < cf->args->nelts; n++) { 163 | if (ngx_strncmp(value[n].data, "code=", 5) == 0) { 164 | dir->code = ngx_atoi(value[n].data + 5, value[n].len - 5); 165 | if (dir->code == NGX_ERROR) { 166 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 167 | "invalid code \"%V\"", &value[n]); 168 | return NGX_CONF_ERROR; 169 | } 170 | 171 | continue; 172 | } 173 | 174 | if (ngx_strcmp(value[n].data, "whitelist") == 0) { 175 | dir->whitelist = 1; 176 | continue; 177 | } 178 | 179 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[n]); 180 | return NGX_CONF_ERROR; 181 | } 182 | 183 | if (cmd->post) { 184 | post = cmd->post; 185 | return post->post_handler(cf, post, dir); 186 | } 187 | 188 | return NGX_CONF_OK; 189 | } 190 | 191 | static void *ngx_http_ip_blocker_create_loc_conf(ngx_conf_t *cf) 192 | { 193 | ngx_http_ip_blocker_loc_conf_st *conf; 194 | 195 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ip_blocker_loc_conf_st)); 196 | if (!conf) { 197 | return NULL; 198 | } 199 | 200 | /* 201 | * set by ngx_pcalloc(): 202 | * 203 | * conf->enabled = 0; 204 | */ 205 | 206 | conf->directives = NGX_CONF_UNSET_PTR; 207 | return conf; 208 | } 209 | 210 | static char *ngx_http_ip_blocker_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 211 | { 212 | const ngx_http_ip_blocker_loc_conf_st *prev = parent; 213 | ngx_http_ip_blocker_loc_conf_st *conf = child; 214 | ngx_http_ip_blocker_directive_st *dir; 215 | ngx_http_ip_blocker_ruleset_st *rule; 216 | ngx_pool_cleanup_t *cln; 217 | size_t i; 218 | struct stat sb; 219 | 220 | ngx_conf_merge_ptr_value(conf->directives, prev->directives, NULL); 221 | 222 | if (!conf->directives || !conf->directives->nelts) { 223 | return NGX_CONF_OK; 224 | } 225 | 226 | dir = conf->directives->elts; 227 | for (i = 0; i < conf->directives->nelts; i++) { 228 | if (ngx_strcmp(dir[i].name.data, "off") == 0) { 229 | return NGX_CONF_OK; 230 | } 231 | } 232 | 233 | if (ngx_array_init(&conf->rules, cf->pool, conf->directives->nelts, 234 | sizeof(ngx_http_ip_blocker_ruleset_st)) != NGX_OK) { 235 | return NGX_CONF_ERROR; 236 | } 237 | 238 | cln = ngx_pool_cleanup_add(cf->pool, 0); 239 | if (!cln) { 240 | return NGX_CONF_ERROR; 241 | } 242 | 243 | cln->handler = ngx_http_ip_blocker_cleanup; 244 | cln->data = conf; 245 | 246 | conf->enabled = 1; 247 | 248 | for (i = 0; i < conf->directives->nelts; i++) { 249 | rule = ngx_array_push(&conf->rules); 250 | if (!rule) { 251 | return NGX_CONF_ERROR; 252 | } 253 | 254 | ngx_memzero(rule, sizeof(ngx_http_ip_blocker_ruleset_st)); 255 | rule->addr = MAP_FAILED; 256 | 257 | if (dir[i].code) { 258 | rule->code = dir[i].code; 259 | } else { 260 | rule->code = NGX_HTTP_FORBIDDEN; 261 | } 262 | 263 | rule->whitelist = dir[i].whitelist; 264 | 265 | rule->fd = shm_open((const char *)dir[i].name.data, O_RDWR, 0); 266 | if (rule->fd == -1) { 267 | ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "shm_open failed"); 268 | return NGX_CONF_ERROR; 269 | } 270 | 271 | if (fstat(rule->fd, &sb) == -1) { 272 | ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "fstat failed"); 273 | return NGX_CONF_ERROR; 274 | } 275 | 276 | rule->addr = mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, rule->fd, 0); 277 | if (rule->addr == MAP_FAILED) { 278 | ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "mmap failed"); 279 | return NGX_CONF_ERROR; 280 | } 281 | 282 | rule->size = sb.st_size; 283 | 284 | if (rule->size < sizeof(ngx_ip_blocker_shm_st) 285 | || ngx_atomic_fetch_add(&rule->addr->version, 0) 286 | != NGX_HTTP_IP_BLOCKER_VERSION) { 287 | ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "invalid shared memory"); 288 | return NGX_CONF_ERROR; 289 | } 290 | 291 | if (ngx_ip_blocker_rwlock_rlock(&rule->addr->lock) != NGX_OK) { 292 | return NGX_CONF_ERROR; 293 | } 294 | 295 | rule->revision = rule->addr->revision; 296 | 297 | if (fstat(rule->fd, &sb) == -1) { 298 | ngx_ip_blocker_rwlock_runlock(&rule->addr->lock); 299 | 300 | ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "fstat failed"); 301 | return NGX_CONF_ERROR; 302 | } 303 | 304 | if ((size_t)sb.st_size != rule->size) { 305 | /* shm has changed since we mmaped it (unlikely but possible) */ 306 | 307 | /* runlock is called inside of remap iff NGX_ERROR is returned */ 308 | if (ngx_http_ip_blocker_remap(rule, cf->log) != NGX_OK) { 309 | return NGX_CONF_ERROR; 310 | } 311 | } else if (ngx_http_ip_blocker_check_shm(rule) != NGX_OK) { 312 | ngx_ip_blocker_rwlock_runlock(&rule->addr->lock); 313 | 314 | ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "invalid shared memory"); 315 | return NGX_CONF_ERROR; 316 | } 317 | 318 | if (ngx_ip_blocker_rwlock_runlock(&rule->addr->lock) != NGX_OK) { 319 | return NGX_CONF_ERROR; 320 | } 321 | } 322 | 323 | return NGX_CONF_OK; 324 | } 325 | 326 | static void ngx_http_ip_blocker_cleanup(void *data) 327 | { 328 | ngx_http_ip_blocker_loc_conf_st *conf = data; 329 | ngx_http_ip_blocker_ruleset_st *rule; 330 | size_t i; 331 | 332 | rule = conf->rules.elts; 333 | for (i = 0; i < conf->rules.nelts; i++) { 334 | if (rule[i].addr != MAP_FAILED) { 335 | munmap(rule[i].addr, rule[i].size); 336 | 337 | rule[i].addr = MAP_FAILED; 338 | rule[i].size = 0; /* not strictly needed */ 339 | } 340 | 341 | if (rule[i].fd != -1) { 342 | close(rule[i].fd); 343 | } 344 | } 345 | } 346 | 347 | /* rlock must be held before calling remap */ 348 | static ngx_inline ngx_int_t ngx_http_ip_blocker_remap(ngx_http_ip_blocker_ruleset_st *rule, 349 | ngx_log_t *log) 350 | { 351 | ngx_ip_blocker_shm_st *addr; 352 | size_t size; 353 | struct stat sb; 354 | 355 | addr = rule->addr; 356 | size = rule->size; 357 | rule->addr = MAP_FAILED; 358 | rule->size = 0; /* not strictly needed */ 359 | 360 | if (fstat(rule->fd, &sb) == -1) { 361 | ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fstat failed"); 362 | goto error; 363 | } 364 | 365 | rule->addr = mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, rule->fd, 0); 366 | if (rule->addr == MAP_FAILED) { 367 | ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "mmap failed"); 368 | goto error; 369 | } 370 | 371 | rule->size = sb.st_size; 372 | 373 | if (ngx_http_ip_blocker_check_shm(rule) != NGX_OK) { 374 | munmap(rule->addr, rule->size); 375 | rule->addr = MAP_FAILED; 376 | 377 | ngx_log_error(NGX_LOG_EMERG, log, 0, "invalid shared memory"); 378 | goto error; 379 | } 380 | 381 | rule->revision = rule->addr->revision; 382 | 383 | munmap(addr, size); 384 | return NGX_OK; 385 | 386 | error: 387 | if (!rule->size || rule->size >= sizeof(ngx_ip_blocker_shm_st)) { 388 | ngx_ip_blocker_rwlock_runlock(&addr->lock); 389 | } else { 390 | ngx_log_error(NGX_LOG_EMERG, log, 0, "failed to release read lock"); 391 | } 392 | 393 | munmap(addr, size); 394 | return NGX_ERROR; 395 | } 396 | 397 | static ngx_inline ngx_int_t ngx_http_ip_blocker_check_shm(ngx_http_ip_blocker_ruleset_st *rule) 398 | { 399 | if (rule->size < sizeof(ngx_ip_blocker_shm_st) 400 | || rule->size < sizeof(ngx_ip_blocker_shm_st) 401 | + rule->addr->ip4.len + rule->addr->ip6.len + rule->addr->ip6route.len 402 | || (rule->addr->ip4.len 403 | && rule->addr->ip4.base < (ssize_t)sizeof(ngx_ip_blocker_shm_st)) 404 | || (rule->addr->ip6.len 405 | && rule->addr->ip6.base < (ssize_t)sizeof(ngx_ip_blocker_shm_st)) 406 | || (rule->addr->ip6route.len 407 | && rule->addr->ip6route.base < (ssize_t)sizeof(ngx_ip_blocker_shm_st)) 408 | || rule->addr->ip4.base + rule->addr->ip4.len > rule->size 409 | || rule->addr->ip6.base + rule->addr->ip6.len > rule->size 410 | || rule->addr->ip6route.base + rule->addr->ip6route.len > rule->size 411 | || rule->addr->ip4.len % 4 != 0 412 | || rule->addr->ip6.len % 16 != 0 413 | || rule->addr->ip6route.len % 8 != 0) { 414 | return NGX_ERROR; 415 | } 416 | 417 | return NGX_OK; 418 | } 419 | 420 | static ngx_int_t ngx_http_ip_blocker_access_handler(ngx_http_request_t *r) 421 | { 422 | ngx_http_ip_blocker_loc_conf_st *conf; 423 | ngx_http_core_loc_conf_t *clcf; 424 | ngx_http_ip_blocker_ruleset_st *rule; 425 | size_t i; 426 | ngx_int_t rc, out_rc; 427 | 428 | switch (r->connection->sockaddr->sa_family) { 429 | case AF_INET: 430 | #if NGX_HAVE_INET6 431 | case AF_INET6: 432 | #endif /* NGX_HAVE_INET6 */ 433 | break; 434 | default: 435 | return NGX_DECLINED; 436 | } 437 | 438 | conf = ngx_http_get_module_loc_conf(r, ngx_http_ip_blocker_module); 439 | if (!conf || !conf->enabled) { 440 | return NGX_DECLINED; 441 | } 442 | 443 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 444 | 445 | out_rc = NGX_DECLINED; 446 | 447 | rule = conf->rules.elts; 448 | for (i = 0; i < conf->rules.nelts; i++) { 449 | rc = ngx_ip_blocker_process_rule(r, &rule[i]); 450 | 451 | if (rc == NGX_DECLINED) { 452 | continue; 453 | } else if (rc == NGX_ERROR) { 454 | return NGX_ERROR; 455 | } 456 | 457 | if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { 458 | if (rc == NGX_OK) { 459 | continue; 460 | } 461 | 462 | return rc; 463 | } 464 | 465 | /* clcf->satisfy == NGX_HTTP_SATISFY_ANY */ 466 | if (rc == NGX_OK) { 467 | return NGX_OK; 468 | } else if (rc == rule[i].code) { 469 | out_rc = rc; 470 | } 471 | } 472 | 473 | return out_rc; 474 | } 475 | 476 | static ngx_int_t ngx_ip_blocker_process_rule(ngx_http_request_t *r, 477 | ngx_http_ip_blocker_ruleset_st *rule) 478 | { 479 | ngx_http_core_loc_conf_t *clcf; 480 | u_char *base, *addr; 481 | size_t len, addr_len; 482 | struct sockaddr_in *sin; 483 | #if NGX_HAVE_INET6 484 | struct sockaddr_in6 *sin6; 485 | #endif /* NGX_HAVE_INET6 */ 486 | int (*compare)(const void *a, const void *b); 487 | 488 | if (rule->addr == MAP_FAILED || rule->size < sizeof(ngx_ip_blocker_shm_st)) { 489 | return NGX_ERROR; 490 | } 491 | 492 | if (ngx_ip_blocker_rwlock_rlock(&rule->addr->lock) != NGX_OK) { 493 | return NGX_ERROR; 494 | } 495 | 496 | /* runlock is called inside of remap iff NGX_ERROR is returned */ 497 | if (rule->revision != rule->addr->revision 498 | && ngx_http_ip_blocker_remap(rule, r->connection->log) != NGX_OK) { 499 | return NGX_ERROR; 500 | } 501 | 502 | switch (r->connection->sockaddr->sa_family) { 503 | case AF_INET: 504 | sin = (struct sockaddr_in *)r->connection->sockaddr; 505 | 506 | base = (u_char *)rule->addr + rule->addr->ip4.base; 507 | len = rule->addr->ip4.len; 508 | 509 | addr = (u_char *)&sin->sin_addr.s_addr; 510 | addr_len = 4; 511 | 512 | compare = ngx_http_ip_blocker_ip4_compare; 513 | break; 514 | #if NGX_HAVE_INET6 515 | case AF_INET6: 516 | sin6 = (struct sockaddr_in6 *)r->connection->sockaddr; 517 | 518 | addr = sin6->sin6_addr.s6_addr; 519 | addr_len = 16; 520 | 521 | if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 522 | base = (u_char *)rule->addr + rule->addr->ip4.base; 523 | len = rule->addr->ip4.len; 524 | 525 | addr += 12; 526 | addr_len -= 12; 527 | 528 | compare = ngx_http_ip_blocker_ip4_compare; 529 | } else { 530 | base = (u_char *)rule->addr + rule->addr->ip6.base; 531 | len = rule->addr->ip6.len; 532 | 533 | compare = ngx_http_ip_blocker_ip6_compare; 534 | } 535 | 536 | break; 537 | #endif /* NGX_HAVE_INET6 */ 538 | default: 539 | ngx_ip_blocker_rwlock_runlock(&rule->addr->lock); 540 | return NGX_ERROR; 541 | } 542 | 543 | search: 544 | if (len && bsearch(addr, base, len / addr_len, addr_len, compare)) { 545 | /* remote address found in block list */ 546 | if (ngx_ip_blocker_rwlock_runlock(&rule->addr->lock) != NGX_OK) { 547 | return NGX_ERROR; 548 | } 549 | 550 | if (rule->whitelist) { 551 | return NGX_OK; 552 | } 553 | 554 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 555 | if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { 556 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 557 | "access forbidden by rule"); 558 | } 559 | 560 | return rule->code; 561 | #if NGX_HAVE_INET6 562 | } else if (addr_len == 16) { 563 | base = (u_char *)rule->addr + rule->addr->ip6route.base; 564 | len = rule->addr->ip6route.len; 565 | 566 | addr_len = 8; 567 | 568 | compare = ngx_http_ip_blocker_ip6route_compare; 569 | goto search; 570 | #endif /* NGX_HAVE_INET6 */ 571 | } else { 572 | /* remote address not found in block list */ 573 | if (ngx_ip_blocker_rwlock_runlock(&rule->addr->lock) != NGX_OK) { 574 | return NGX_ERROR; 575 | } 576 | 577 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 578 | if (rule->whitelist && clcf->satisfy == NGX_HTTP_SATISFY_ALL) { 579 | return rule->code; 580 | } else { 581 | return NGX_DECLINED; 582 | } 583 | } 584 | } 585 | 586 | static int ngx_http_ip_blocker_ip4_compare(const void *a, const void *b) 587 | { 588 | return memcmp(a, b, 4); 589 | } 590 | 591 | #if NGX_HAVE_INET6 592 | static int ngx_http_ip_blocker_ip6_compare(const void *a, const void *b) 593 | { 594 | return memcmp(a, b, 16); 595 | } 596 | 597 | static int ngx_http_ip_blocker_ip6route_compare(const void *a, const void *b) 598 | { 599 | return memcmp(a, b, 8); 600 | } 601 | #endif /* NGX_HAVE_INET6 */ 602 | -------------------------------------------------------------------------------- /ngx_http_ip_blocker_rwlock.c: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ngx_ip_blocker_shm.h" 10 | 11 | #include // For assert 12 | #include // For sem_* 13 | 14 | // rlock locks rw for reading. 15 | ngx_int_t ngx_ip_blocker_rwlock_rlock(ngx_ip_blocker_rwlock_st *rw) 16 | { 17 | if (ngx_atomic_fetch_add(&rw->reader_count, 1) < -1) { 18 | // A writer is pending, wait for it. 19 | if (sem_wait(&rw->reader_sem) != 0) { 20 | return NGX_ERROR; 21 | } 22 | } 23 | 24 | return NGX_OK; 25 | } 26 | 27 | // runlock undoes a single rlock call; 28 | // it does not affect other simultaneous readers. 29 | // It is a run-time error if rw is not locked for reading 30 | // on entry to runlock. 31 | ngx_int_t ngx_ip_blocker_rwlock_runlock(ngx_ip_blocker_rwlock_st *rw) 32 | { 33 | int32_t r; 34 | 35 | r = ngx_atomic_fetch_add(&rw->reader_count, -1); 36 | if (r < 1) { 37 | assert(r != 0 && r != -NGX_IP_BLOCKER_MAX_READERS); 38 | 39 | // A writer is pending. 40 | if (ngx_atomic_fetch_add(&rw->reader_wait, -1) == 1) { 41 | // The last reader unblocks the writer. 42 | if (sem_post(&rw->writer_sem) != 0) { 43 | return NGX_ERROR; 44 | } 45 | } 46 | } 47 | 48 | return NGX_OK; 49 | } 50 | -------------------------------------------------------------------------------- /ngx_ip_blocker_shm.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Tom Thorogood. All rights reserved. 2 | // Use of this source code is governed by a 3 | // Modified BSD License license that can be found in 4 | // the LICENSE file. 5 | 6 | #include // For int32_t 7 | #include // For sem_t 8 | 9 | #define NGX_IP_BLOCKER_MAX_READERS (1 << 30) 10 | 11 | typedef struct { 12 | sem_t sem; 13 | } ngx_ip_blocker_mutex_st; 14 | 15 | typedef struct { 16 | ngx_ip_blocker_mutex_st w; // held if there are pending writers 17 | sem_t writer_sem; // semaphore for writers to wait for completing readers 18 | sem_t reader_sem; // semaphore for readers to wait for completing writers 19 | volatile int32_t reader_count; // number of pending readers 20 | volatile int32_t reader_wait; // number of departing readers 21 | } ngx_ip_blocker_rwlock_st; 22 | 23 | typedef struct { 24 | uint32_t version; 25 | volatile uint32_t revision; 26 | 27 | ngx_ip_blocker_rwlock_st lock; 28 | 29 | struct { 30 | volatile size_t base; 31 | volatile size_t len; 32 | } ip4, ip6, ip6route; 33 | } ngx_ip_blocker_shm_st; 34 | 35 | // -*- mode: c;-*- 36 | --------------------------------------------------------------------------------