diff src/http/modules/ngx_http_access_module.c @ 552:c04fa65fe604 NGINX_0_8_22

nginx 0.8.22 *) Feature: the "proxy_bind", "fastcgi_bind", and "memcached_bind" directives. *) Feature: the "access" and the "deny" directives support IPv6. *) Feature: the "set_real_ip_from" directive supports IPv6 addresses in request headers. *) Feature: the "unix:" parameter of the "set_real_ip_from" directive. *) Bugfix: nginx did not delete unix domain socket after configuration testing. *) Bugfix: nginx deleted unix domain socket while online upgrade. *) Bugfix: the "!-x" operator did not work. Thanks to Maxim Dounin. *) Bugfix: a segmentation fault might occur in a worker process, if limit_rate was used in HTTPS server. Thanks to Maxim Dounin. *) Bugfix: a segmentation fault might occur in a worker process while $limit_rate logging. Thanks to Maxim Dounin. *) Bugfix: a segmentation fault might occur in a worker process, if there was no "listen" directive in "server" block; the bug had appeared in 0.8.21.
author Igor Sysoev <http://sysoev.ru>
date Tue, 03 Nov 2009 00:00:00 +0300
parents f39b9e29530d
children 53f5f04a64b8
line wrap: on
line diff
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -10,18 +10,37 @@
 
 
 typedef struct {
-    in_addr_t     mask;
-    in_addr_t     addr;
-    ngx_uint_t    deny;      /* unsigned  deny:1; */
+    in_addr_t         mask;
+    in_addr_t         addr;
+    ngx_uint_t        deny;      /* unsigned  deny:1; */
 } ngx_http_access_rule_t;
 
+#if (NGX_HAVE_INET6)
 
 typedef struct {
-    ngx_array_t  *rules;     /* array of ngx_http_access_rule_t */
+    struct in6_addr   addr;
+    struct in6_addr   mask;
+    ngx_uint_t        deny;      /* unsigned  deny:1; */
+} ngx_http_access_rule6_t;
+
+#endif
+
+typedef struct {
+    ngx_array_t      *rules;     /* array of ngx_http_access_rule_t */
+#if (NGX_HAVE_INET6)
+    ngx_array_t      *rules6;    /* array of ngx_http_access_rule6_t */
+#endif
 } ngx_http_access_loc_conf_t;
 
 
 static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_access_inet(ngx_http_request_t *r,
+    ngx_http_access_loc_conf_t *alcf, in_addr_t addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_http_access_inet6(ngx_http_request_t *r,
+    ngx_http_access_loc_conf_t *alcf, u_char *p);
+#endif
+static ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny);
 static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf);
@@ -87,46 +106,59 @@ ngx_module_t  ngx_http_access_module = {
 static ngx_int_t
 ngx_http_access_handler(ngx_http_request_t *r)
 {
-    ngx_uint_t                   i;
     struct sockaddr_in          *sin;
-    ngx_http_access_rule_t      *rule;
-    ngx_http_core_loc_conf_t    *clcf;
     ngx_http_access_loc_conf_t  *alcf;
 
     alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);
 
-    if (alcf->rules == NULL) {
-        return NGX_DECLINED;
+#if (NGX_HAVE_INET6)
+
+    if (alcf->rules6 && r->connection->sockaddr->sa_family == AF_INET6) {
+        u_char               *p;
+        in_addr_t             addr;
+        struct sockaddr_in6  *sin6;
+
+        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+        p = sin6->sin6_addr.s6_addr;
+
+        if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+            addr = p[12] << 24;
+            addr += p[13] << 16;
+            addr += p[14] << 8;
+            addr += p[15];
+            return ngx_http_access_inet(r, alcf, htonl(addr));
+        }
+
+        return ngx_http_access_inet6(r, alcf, p);
     }
 
-    /* AF_INET only */
+#endif
 
-    if (r->connection->sockaddr->sa_family != AF_INET) {
-        return NGX_DECLINED;
+    if (alcf->rules && r->connection->sockaddr->sa_family == AF_INET) {
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+        return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);
     }
 
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
+    return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_access_inet(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
+    in_addr_t addr)
+{
+    ngx_uint_t               i;
+    ngx_http_access_rule_t  *rule;
 
     rule = alcf->rules->elts;
     for (i = 0; i < alcf->rules->nelts; i++) {
 
         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "access: %08XD %08XD %08XD",
-                       sin->sin_addr.s_addr, rule[i].mask, rule[i].addr);
-
-        if ((sin->sin_addr.s_addr & rule[i].mask) == rule[i].addr) {
-            if (rule[i].deny) {
-                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+                       addr, rule[i].mask, rule[i].addr);
 
-                if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
-                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                                  "access forbidden by rule");
-                }
-
-                return NGX_HTTP_FORBIDDEN;
-            }
-
-            return NGX_OK;
+        if ((addr & rule[i].mask) == rule[i].addr) {
+            return ngx_http_access_found(r, rule[i].deny);
         }
     }
 
