changeset 1892:057d362ee50e

resolver in smtp proxy module
author Igor Sysoev <igor@sysoev.ru>
date Wed, 13 Feb 2008 13:50:04 +0000
parents 782af1038115
children 685048830623
files src/mail/ngx_mail.h src/mail/ngx_mail_core_module.c src/mail/ngx_mail_proxy_module.c src/mail/ngx_mail_smtp_handler.c src/mail/ngx_mail_smtp_module.c
diffstat 5 files changed, 244 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -81,6 +81,7 @@ typedef struct {
     ngx_mail_protocol_t    *protocol;
 
     ngx_msec_t              timeout;
+    ngx_msec_t              resolver_timeout;
 
     ngx_flag_t              so_keepalive;
 
@@ -89,6 +90,8 @@ typedef struct {
     u_char                 *file_name;
     ngx_int_t               line;
 
+    ngx_resolver_t         *resolver;
+
     /* server ctx */
     ngx_mail_conf_ctx_t    *ctx;
 } ngx_mail_core_srv_conf_t;
@@ -147,6 +150,8 @@ typedef struct {
     void                  **main_conf;
     void                  **srv_conf;
 
+    ngx_resolver_ctx_t     *resolver_ctx;
+
     ngx_mail_proxy_ctx_t   *proxy;
 
     ngx_uint_t              mail_state;
@@ -171,6 +176,7 @@ typedef struct {
     ngx_str_t               text;
 
     ngx_str_t              *addr_text;
+    ngx_str_t               host;
     ngx_str_t               smtp_helo;
 
     ngx_uint_t              command;
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -20,6 +20,8 @@ static char *ngx_mail_core_listen(ngx_co
     void *conf);
 static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 
 static ngx_command_t  ngx_mail_core_commands[] = {
@@ -66,6 +68,20 @@ static ngx_command_t  ngx_mail_core_comm
       offsetof(ngx_mail_core_srv_conf_t, server_name),
       NULL },
 
+    { ngx_string("resolver"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_mail_core_resolver,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("resolver_timeout"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_msec_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),
+      NULL },
+
       ngx_null_command
 };
 
@@ -138,11 +154,16 @@ ngx_mail_core_create_srv_conf(ngx_conf_t
      * set by ngx_pcalloc():
      *
      *     cscf->protocol = NULL;
+     *     cscf->resolver = NULL;
      */
 
     cscf->timeout = NGX_CONF_UNSET_MSEC;
+    cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
     cscf->so_keepalive = NGX_CONF_UNSET;
 
+    cscf->file_name = cf->conf_file->file.name.data;
+    cscf->line = cf->conf_file->line;
+
     return cscf;
 }
 
@@ -154,6 +175,8 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
     ngx_mail_core_srv_conf_t *conf = child;
 
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+    ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
+                              30000);
 
     ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0);
 
@@ -184,6 +207,10 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t 
         return NGX_CONF_ERROR;
     }
 
+    if (conf->resolver == NULL) {
+        conf->resolver = prev->resolver;
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -237,9 +264,6 @@ ngx_mail_core_server(ngx_conf_t *cf, ngx
     cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index];
     cscf->ctx = ctx;
 
-    cscf->file_name = cf->conf_file->file.name.data;
-    cscf->line = cf->conf_file->line;
-
     cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];
 
     cscfp = ngx_array_push(&cmcf->servers);
@@ -389,6 +413,35 @@ ngx_mail_core_protocol(ngx_conf_t *cf, n
 }
 
 
+static char *
+ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_mail_core_srv_conf_t  *cscf = conf;
+
+    ngx_url_t   u;
+    ngx_str_t  *value;
+
+    value = cf->args->elts;
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    u.host = value[1];
+    u.port = 53;
+
+    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V: %s", &u.host, u.err);
+        return NGX_CONF_ERROR;
+    }
+
+    cscf->resolver = ngx_resolver_create(&u.addrs[0], cf->cycle->new_log);
+    if (cscf->resolver == NULL) {
+        return NGX_OK;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 char *
 ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -528,10 +528,10 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
 
         s->connection->log->action = "sending XCLIENT to upstream";
 
-        line.len = sizeof("XCLIENT PROTO=SMTP HELO= ADDR= LOGIN= "
-                          "NAME=[UNAVAILABLE]" CRLF) - 1
+        line.len = sizeof("XCLIENT PROTO=SMTP HELO= ADDR= LOGIN= NAME="
+                          CRLF) - 1
                    + s->esmtp + s->smtp_helo.len
-                   + s->connection->addr_text.len + s->login.len;
+                   + s->connection->addr_text.len + s->login.len + s->host.len;
 
         line.data = ngx_palloc(c->pool, line.len);
         if (line.data == NULL) {
@@ -542,15 +542,14 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         if (s->smtp_helo.len) {
             line.len = ngx_sprintf(line.data,
                            "XCLIENT PROTO=%sSMTP HELO=%V ADDR=%V LOGIN=%V "
-                           "NAME=[UNAVAILABLE]" CRLF,
+                           "NAME=%V" CRLF,
                            (s->esmtp ? "E" : ""), &s->smtp_helo,
-                           &s->connection->addr_text, &s->login)
+                           &s->connection->addr_text, &s->login, &s->host)
                        - line.data;
         } else {
             line.len = ngx_sprintf(line.data,
-                           "XCLIENT PROTO=SMTP ADDR=%V LOGIN=%V "
-                           "NAME=[UNAVAILABLE]" CRLF,
-                           &s->connection->addr_text, &s->login)
+                           "XCLIENT PROTO=SMTP ADDR=%V LOGIN=%V NAME=%V" CRLF,
+                           &s->connection->addr_text, &s->login, &s->host)
                        - line.data;
         }
 
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -11,6 +11,9 @@
 #include <ngx_mail_smtp_module.h>
 
 
