[nginx] Mail: limit_conn module.

Maxim Dounin mdounin at mdounin.ru
Mon Jul 7 04:29:40 UTC 2025


details:   http://freenginx.org/hg/nginx/rev/9fa6118103ca
branches:  
changeset: 9390:9fa6118103ca
user:      Maxim Dounin <mdounin at mdounin.ru>
date:      Mon Jul 07 04:34:22 2025 +0300
description:
Mail: limit_conn module.

The limit_conn module limits connections after auth_http authentication,
so it can use the user name updated by the authentication server, and
headers returned by the authentication server.  Mostly usable to limit
number of connections from a particular authenticated client.

Configuration is similar to the one in HTTP, but since there are no
variables in mail, limit_conn_zone can use the following restricted
set of keys:

- $remote_addr, client address;
- $remote_user, user name supplied during authentication;
- $auth_http_*, auth_http response header field.

For example:

    limit_conn_zone $auth_http_limit_key zone=one:1m;
    limit_conn one 10;

If the limit is hit, the following errors are returned for IMAP, POP3,
and SMTP (and the connection is closed):

    <tag> NO [UNAVAILABLE] Too many connections
    -ERR [SYS/TEMP] Too many connections
    454 4.7.0 Too many connections

For IMAP, the "[UNAVAILABLE]" response code (RFC 5530) is used.

For POP3, the "[SYS/TEMP]" extended response code (RFC 2449, RFC 3206)
is used, and the default capabilities list is updated with RESP-CODES
to ensure that extended response codes can be properly recognized by
clients.

At least Apple Mail (both on macOS and iOS) is able to recognize the
response codes being used, and won't show password prompt if the limit
is hit.  Still, many clients won't be able to.  In particular, Thunderbird
is known to show a generic error dialog with options to try again or
enter a new password, and Outlook always asks for a new password.
As such, it is not recommended to configure limits which are low enough
to be hit by well-behaving clients.

diffstat:

 auto/modules                          |    6 +
 src/mail/ngx_mail.h                   |    3 +
 src/mail/ngx_mail_auth_http_module.c  |  153 ++++++++++-
 src/mail/ngx_mail_limit_conn_module.c |  464 ++++++++++++++++-----------------
 src/mail/ngx_mail_pop3_module.c       |    1 +
 5 files changed, 373 insertions(+), 254 deletions(-)

diffs (1091 lines):

diff --git a/auto/modules b/auto/modules
--- a/auto/modules
+++ b/auto/modules
@@ -1032,6 +1032,12 @@ if [ $MAIL != NO ]; then
     ngx_module_srcs=src/mail/ngx_mail_realip_module.c
 
     . auto/module
+
+    ngx_module_name=ngx_mail_limit_conn_module
+    ngx_module_deps=
+    ngx_module_srcs=src/mail/ngx_mail_limit_conn_module.c
+
+    . auto/module
 fi
 
 
diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -449,7 +449,10 @@ char *ngx_mail_capabilities(ngx_conf_t *
 /* STUB */
 void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer);
 void ngx_mail_auth_http_init(ngx_mail_session_t *s);
+ngx_int_t ngx_mail_auth_http_header_value(ngx_mail_session_t *s,
+    ngx_str_t *name, ngx_str_t *value);
 ngx_int_t ngx_mail_realip_handler(ngx_mail_session_t *s);
+ngx_int_t ngx_mail_limit_conn_handler(ngx_mail_session_t *s);
 /**/
 
 
diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -57,6 +57,8 @@ struct ngx_mail_auth_http_ctx_s {
 
     time_t                          sleep;
 
+    ngx_list_t                      headers;
+
     ngx_pool_t                     *pool;
 };
 
@@ -196,6 +198,14 @@ ngx_mail_auth_http_init(ngx_mail_session
         return;
     }
 
+    if (ngx_list_init(&ctx->headers, pool, 8, sizeof(ngx_table_elt_t))
+        != NGX_OK)
+    {
+        ngx_destroy_pool(ctx->pool);
+        ngx_mail_session_internal_server_error(s);
+        return;
+    }
+
     ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);
 
     ctx->peer.sockaddr = ahcf->peer->sockaddr;
@@ -467,11 +477,12 @@ static void
 ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
     ngx_mail_auth_http_ctx_t *ctx)
 {
-    u_char      *p;
-    time_t       timer;
-    size_t       len, size;
-    ngx_int_t    rc, port, n;
-    ngx_addr_t  *peer;
+    u_char           *p;
+    time_t            timer;
+    size_t            len, size;
+    ngx_int_t         rc, port, n;
+    ngx_addr_t       *peer;
+    ngx_table_elt_t  *h;
 
     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
                    "mail auth http process headers");
