view src/imap/ngx_imap.c @ 196:8759b346e431 NGINX_0_3_45

nginx 0.3.45 *) Feature: the "ssl_verify_client", "ssl_verify_depth", and "ssl_client_certificate" directives. *) Change: the $request_method variable now returns the main request method. *) Change: the ° symbol codes were changed in koi-win conversion table. *) Feature: the euro É N symbols were added to koi-win conversion table. *) Bugfix: if nginx distributed the requests among several backends and some backend failed, then requests intended for this backend was directed to one live backend only instead of being distributed among the rest.
author Igor Sysoev <http://sysoev.ru>
date Sat, 06 May 2006 00:00:00 +0400
parents 3689cd4e3228
children
line wrap: on
line source


/*
 * Copyright (C) Igor Sysoev
 */


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_imap.h>


static char *ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static int ngx_libc_cdecl ngx_imap_cmp_conf_in_addrs(const void *one,
    const void *two);


ngx_uint_t  ngx_imap_max_module;


static ngx_command_t  ngx_imap_commands[] = {

    { ngx_string("imap"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_imap_block,
      0,
      0,
      NULL },

      ngx_null_command
};


static ngx_core_module_t  ngx_imap_module_ctx = {
    ngx_string("imap"),
    NULL,
    NULL
};


ngx_module_t  ngx_imap_module = {
    NGX_MODULE_V1,
    &ngx_imap_module_ctx,                  /* module context */
    ngx_imap_commands,                     /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};


static char *
ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                        *rv;
    u_char                      *text;
    size_t                       len;
    ngx_uint_t                   i, a, l, m, mi, s, p, last, bind_all, done;
    ngx_conf_t                   pcf;
    ngx_array_t                  in_ports;
    ngx_listening_t             *ls;
    ngx_imap_listen_t           *imls;
    ngx_imap_module_t           *module;
    ngx_imap_in_port_t          *imip;
    ngx_imap_conf_ctx_t         *ctx;
    ngx_imap_conf_in_port_t     *in_port;
    ngx_imap_conf_in_addr_t     *in_addr;
    ngx_imap_core_srv_conf_t   **cscfp;
    ngx_imap_core_main_conf_t   *cmcf;

    /* the main imap context */

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_imap_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    *(ngx_imap_conf_ctx_t **) conf = ctx;

    /* count the number of the http modules and set up their indices */

    ngx_imap_max_module = 0;
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
            continue;
        }

        ngx_modules[m]->ctx_index = ngx_imap_max_module++;
    }


    /* the imap main_conf context, it is the same in the all imap contexts */

    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_imap_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * the imap null srv_conf context, it is used to merge
     * the server{}s' srv_conf's
     */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_imap_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * create the main_conf's, the null srv_conf's, and the null loc_conf's
     * of the all imap modules
     */

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        mi = ngx_modules[m]->ctx_index;

        if (module->create_main_conf) {
            ctx->main_conf[mi] = module->create_main_conf(cf);
            if (ctx->main_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        if (module->create_srv_conf) {
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
            if (ctx->srv_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }


    /* parse inside the imap{} block */

    pcf = *cf;
    cf->ctx = ctx;

    cf->module_type = NGX_IMAP_MODULE;
    cf->cmd_type = NGX_IMAP_MAIN_CONF;
    rv = ngx_conf_parse(cf, NULL);

    if (rv != NGX_CONF_OK) {
        *cf = pcf;
        return rv;
    }


    /* init imap{} main_conf's, merge the server{}s' srv_conf's */

    cmcf = ctx->main_conf[ngx_imap_core_module.ctx_index];
    cscfp = cmcf->servers.elts;

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        mi = ngx_modules[m]->ctx_index;

        /* init imap{} main_conf's */

        if (module->init_main_conf) {
            rv = module->init_main_conf(cf, ctx->main_conf[mi]);
            if (rv != NGX_CONF_OK) {
                *cf = pcf;
                return rv;
            }
        }

        for (s = 0; s < cmcf->servers.nelts; s++) {

            /* merge the server{}s' srv_conf's */

            if (module->merge_srv_conf) {
                rv = module->merge_srv_conf(cf,
                                            ctx->srv_conf[mi],
                                            cscfp[s]->ctx->srv_conf[mi]);
                if (rv != NGX_CONF_OK) {
                    *cf = pcf;
                    return rv;
                }
            }
        }
    }

    /* imap{}'s cf->ctx was needed while the configuration merging */

    *cf = pcf;


    if (ngx_array_init(&in_ports, cf->temp_pool, 4,
                       sizeof(ngx_imap_conf_in_port_t))
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    imls = cmcf->listen.elts;

    for (l = 0; l < cmcf->listen.nelts; l++) {

        /* AF_INET only */

        in_port = in_ports.elts;
        for (p = 0; p < in_ports.nelts; p++) {
            if (in_port[p].port == imls[l].port) {
                in_port = &in_port[p];
                goto found;
            }
        }

        in_port = ngx_array_push(&in_ports);
        if (in_port == NULL) {
            return NGX_CONF_ERROR;
        }

        in_port->port = imls[l].port;

        if (ngx_array_init(&in_port->addrs, cf->temp_pool, 2,
                           sizeof(ngx_imap_conf_in_addr_t))
            != NGX_OK)
        {
            return NGX_CONF_ERROR;
        }

    found:

        in_addr = ngx_array_push(&in_port->addrs);
        if (in_addr == NULL) {
            return NGX_CONF_ERROR;
        }

        in_addr->addr = imls[l].addr;
        in_addr->ctx = imls[l].ctx;
        in_addr->bind = imls[l].bind;
    }

    /* optimize the lists of ports and addresses */

    /* AF_INET only */

    in_port = in_ports.elts;
    for (p = 0; p < in_ports.nelts; p++) {

        ngx_qsort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts,
                  sizeof(ngx_imap_conf_in_addr_t), ngx_imap_cmp_conf_in_addrs);

        in_addr = in_port[p].addrs.elts;
        last = in_port[p].addrs.nelts;

        /*
         * if there is the binding to the "*:port" then we need to bind()
         * to the "*:port" only and ignore the other bindings
         */

        if (in_addr[last - 1].addr == INADDR_ANY) {
            in_addr[last - 1].bind = 1;
            bind_all = 0;

        } else {
            bind_all = 1;
        }

        for (a = 0; a < last; /* void */ ) {

            if (!bind_all && !in_addr[a].bind) {
                a++;
                continue;
            }

            ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
                                                  in_port[p].port);
            if (ls == NULL) {
                return NGX_CONF_ERROR;
            }

            ls->backlog = -1;
            ls->rcvbuf = -1;
            ls->sndbuf = -1;

            ls->addr_ntop = 1;
            ls->handler = ngx_imap_init_connection;
            ls->pool_size = 256;

            /* STUB */
            ls->log = *cf->cycle->new_log;
            ls->log.data = &ls->addr_text;
            ls->log.handler = ngx_accept_log_error;
            /**/

            imip = ngx_palloc(cf->pool, sizeof(ngx_imap_in_port_t));
            if (imip == NULL) {
                return NGX_CONF_ERROR;
            }

            ls->servers = imip;

            in_addr = in_port[p].addrs.elts;

            if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) {
                imip->naddrs = 1;
                done = 0;

            } else if (in_port[p].addrs.nelts > 1
                       && in_addr[last - 1].addr == INADDR_ANY)
            {
                imip->naddrs = last;
                done = 1;

            } else {
                imip->naddrs = 1;
                done = 0;
            }

#if 0
            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
                          "%ui: %V %d %ui %ui",
                          a, &ls->addr_text, in_addr[a].bind,
                          imip->naddrs, last);
#endif

            imip->addrs = ngx_pcalloc(cf->pool,
                                    imip->naddrs * sizeof(ngx_imap_in_addr_t));
            if (imip->addrs == NULL) {
                return NGX_CONF_ERROR;
            }

            for (i = 0; i < imip->naddrs; i++) {
                imip->addrs[i].addr = in_addr[i].addr;
                imip->addrs[i].ctx = in_addr[i].ctx;

                text = ngx_palloc(cf->pool,
                                  INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1);
                if (text == NULL) {
                    return NGX_CONF_ERROR;
                }

                len = ngx_inet_ntop(AF_INET, &in_addr[i].addr, text,
                                    INET_ADDRSTRLEN);

                len = ngx_sprintf(text + len, ":%d", in_port[p].port) - text;

                imip->addrs[i].addr_text.len = len;
                imip->addrs[i].addr_text.data = text;
            }

            if (done) {
                break;
            }

            in_addr++;
            in_port[p].addrs.elts = in_addr;
            last--;

            a = 0;
        }
    }

    return NGX_CONF_OK;
}


static int ngx_libc_cdecl
ngx_imap_cmp_conf_in_addrs(const void *one, const void *two)
{
    ngx_imap_conf_in_addr_t  *first, *second;

    first = (ngx_imap_conf_in_addr_t *) one;
    second = (ngx_imap_conf_in_addr_t *) two;

    if (first->addr == INADDR_ANY) {
        /* the INADDR_ANY must be the last resort, shift it to the end */
        return 1;
    }

    if (first->bind && !second->bind) {
        /* shift explicit bind()ed addresses to the start */
        return -1;
    }

    if (!first->bind && second->bind) {
        /* shift explicit bind()ed addresses to the start */
        return 1;
    }

    /* do not sort by default */

    return 0;
}