[PATCH 2 of 2] Mail: limit_conn module
Maxim Dounin
mdounin at mdounin.ru
Wed Jul 2 02:00:34 UTC 2025
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1751421436 -10800
# Wed Jul 02 04:57:16 2025 +0300
# Node ID 51e61f74584c40b0b3164b15491804ec8976432e
# Parent be87e6012ac4fdea00597d075dcfe116beecdef2
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.
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