@@ -134,62 +166,159 @@ ngx_http_access_handler(ngx_http_request
 }
 
 
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_http_access_inet6(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
+    u_char *p)
+{
+    ngx_uint_t                n;
+    ngx_uint_t                i;
+    ngx_http_access_rule6_t  *rule6;
+
+    rule6 = alcf->rules6->elts;
+    for (i = 0; i < alcf->rules6->nelts; i++) {
+
+#if (NGX_DEBUG)
+        {
+        size_t  cl, ml, al;
+        u_char  ct[NGX_INET6_ADDRSTRLEN];
+        u_char  mt[NGX_INET6_ADDRSTRLEN];
+        u_char  at[NGX_INET6_ADDRSTRLEN];
+
+        cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);
+        ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);
+        al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);
+
+        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "access: %*s %*s %*s", cl, ct, ml, mt, al, at);
+        }
+#endif
+
+        for (n = 0; n < 16; n++) {
+            if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {
+                goto next;
+            }
+        }
+
+        return ngx_http_access_found(r, rule6[i].deny);
+
+    next:
+        continue;
+    }
+
+    return NGX_DECLINED;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny)
+{
+    ngx_http_core_loc_conf_t  *clcf;
+
+    if (deny) {
+        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+        if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "access forbidden by rule");
+        }
+
+        return NGX_HTTP_FORBIDDEN;
+    }
+
+    return NGX_OK;
+}
+
+
 static char *
 ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_access_loc_conf_t *alcf = conf;
 
-    ngx_int_t                rc;
-    ngx_str_t               *value;
-    ngx_cidr_t               cidr;
-    ngx_http_access_rule_t  *rule;
+    ngx_int_t                 rc;
+    ngx_uint_t                all;
+    ngx_str_t                *value;
+    ngx_cidr_t                cidr;
+    ngx_http_access_rule_t   *rule;
+#if (NGX_HAVE_INET6)
+    ngx_http_access_rule6_t  *rule6;
+#endif
 
-    if (alcf->rules == NULL) {
-        alcf->rules = ngx_array_create(cf->pool, 4,
-                                       sizeof(ngx_http_access_rule_t));
-        if (alcf->rules == NULL) {
-            return NGX_CONF_ERROR;
-        }
-    }
-
-    rule = ngx_array_push(alcf->rules);
-    if (rule == NULL) {
-        return NGX_CONF_ERROR;
-    }
+    ngx_memzero(&cidr, sizeof(ngx_cidr_t));
 
     value = cf->args->elts;
 
-    rule->deny = (value[0].data[0] == 'd') ? 1 : 0;
+    all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0);
+
+    if (!all) {
+
+        rc = ngx_ptocidr(&value[1], &cidr);
 
-    if (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0) {
-        rule->mask = 0;
-        rule->addr = 0;
+        if (rc == NGX_ERROR) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                         "invalid parameter \"%V\"", &value[1]);
+            return NGX_CONF_ERROR;
+        }
 
-        return NGX_CONF_OK;
+        if (rc == NGX_DONE) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                         "low address bits of %V are meaningless", &value[1]);
+        }
     }
 
-    rc = ngx_ptocidr(&value[1], &cidr);
+    switch (cidr.family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+    case 0: /* all */
 
-    if (rc == NGX_ERROR) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
-                           &value[1]);
-        return NGX_CONF_ERROR;
-    }
+        if (alcf->rules6 == NULL) {
+            alcf->rules6 = ngx_array_create(cf->pool, 4,
+                                            sizeof(ngx_http_access_rule6_t));
+            if (alcf->rules6 == NULL) {
+                return NGX_CONF_ERROR;
+            }
+        }
+
+        rule6 = ngx_array_push(alcf->rules6);
+        if (rule6 == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        rule6->mask = cidr.u.in6.mask;
+        rule6->addr = cidr.u.in6.addr;
+        rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;
 
-    if (cidr.family != AF_INET) {
-        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "\"allow\" supports IPv4 only");
-        return NGX_CONF_ERROR;
-    }
+        if (!all) {
+            break;
+        }
+
+        /* "all" passes through */
+#endif
+
+    default: /* AF_INET */
 
-    if (rc == NGX_DONE) {
-        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                           "low address bits of %V are meaningless", &value[1]);
+        if (alcf->rules == NULL) {
+            alcf->rules = ngx_array_create(cf->pool, 4,
+                                           sizeof(ngx_http_access_rule_t));
+            if (alcf->rules == NULL) {
+                return NGX_CONF_ERROR;
+            }
+        }
+
+        rule = ngx_array_push(alcf->rules);
+        if (rule == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        rule->mask = cidr.u.in.mask;
+        rule->addr = cidr.u.in.addr;
+        rule->deny = (value[0].data[0] == 'd') ? 1 : 0;
     }
 
-    rule->mask = cidr.u.in.mask;
-    rule->addr = cidr.u.in.addr;
-
     return NGX_CONF_OK;
 }
 
@@ -218,6 +347,12 @@ ngx_http_access_merge_loc_conf(ngx_conf_
         conf->rules = prev->rules;
     }
 
+#if (NGX_HAVE_INET6)
+    if (conf->rules6 == NULL) {
+        conf->rules6 = prev->rules6;
+    }
+#endif
+
     return NGX_CONF_OK;
 }