@@ -481,20 +492,24 @@ ngx_mail_auth_http_process_headers(ngx_m
 
         if (rc == NGX_OK) {
 
-#if (NGX_DEBUG)
-            {
-            ngx_str_t  key, value;
+            /* a header line has been parsed successfully */
 
-            key.len = ctx->header_name_end - ctx->header_name_start;
-            key.data = ctx->header_name_start;
-            value.len = ctx->header_end - ctx->header_start;
-            value.data = ctx->header_start;
+            h = ngx_list_push(&ctx->headers);
+            if (h == NULL) {
+                ngx_close_connection(ctx->peer.connection);
+                ngx_destroy_pool(ctx->pool);
+                ngx_mail_session_internal_server_error(s);
+                return;
+            }
+
+            h->key.len = ctx->header_name_end - ctx->header_name_start;
+            h->key.data = ctx->header_name_start;
+            h->value.len = ctx->header_end - ctx->header_start;
+            h->value.data = ctx->header_start;
 
             ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
                            "mail auth http header: \"%V: %V\"",
-                           &key, &value);
-            }
-#endif
+                           &h->key, &h->value);
 
             len = ctx->header_name_end - ctx->header_name_start;
 
@@ -881,6 +896,13 @@ ngx_mail_auth_http_process_headers(ngx_m
 
             ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);
 
+            /* check connection limits */
+
+            if (ngx_mail_limit_conn_handler(s) != NGX_OK) {
+                ngx_destroy_pool(ctx->pool);
+                return;
+            }
+
             ngx_destroy_pool(ctx->pool);
             ngx_mail_proxy_init(s, peer);
 
@@ -1590,6 +1612,107 @@ ngx_mail_auth_http_escape(ngx_pool_t *po
 }
 
 
+ngx_int_t
+ngx_mail_auth_http_header_value(ngx_mail_session_t *s, ngx_str_t *name,
+    ngx_str_t *value)
+{
+    u_char                    *p, ch;
+    size_t                     len;
+    ngx_uint_t                 i, n;
+    ngx_list_part_t           *part;
+    ngx_table_elt_t           *header, *h, **ph;
+    ngx_mail_auth_http_ctx_t  *ctx;
+
+    ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
+
+    ph = &h;
+    len = 0;
+
+    part = &ctx->headers.part;
+    header = part->elts;
+
+    for (i = 0; /* void */ ; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            header = part->elts;
+            i = 0;
+        }
+
+        if (header[i].key.len != name->len) {
+            continue;
+        }
+
+        for (n = 0; n < name->len; n++) {
+            ch = header[i].key.data[n];
+
+            if (ch >= 'A' && ch <= 'Z') {
+                ch |= 0x20;
+
+            } else if (ch == '-') {
+                ch = '_';
+            }
+
+            if (name->data[n] != ch) {
+                break;
+            }
+        }
+
+
+        if (n != name->len) {
+            continue;
+        }
+
+        len += header[i].value.len + 2;
+
+        *ph = &header[i];
+        ph = &header[i].next;
+    }
+
+    *ph = NULL;
+
+    if (h == NULL) {
+        value->len = 0;
+        value->data = NULL;
+        return NGX_OK;
+    }
+
+    len -= 2;
+
+    if (h->next == NULL) {
+        *value = h->value;
+        return NGX_OK;
+    }
+
+    p = ngx_pnalloc(ctx->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    value->len = len;
+    value->data = p;
+
+    for ( ;; ) {
+
+        p = ngx_copy(p, h->value.data, h->value.len);
+
+        if (h->next == NULL) {
+            break;
+        }
+
+        *p++ = ','; *p++ = ' ';
+
+        h = h->next;
+    }
+
+    return NGX_OK;
+}
+
+
 static void *
 ngx_mail_auth_http_create_conf(ngx_conf_t *cf)
 {
diff --git a/src/http/modules/ngx_http_limit_conn_module.c b/src/mail/ngx_mail_limit_conn_module.c
copy from src/http/modules/ngx_http_limit_conn_module.c
copy to src/mail/ngx_mail_limit_conn_module.c
--- a/src/http/modules/ngx_http_limit_conn_module.c
+++ b/src/mail/ngx_mail_limit_conn_module.c
@@ -1,18 +1,19 @@
 
 /*
  * Copyright (C) Igor Sysoev
+ * Copyright (C) Maxim Dounin
  * Copyright (C) Nginx, Inc.
  */
 
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_http.h>
+#include <ngx_mail.h>
 
 
-#define NGX_HTTP_LIMIT_CONN_PASSED            1
-#define NGX_HTTP_LIMIT_CONN_REJECTED          2
-#define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN  3
+#define NGX_MAIL_LIMIT_CONN_REMOTE_USER  1
+#define NGX_MAIL_LIMIT_CONN_REMOTE_ADDR  2
+#define NGX_MAIL_LIMIT_CONN_AUTH_HTTP    3
 
 
 typedef struct {
@@ -20,63 +21,62 @@ typedef struct {
     u_char                        len;
     u_short                       conn;
     u_char                        data[1];
-} ngx_http_limit_conn_node_t;
+} ngx_mail_limit_conn_node_t;
 
 
 typedef struct {
     ngx_shm_zone_t               *shm_zone;
     ngx_rbtree_node_t            *node;
-} ngx_http_limit_conn_cleanup_t;
+} ngx_mail_limit_conn_cleanup_t;
 
 
 typedef struct {
     ngx_rbtree_t                  rbtree;
     ngx_rbtree_node_t             sentinel;
-} ngx_http_limit_conn_shctx_t;
+} ngx_mail_limit_conn_shctx_t;
 
 
 typedef struct {
-    ngx_http_limit_conn_shctx_t  *sh;
+    ngx_mail_limit_conn_shctx_t  *sh;
     ngx_slab_pool_t              *shpool;
-    ngx_http_complex_value_t      key;
-} ngx_http_limit_conn_ctx_t;
+    ngx_str_t                     key;
+    ngx_uint_t                    key_type;
+} ngx_mail_limit_conn_ctx_t;
 
 
 typedef struct {
     ngx_shm_zone_t               *shm_zone;
     ngx_uint_t                    conn;
-} ngx_http_limit_conn_limit_t;
+} ngx_mail_limit_conn_limit_t;
 
 
 typedef struct {
     ngx_array_t                   limits;
     ngx_uint_t                    log_level;
-    ngx_uint_t                    status_code;
     ngx_flag_t                    dry_run;
-} ngx_http_limit_conn_conf_t;
+} ngx_mail_limit_conn_conf_t;
 
 
-static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,
+static ngx_int_t ngx_mail_limit_conn_key(ngx_mail_session_t *s,
+    ngx_mail_limit_conn_ctx_t *ctx, ngx_str_t *key);
+static void ngx_mail_limit_conn_send_error(ngx_mail_session_t *s);
+static ngx_rbtree_node_t *ngx_mail_limit_conn_lookup(ngx_rbtree_t *rbtree,
     ngx_str_t *key, uint32_t hash);