+static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
 static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
 static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
     ngx_connection_t *c);
@@ -40,13 +43,176 @@ static u_char  smtp_invalid_argument[] =
 static u_char  smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;
 
 
+static ngx_str_t  smtp_unavailable = ngx_string("[UNAVAILABLE]");
+static ngx_str_t  smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");
+
+
 void
 ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
 {
+    struct sockaddr_in        *sin;
+    ngx_resolver_ctx_t        *ctx;
+    ngx_mail_core_srv_conf_t  *cscf;
+
+    c->log->action = "in resolving client address";
+
+    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+    ctx = ngx_resolve_start(cscf->resolver, NULL);
+    if (ctx == NULL) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    /* AF_INET only */
+
+    sin = (struct sockaddr_in *) c->sockaddr;
+
+    ctx->addr = sin->sin_addr.s_addr;
+    ctx->handler = ngx_mail_smtp_resolve_addr_handler;
+    ctx->data = s;
+    ctx->timeout = cscf->resolver_timeout;
+
+    if (ngx_resolve_addr(ctx) != NGX_OK) {
+        ngx_mail_close_connection(c);
+    }
+}
+
+
+static void
+ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)
+{
+    ngx_connection_t          *c;
+    ngx_mail_session_t        *s;
+    ngx_mail_core_srv_conf_t  *cscf;
+
+    s = ctx->data;
+    c = s->connection;
+
+    if (ctx->state) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "%V could not be resolved (%i: %s)",
+                      &c->addr_text, ctx->state,
+                      ngx_resolver_strerror(ctx->state));
+
+        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+            s->host = smtp_unavailable;
+
+        } else {
+            s->host = smtp_tempunavail;
+        }
+
+        ngx_resolve_addr_done(ctx);
+
+        ngx_mail_smtp_greeting(s, s->connection);
+
+        return;
+    }
+
+    c->log->action = "in resolving client hostname";
+
+    s->host.data = ngx_pstrdup(c->pool, &ctx->name);
+    if (s->host.data == NULL) {
+        ngx_resolve_addr_done(ctx);
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    s->host.len = ctx->name.len;
+
+    ngx_resolve_addr_done(ctx);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "address resolved: %V", &s->host);
+
+    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+    ctx = ngx_resolve_start(cscf->resolver, NULL);
+    if (ctx == NULL) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+
+    ctx->name = s->host;
+    ctx->type = NGX_RESOLVE_A;
+    ctx->handler = ngx_mail_smtp_resolve_name_handler;
+    ctx->data = s;
+    ctx->timeout = cscf->resolver_timeout;
+
+    if (ngx_resolve_name(ctx) != NGX_OK) {
+        ngx_mail_close_connection(c);
+    }
+}
+
+
+static void
+ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)
+{
+    in_addr_t            addr;
+    ngx_uint_t           i;
+    ngx_connection_t    *c;
+    struct sockaddr_in  *sin;
+    ngx_mail_session_t  *s;
+
+    s = ctx->data;
+    c = s->connection;
+
+    if (ctx->state) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "%V could not be resolved (%i: %s)",
+                      &ctx->name, ctx->state,
+                      ngx_resolver_strerror(ctx->state));
+
+        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+            s->host = smtp_unavailable;
+
+        } else {
+            s->host = smtp_tempunavail;
+        }
+
+    } else {
+
+        /* AF_INET only */
+
+        sin = (struct sockaddr_in *) c->sockaddr;
+
+        for (i = 0; i < ctx->naddrs; i++) {
+
+            addr = ctx->addrs[i];
+
+            ngx_log_debug4(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "name was resolved to %ud.%ud.%ud.%ud",
+                           (ntohl(addr) >> 24) & 0xff,
+                           (ntohl(addr) >> 16) & 0xff,
+                           (ntohl(addr) >> 8) & 0xff,
+                           ntohl(addr) & 0xff);
+
+            if (addr == sin->sin_addr.s_addr) {
+                goto found;
+            }
+        }
+
+        s->host = smtp_unavailable;
+    }
+
+found:
+
+    ngx_resolve_name_done(ctx);
+
+    ngx_mail_smtp_greeting(s, c);
+}
+
+
+static void
+ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
+{
     ngx_msec_t                 timeout;
     ngx_mail_core_srv_conf_t  *cscf;
     ngx_mail_smtp_srv_conf_t  *sscf;
 
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp greeting for \"%V\"", &s->host);
+
     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
 
--- a/src/mail/ngx_mail_smtp_module.c
+++ b/src/mail/ngx_mail_smtp_module.c
@@ -158,6 +158,15 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
 
     cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module);
 
+    if (cscf->protocol->type == NGX_MAIL_SMTP_PROTOCOL
+        && cscf->resolver == NULL)
+    {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "undefined resolver for server in %s:%ui",
+                      cscf->file_name, cscf->line);
+        return NGX_CONF_ERROR;
+    }
+
     size = sizeof("220  ESMTP ready" CRLF) - 1 + cscf->server_name.len;
 
     p = ngx_palloc(cf->pool, size);