-static void ngx_http_limit_conn_cleanup(void *data);
-static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);
-static void ngx_http_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
+static void ngx_mail_limit_conn_cleanup(void *data);
+static ngx_inline void ngx_mail_limit_conn_cleanup_all(ngx_pool_t *pool);
+static void ngx_mail_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
     ngx_rbtree_node_t *node);
 
-static ngx_int_t ngx_http_limit_conn_status_variable(ngx_http_request_t *r,
-    ngx_http_variable_value_t *v, uintptr_t data);
-static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);
-static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
+static void *ngx_mail_limit_conn_create_conf(ngx_conf_t *cf);
+static char *ngx_mail_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
     void *child);
-static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+static char *ngx_mail_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
-static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
+static char *ngx_mail_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
-static ngx_int_t ngx_http_limit_conn_add_variables(ngx_conf_t *cf);
-static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);
 
 
-static ngx_conf_enum_t  ngx_http_limit_conn_log_levels[] = {
+static ngx_conf_enum_t  ngx_mail_limit_conn_log_levels[] = {
     { ngx_string("info"), NGX_LOG_INFO },
     { ngx_string("notice"), NGX_LOG_NOTICE },
     { ngx_string("warn"), NGX_LOG_WARN },
@@ -85,72 +85,56 @@ static ngx_conf_enum_t  ngx_http_limit_c
 };
 
 
-static ngx_conf_num_bounds_t  ngx_http_limit_conn_status_bounds = {
-    ngx_conf_check_num_bounds, 400, 599
-};
-
-
-static ngx_command_t  ngx_http_limit_conn_commands[] = {
+static ngx_command_t  ngx_mail_limit_conn_commands[] = {
 
     { ngx_string("limit_conn_zone"),
-      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
-      ngx_http_limit_conn_zone,
+      NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE2,
+      ngx_mail_limit_conn_zone,
       0,
       0,
       NULL },
 
     { ngx_string("limit_conn"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
-      ngx_http_limit_conn,
-      NGX_HTTP_LOC_CONF_OFFSET,
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,
+      ngx_mail_limit_conn,
+      NGX_MAIL_SRV_CONF_OFFSET,
       0,
       NULL },
 
     { ngx_string("limit_conn_log_level"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_enum_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_limit_conn_conf_t, log_level),
-      &ngx_http_limit_conn_log_levels },
-
-    { ngx_string("limit_conn_status"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_num_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_limit_conn_conf_t, status_code),
-      &ngx_http_limit_conn_status_bounds },
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_limit_conn_conf_t, log_level),
+      &ngx_mail_limit_conn_log_levels },
 
     { ngx_string("limit_conn_dry_run"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_limit_conn_conf_t, dry_run),
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_limit_conn_conf_t, dry_run),
       NULL },
 
       ngx_null_command
 };
 
 
-static ngx_http_module_t  ngx_http_limit_conn_module_ctx = {
-    ngx_http_limit_conn_add_variables,     /* preconfiguration */
-    ngx_http_limit_conn_init,              /* postconfiguration */
+static ngx_mail_module_t  ngx_mail_limit_conn_module_ctx = {
+    NULL,                                  /* protocol */
 
     NULL,                                  /* create main configuration */
     NULL,                                  /* init main configuration */
 
-    NULL,                                  /* create server configuration */
-    NULL,                                  /* merge server configuration */
-
-    ngx_http_limit_conn_create_conf,       /* create location configuration */
-    ngx_http_limit_conn_merge_conf         /* merge location configuration */
+    ngx_mail_limit_conn_create_conf,       /* create server configuration */
+    ngx_mail_limit_conn_merge_conf         /* merge server configuration */
 };
 
 
-ngx_module_t  ngx_http_limit_conn_module = {
+ngx_module_t  ngx_mail_limit_conn_module = {
     NGX_MODULE_V1,
-    &ngx_http_limit_conn_module_ctx,       /* module context */
-    ngx_http_limit_conn_commands,          /* module directives */
-    NGX_HTTP_MODULE,                       /* module type */
+    &ngx_mail_limit_conn_module_ctx,       /* module context */
+    ngx_mail_limit_conn_commands,          /* module directives */
+    NGX_MAIL_MODULE,                       /* module type */
     NULL,                                  /* init master */
     NULL,                                  /* init module */
     NULL,                                  /* init process */
@@ -162,24 +146,13 @@ ngx_module_t  ngx_http_limit_conn_module
 };
 
 
-static ngx_http_variable_t  ngx_http_limit_conn_vars[] = {
-
-    { ngx_string("limit_conn_status"), NULL,
-      ngx_http_limit_conn_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
-
-      ngx_http_null_variable
-};
+static u_char  pop3_error[] = "-ERR [SYS/TEMP] Too many connections" CRLF;
+static u_char  imap_error[] = "NO [UNAVAILABLE] Too many connections" CRLF;
+static u_char  smtp_error[] = "454 4.7.0 Too many connections" CRLF;
 
 
-static ngx_str_t  ngx_http_limit_conn_status[] = {
-    ngx_string("PASSED"),
-    ngx_string("REJECTED"),
-    ngx_string("REJECTED_DRY_RUN")
-};
-
-
-static ngx_int_t
-ngx_http_limit_conn_handler(ngx_http_request_t *r)
+ngx_int_t
+ngx_mail_limit_conn_handler(ngx_mail_session_t *s)
 {
     size_t                          n;
     uint32_t                        hash;
@@ -187,24 +160,23 @@ ngx_http_limit_conn_handler(ngx_http_req
     ngx_uint_t                      i;
     ngx_rbtree_node_t              *node;
     ngx_pool_cleanup_t             *cln;
-    ngx_http_limit_conn_ctx_t      *ctx;
-    ngx_http_limit_conn_node_t     *lc;
-    ngx_http_limit_conn_conf_t     *lccf;
-    ngx_http_limit_conn_limit_t    *limits;
-    ngx_http_limit_conn_cleanup_t  *lccln;
+    ngx_mail_limit_conn_ctx_t      *ctx;
+    ngx_mail_limit_conn_node_t     *lc;
+    ngx_mail_limit_conn_conf_t     *lccf;
+    ngx_mail_limit_conn_limit_t    *limits;
+    ngx_mail_limit_conn_cleanup_t  *lccln;
 
-    if (r->main->limit_conn_status) {
-        return NGX_DECLINED;
-    }
+    s->connection->log->action = NULL;
 
-    lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
+    lccf = ngx_mail_get_module_srv_conf(s, ngx_mail_limit_conn_module);
     limits = lccf->limits.elts;
 
     for (i = 0; i < lccf->limits.nelts; i++) {
         ctx = limits[i].shm_zone->data;
 
-        if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        if (ngx_mail_limit_conn_key(s, ctx, &key) != NGX_OK) {
+            ngx_mail_session_internal_server_error(s);
+            return NGX_ERROR;
         }
 
         if (key.len == 0) {
@@ -212,45 +184,40 @@ ngx_http_limit_conn_handler(ngx_http_req
         }
 
         if (key.len > 255) {
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                           "the value of the \"%V\" key "
                           "is more than 255 bytes: \"%V\"",
-                          &ctx->key.value, &key);
+                          &ctx->key, &key);
             continue;
         }
 
-        r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED;
-
         hash = ngx_crc32_short(key.data, key.len);
 
         ngx_shmtx_lock(&ctx->shpool->mutex);
 
-        node = ngx_http_limit_conn_lookup(&ctx->sh->rbtree, &key, hash);
+        node = ngx_mail_limit_conn_lookup(&ctx->sh->rbtree, &key, hash);
 
         if (node == NULL) {
 
             n = offsetof(ngx_rbtree_node_t, color)
-                + offsetof(ngx_http_limit_conn_node_t, data)
+                + offsetof(ngx_mail_limit_conn_node_t, data)
                 + key.len;
 
             node = ngx_slab_alloc_locked(ctx->shpool, n);
 
             if (node == NULL) {
                 ngx_shmtx_unlock(&ctx->shpool->mutex);
-                ngx_http_limit_conn_cleanup_all(r->pool);
+                ngx_mail_limit_conn_cleanup_all(s->connection->pool);
 
                 if (lccf->dry_run) {
-                    r->main->limit_conn_status =
-                                          NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN;
-                    return NGX_DECLINED;
+                    return NGX_OK;
                 }
 
-                r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED;
-
-                return lccf->status_code;
+                ngx_mail_session_internal_server_error(s);
+                return NGX_ERROR;
             }
 
-            lc = (ngx_http_limit_conn_node_t *) &node->color;
+            lc = (ngx_mail_limit_conn_node_t *) &node->color;
 
             node->key = hash;
             lc->len = (u_char) key.len;
@@ -261,62 +228,135 @@ ngx_http_limit_conn_handler(ngx_http_req
 
         } else {
 
-            lc = (ngx_http_limit_conn_node_t *) &node->color;
+            lc = (ngx_mail_limit_conn_node_t *) &node->color;
 
             if ((ngx_uint_t) lc->conn >= limits[i].conn) {
 
                 ngx_shmtx_unlock(&ctx->shpool->mutex);
 
-                ngx_log_error(lccf->log_level, r->connection->log, 0,
+                ngx_log_error(lccf->log_level, s->connection->log, 0,
                               "limiting connections%s by zone \"%V\"",
                               lccf->dry_run ? ", dry run," : "",
                               &limits[i].shm_zone->shm.name);
 
-                ngx_http_limit_conn_cleanup_all(r->pool);
+                ngx_mail_limit_conn_cleanup_all(s->connection->pool);
 
                 if (lccf->dry_run) {
-                    r->main->limit_conn_status =
-                                          NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN;
-                    return NGX_DECLINED;
+                    return NGX_OK;
                 }
 
-                r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED;
-
-                return lccf->status_code;
+                ngx_mail_limit_conn_send_error(s);
+                return NGX_BUSY;
             }
 
             lc->conn++;
         }
 
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+        ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
                        "limit conn: %08Xi %d", node->key, lc->conn);
 
         ngx_shmtx_unlock(&ctx->shpool->mutex);
 
-        cln = ngx_pool_cleanup_add(r->pool,
-                                   sizeof(ngx_http_limit_conn_cleanup_t));
+        cln = ngx_pool_cleanup_add(s->connection->pool,
+                                   sizeof(ngx_mail_limit_conn_cleanup_t));
         if (cln == NULL) {
-            ngx_http_limit_conn_cleanup_node(limits[i].shm_zone, node);
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            ngx_mail_limit_conn_cleanup_node(limits[i].shm_zone, node);
+            ngx_mail_session_internal_server_error(s);
+            return NGX_ERROR;
         }
 
-        cln->handler = ngx_http_limit_conn_cleanup;
+        cln->handler = ngx_mail_limit_conn_cleanup;
         lccln = cln->data;
 
         lccln->shm_zone = limits[i].shm_zone;
         lccln->node = node;
     }
 
-    return NGX_DECLINED;
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_limit_conn_key(ngx_mail_session_t *s, ngx_mail_limit_conn_ctx_t *ctx,
+    ngx_str_t *key)
+{
+    ngx_str_t  name;
+
+    switch (ctx->key_type) {
+
+    case NGX_MAIL_LIMIT_CONN_REMOTE_USER:
+        *key = s->login;
+        break;
+
+    case NGX_MAIL_LIMIT_CONN_REMOTE_ADDR:
+        *key = s->connection->addr_text;
+        break;
+
+    case NGX_MAIL_LIMIT_CONN_AUTH_HTTP:
+
+        name.len = ctx->key.len - (sizeof("$auth_http_") - 1);
+        name.data = ctx->key.data + (sizeof("$auth_http_") - 1);
+
+        if (ngx_mail_auth_http_header_value(s, &name, key) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        break;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+                   "limit conn key: \"%V\"", key);
+
+    return NGX_OK;
 }
 
 
 static void
-ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ngx_mail_limit_conn_send_error(ngx_mail_session_t *s)
+{
+    u_char  *p, *err;
+    size_t   len;
+
+    switch (s->protocol) {
+
+    case NGX_MAIL_POP3_PROTOCOL:
+        ngx_str_set(&s->out, pop3_error);
+        break;
+
+    case NGX_MAIL_IMAP_PROTOCOL:
+        len = s->tag.len + sizeof(imap_error) - 1;
+
+        err = ngx_pnalloc(s->connection->pool, len);
+        if (err == NULL) {
+            ngx_mail_session_internal_server_error(s);
+            return;
+        }
+
+        p = ngx_cpymem(err, s->tag.data, s->tag.len);
+        ngx_memcpy(p, imap_error, sizeof(imap_error) - 1);
+
+        s->out.len = len;
+        s->out.data = err;
+
+        break;
+
+    default: /* NGX_MAIL_SMTP_PROTOCOL */
+        ngx_str_set(&s->out, smtp_error);
+        break;
+    }
+
+    s->quit = 1;
+
+    ngx_mail_send(s->connection->write);
+}
+
+
+static void
+ngx_mail_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
 {
     ngx_rbtree_node_t           **p;
-    ngx_http_limit_conn_node_t   *lcn, *lcnt;
+    ngx_mail_limit_conn_node_t   *lcn, *lcnt;
 
     for ( ;; ) {
 
@@ -330,8 +370,8 @@ ngx_http_limit_conn_rbtree_insert_value(
 
         } else { /* node->key == temp->key */
 
-            lcn = (ngx_http_limit_conn_node_t *) &node->color;
-            lcnt = (ngx_http_limit_conn_node_t *) &temp->color;
+            lcn = (ngx_mail_limit_conn_node_t *) &node->color;
+            lcnt = (ngx_mail_limit_conn_node_t *) &temp->color;
 
             p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
                 ? &temp->left : &temp->right;
@@ -353,11 +393,11 @@ ngx_http_limit_conn_rbtree_insert_value(
 
 
 static ngx_rbtree_node_t *
-ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)
+ngx_mail_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)
 {
     ngx_int_t                    rc;
     ngx_rbtree_node_t           *node, *sentinel;
-    ngx_http_limit_conn_node_t  *lcn;
+    ngx_mail_limit_conn_node_t  *lcn;
 
     node = rbtree->root;
     sentinel = rbtree->sentinel;
@@ -376,7 +416,7 @@ ngx_http_limit_conn_lookup(ngx_rbtree_t 
 
         /* hash == node->key */
 
-        lcn = (ngx_http_limit_conn_node_t *) &node->color;
+        lcn = (ngx_mail_limit_conn_node_t *) &node->color;
 
         rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);
 
@@ -392,21 +432,21 @@ ngx_http_limit_conn_lookup(ngx_rbtree_t 
 
 
 static void
-ngx_http_limit_conn_cleanup(void *data)
+ngx_mail_limit_conn_cleanup(void *data)
 {
-    ngx_http_limit_conn_cleanup_t  *lccln = data;
+    ngx_mail_limit_conn_cleanup_t  *lccln = data;
 
     ngx_rbtree_node_t           *node;
-    ngx_http_limit_conn_ctx_t   *ctx;
-    ngx_http_limit_conn_node_t  *lc;
+    ngx_mail_limit_conn_ctx_t   *ctx;
+    ngx_mail_limit_conn_node_t  *lc;
 
     ctx = lccln->shm_zone->data;
     node = lccln->node;
-    lc = (ngx_http_limit_conn_node_t *) &node->color;
+    lc = (ngx_mail_limit_conn_node_t *) &node->color;
 
     ngx_shmtx_lock(&ctx->shpool->mutex);
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,
+    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, lccln->shm_zone->shm.log, 0,
                    "limit conn cleanup: %08Xi %d", node->key, lc->conn);
 
     lc->conn--;
@@ -421,14 +461,14 @@ ngx_http_limit_conn_cleanup(void *data)
 
 
 static ngx_inline void
-ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
+ngx_mail_limit_conn_cleanup_all(ngx_pool_t *pool)
 {
     ngx_pool_cleanup_t  *cln;
 
     cln = pool->cleanup;
 
-    while (cln && cln->handler == ngx_http_limit_conn_cleanup) {
-        ngx_http_limit_conn_cleanup(cln->data);
+    while (cln && cln->handler == ngx_mail_limit_conn_cleanup) {
+        ngx_mail_limit_conn_cleanup(cln->data);
         cln = cln->next;
     }
 
@@ -437,39 +477,39 @@ ngx_http_limit_conn_cleanup_all(ngx_pool
 
 
 static void
-ngx_http_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
+ngx_mail_limit_conn_cleanup_node(ngx_shm_zone_t *shm_zone,
     ngx_rbtree_node_t *node)
 {
-    ngx_http_limit_conn_cleanup_t  lccln;
+    ngx_mail_limit_conn_cleanup_t  lccln;
 
     lccln.shm_zone = shm_zone;
     lccln.node = node;
 
-    ngx_http_limit_conn_cleanup(&lccln);
+    ngx_mail_limit_conn_cleanup(&lccln);
 }
 
 
 static ngx_int_t
-ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+ngx_mail_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
 {
-    ngx_http_limit_conn_ctx_t  *octx = data;
+    ngx_mail_limit_conn_ctx_t  *octx = data;
 
     size_t                      len;
-    ngx_http_limit_conn_ctx_t  *ctx;
+    ngx_mail_limit_conn_ctx_t  *ctx;
 
     ctx = shm_zone->data;
 
     if (octx) {
-        if (ctx->key.value.len != octx->key.value.len
-            || ngx_strncmp(ctx->key.value.data, octx->key.value.data,
-                           ctx->key.value.len)
+        if (ctx->key.len != octx->key.len
+            || ngx_strncmp(ctx->key.data, octx->key.data,
+                           ctx->key.len)
                != 0)
         {
             ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
                           "limit_conn_zone \"%V\" uses the \"%V\" key "
                           "while previously it used the \"%V\" key",
-                          &shm_zone->shm.name, &ctx->key.value,
-                          &octx->key.value);
+                          &shm_zone->shm.name, &ctx->key,
+                          &octx->key);
             return NGX_ERROR;
         }
 
@@ -487,7 +527,7 @@ ngx_http_limit_conn_init_zone(ngx_shm_zo
         return NGX_OK;
     }
 
-    ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_conn_shctx_t));
+    ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_mail_limit_conn_shctx_t));
     if (ctx->sh == NULL) {
         return NGX_ERROR;
     }
@@ -495,7 +535,7 @@ ngx_http_limit_conn_init_zone(ngx_shm_zo
     ctx->shpool->data = ctx->sh;
 
     ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
-                    ngx_http_limit_conn_rbtree_insert_value);
+                    ngx_mail_limit_conn_rbtree_insert_value);
 
     len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
 
@@ -511,31 +551,12 @@ ngx_http_limit_conn_init_zone(ngx_shm_zo
 }
 
 
-static ngx_int_t
-ngx_http_limit_conn_status_variable(ngx_http_request_t *r,
-    ngx_http_variable_value_t *v, uintptr_t data)
+static void *
+ngx_mail_limit_conn_create_conf(ngx_conf_t *cf)
 {
-    if (r->main->limit_conn_status == 0) {
-        v->not_found = 1;
-        return NGX_OK;
-    }
+    ngx_mail_limit_conn_conf_t  *conf;
 
-    v->valid = 1;
-    v->no_cacheable = 0;
-    v->not_found = 0;
-    v->len = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].len;
-    v->data = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].data;
-
-    return NGX_OK;
-}
-
-
-static void *
-ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
-{
-    ngx_http_limit_conn_conf_t  *conf;
-
-    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_limit_conn_conf_t));
     if (conf == NULL) {
         return NULL;
     }
@@ -547,7 +568,6 @@ ngx_http_limit_conn_create_conf(ngx_conf
      */
 
     conf->log_level = NGX_CONF_UNSET_UINT;
-    conf->status_code = NGX_CONF_UNSET_UINT;
     conf->dry_run = NGX_CONF_UNSET;
 
     return conf;
@@ -555,19 +575,16 @@ ngx_http_limit_conn_create_conf(ngx_conf
 
 
 static char *
-ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+ngx_mail_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 {
-    ngx_http_limit_conn_conf_t *prev = parent;
-    ngx_http_limit_conn_conf_t *conf = child;
+    ngx_mail_limit_conn_conf_t *prev = parent;
+    ngx_mail_limit_conn_conf_t *conf = child;
 
     if (conf->limits.elts == NULL) {
         conf->limits = prev->limits;
     }
 
     ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
-    ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
-                              NGX_HTTP_SERVICE_UNAVAILABLE);
-
     ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0);
 
     return NGX_CONF_OK;
@@ -575,30 +592,37 @@ ngx_http_limit_conn_merge_conf(ngx_conf_
 
 
 static char *
-ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ngx_mail_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    u_char                            *p;
-    ssize_t                            size;
-    ngx_str_t                         *value, name, s;
-    ngx_uint_t                         i;
-    ngx_shm_zone_t                    *shm_zone;
-    ngx_http_limit_conn_ctx_t         *ctx;
-    ngx_http_compile_complex_value_t   ccv;
+    u_char                     *p;
+    ssize_t                     size;
+    ngx_str_t                  *value, name, s;
+    ngx_uint_t                  i;
+    ngx_shm_zone_t             *shm_zone;
+    ngx_mail_limit_conn_ctx_t  *ctx;
 
     value = cf->args->elts;
 
-    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
+    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_limit_conn_ctx_t));
     if (ctx == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+    if (ngx_strcmp(value[1].data, "$remote_user") == 0) {
+        ctx->key = value[1];
+        ctx->key_type = NGX_MAIL_LIMIT_CONN_REMOTE_USER;
+
+    } else if (ngx_strcmp(value[1].data, "$remote_addr") == 0) {
+        ctx->key = value[1];
+        ctx->key_type = NGX_MAIL_LIMIT_CONN_REMOTE_ADDR;
 
-    ccv.cf = cf;
-    ccv.value = &value[1];
-    ccv.complex_value = &ctx->key;
+    } else if (ngx_strncmp(value[1].data, "$auth_http_", 11) == 0) {
+        ctx->key = value[1];
+        ctx->key_type = NGX_MAIL_LIMIT_CONN_AUTH_HTTP;
 
-    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+    } else {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[1]);
         return NGX_CONF_ERROR;
     }
 
@@ -654,7 +678,7 @@ ngx_http_limit_conn_zone(ngx_conf_t *cf,
     }
 
     shm_zone = ngx_shared_memory_add(cf, &name, size,
-                                     &ngx_http_limit_conn_module);
+                                     &ngx_mail_limit_conn_module);
     if (shm_zone == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -664,11 +688,11 @@ ngx_http_limit_conn_zone(ngx_conf_t *cf,
 
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "%V \"%V\" is already bound to key \"%V\"",
-                           &cmd->name, &name, &ctx->key.value);
+                           &cmd->name, &name, &ctx->key);
         return NGX_CONF_ERROR;
     }
 
-    shm_zone->init = ngx_http_limit_conn_init_zone;
+    shm_zone->init = ngx_mail_limit_conn_init_zone;
     shm_zone->data = ctx;
 
     return NGX_CONF_OK;
@@ -676,11 +700,11 @@ ngx_http_limit_conn_zone(ngx_conf_t *cf,
 
 
 static char *
-ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ngx_mail_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_shm_zone_t               *shm_zone;
-    ngx_http_limit_conn_conf_t   *lccf = conf;
-    ngx_http_limit_conn_limit_t  *limit, *limits;
+    ngx_mail_limit_conn_conf_t   *lccf = conf;
+    ngx_mail_limit_conn_limit_t  *limit, *limits;
 
     ngx_str_t  *value;
     ngx_int_t   n;
@@ -689,7 +713,7 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_
     value = cf->args->elts;
 
     shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
-                                     &ngx_http_limit_conn_module);
+                                     &ngx_mail_limit_conn_module);
     if (shm_zone == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -698,7 +722,7 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_
 
     if (limits == NULL) {
         if (ngx_array_init(&lccf->limits, cf->pool, 1,
-                           sizeof(ngx_http_limit_conn_limit_t))
+                           sizeof(ngx_mail_limit_conn_limit_t))
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
@@ -734,41 +758,3 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_
 
     return NGX_CONF_OK;
 }
-
-
-static ngx_int_t
-ngx_http_limit_conn_add_variables(ngx_conf_t *cf)
-{
-    ngx_http_variable_t  *var, *v;
-
-    for (v = ngx_http_limit_conn_vars; v->name.len; v++) {
-        var = ngx_http_add_variable(cf, &v->name, v->flags);
-        if (var == NULL) {
-            return NGX_ERROR;
-        }
-
-        var->get_handler = v->get_handler;
-        var->data = v->data;
-    }
-
-    return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_http_limit_conn_init(ngx_conf_t *cf)
-{
-    ngx_http_handler_pt        *h;
-    ngx_http_core_main_conf_t  *cmcf;
-
-    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
-
-    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
-    if (h == NULL) {
-        return NGX_ERROR;
-    }
-
-    *h = ngx_http_limit_conn_handler;
-
-    return NGX_OK;
-}
diff --git a/src/mail/ngx_mail_pop3_module.c b/src/mail/ngx_mail_pop3_module.c
--- a/src/mail/ngx_mail_pop3_module.c
+++ b/src/mail/ngx_mail_pop3_module.c
@@ -21,6 +21,7 @@ static ngx_str_t  ngx_mail_pop3_default_
     ngx_string("TOP"),
     ngx_string("USER"),
     ngx_string("UIDL"),
+    ngx_string("RESP-CODES"),
     ngx_null_string
 };
 


More information about the nginx-devel mailing list