[PATCH 2 of 3] Stream: virtual servers

Roman Arutyunyan arut at nginx.com
Thu Dec 14 06:22:24 UTC 2023


Hi,

On Wed, Dec 13, 2023 at 05:40:09PM +0400, Sergey Kandaurov wrote:
> 
> > On 10 Nov 2023, at 14:07, Roman Arutyunyan <arut at nginx.com> wrote:
> > 
> > # HG changeset patch
> > # User Roman Arutyunyan <arut at nginx.com>
> > # Date 1699035295 -14400
> > #      Fri Nov 03 22:14:55 2023 +0400
> > # Node ID 1d3464283405a4d8ac54caae9bf1815c723f04c5
> > # Parent  966331bb4936888ef2f034aa2700c130514d0b57
> > Stream: virtual servers.
> > 
> > Server name is taken either from ngx_stream_ssl_module or
> > ngx_stream_ssl_preread_module.
> > 
> 
> You may want to consider mentioning here about various
> directives introduced in this change, for the reference.

ok

> > diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c
> > --- a/src/stream/ngx_stream.c
> > +++ b/src/stream/ngx_stream.c
> > @@ -16,16 +16,34 @@ static ngx_int_t ngx_stream_init_phases(
> >     ngx_stream_core_main_conf_t *cmcf);
> > static ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf,
> >     ngx_stream_core_main_conf_t *cmcf);
> > -static ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
> > -    ngx_stream_listen_t *listen);
> > -static char *ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
> > +
> > +static ngx_int_t ngx_stream_add_addresses(ngx_conf_t *cf,
> > +    ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port,
> > +    ngx_stream_listen_opt_t *lsopt);
> > +static ngx_int_t ngx_stream_add_address(ngx_conf_t *cf,
> > +    ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port,
> > +    ngx_stream_listen_opt_t *lsopt);
> > +static ngx_int_t ngx_stream_add_server(ngx_conf_t *cf,
> > +    ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_addr_t *addr);
> > +
> > +static ngx_int_t ngx_stream_optimize_servers(ngx_conf_t *cf,
> > +    ngx_stream_core_main_conf_t *cmcf, ngx_array_t *ports);
> > +static ngx_int_t ngx_stream_server_names(ngx_conf_t *cf,
> > +    ngx_stream_core_main_conf_t *cmcf, ngx_stream_conf_addr_t *addr);
> > +static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
> > +static int ngx_libc_cdecl ngx_stream_cmp_dns_wildcards(const void *one,
> > +    const void *two);
> > +
> > +static ngx_int_t ngx_stream_init_listening(ngx_conf_t *cf,
> > +    ngx_stream_conf_port_t *port);
> > +static ngx_listening_t *ngx_stream_add_listening(ngx_conf_t *cf,
> > +    ngx_stream_conf_addr_t *addr);
> > static ngx_int_t ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
> >     ngx_stream_conf_addr_t *addr);
> > #if (NGX_HAVE_INET6)
> > static ngx_int_t ngx_stream_add_addrs6(ngx_conf_t *cf,
> >     ngx_stream_port_t *stport, ngx_stream_conf_addr_t *addr);
> > #endif
> > -static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
> > 
> > 
> > ngx_uint_t  ngx_stream_max_module;
> > @@ -74,10 +92,8 @@ static char *
> > ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> > {
> >     char                          *rv;
> > -    ngx_uint_t                     i, m, mi, s;
> > +    ngx_uint_t                     m, mi, s;
> 
> Nitpicking:
> virtual servers support is largely based on the existing code
> in http module, and we'd like to keep it as similar as possible
> to reduce maintenance costs.   Still, it has subtle differences.
> For example, http has a different declaration order in a similar
> code for ngx_http_block().  As part of this change, you may want
> to re-align this in stream to how it's done in http, up to you.
> 
> >     ngx_conf_t                     pcf;
> > -    ngx_array_t                    ports;
> > -    ngx_stream_listen_t           *listen;
> >     ngx_stream_module_t           *module;
> >     ngx_stream_conf_ctx_t         *ctx;
> >     ngx_stream_core_srv_conf_t   **cscfp;
> > @@ -251,21 +267,13 @@ ngx_stream_block(ngx_conf_t *cf, ngx_com
> >         return NGX_CONF_ERROR;
> >     }
> > 
> > -    if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_stream_conf_port_t))
> > -        != NGX_OK)
> > -    {
> > +    /* optimize the lists of ports, addresses and server names */
> > +
> > +    if (ngx_stream_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
> >         return NGX_CONF_ERROR;
> >     }
> > 
> > -    listen = cmcf->listen.elts;
> > -
> > -    for (i = 0; i < cmcf->listen.nelts; i++) {
> > -        if (ngx_stream_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
> > -            return NGX_CONF_ERROR;
> > -        }
> > -    }
> > -
> > -    return ngx_stream_optimize_servers(cf, &ports);
> > +    return NGX_CONF_OK;
> > }
> > 
> > 
> > @@ -377,73 +385,295 @@ ngx_stream_init_phase_handlers(ngx_conf_
> > }
> > 
> > 
> > -static ngx_int_t
> > -ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
> > -    ngx_stream_listen_t *listen)
> > +ngx_int_t
> > +ngx_stream_add_listen(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
> > +    ngx_stream_listen_opt_t *lsopt)
> > {
> > -    in_port_t                p;
> > -    ngx_uint_t               i;
> > -    struct sockaddr         *sa;
> > -    ngx_stream_conf_port_t  *port;
> > -    ngx_stream_conf_addr_t  *addr;
> > +    in_port_t                     p;
> > +    ngx_uint_t                    i;
> > +    struct sockaddr              *sa;
> > +    ngx_stream_conf_port_t       *port;
> > +    ngx_stream_core_main_conf_t  *cmcf;
> > +
> > +    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
> > 
> > -    sa = listen->sockaddr;
> > +    if (cmcf->ports == NULL) {
> > +        cmcf->ports = ngx_array_create(cf->temp_pool, 2,
> > +                                       sizeof(ngx_stream_conf_port_t));
> > +        if (cmcf->ports == NULL) {
> > +            return NGX_ERROR;
> > +        }
> > +    }
> > +
> > +    sa = lsopt->sockaddr;
> >     p = ngx_inet_get_port(sa);
> > 
> > -    port = ports->elts;
> > -    for (i = 0; i < ports->nelts; i++) {
> > +    port = cmcf->ports->elts;
> > +    for (i = 0; i < cmcf->ports->nelts; i++) {
> > 
> > -        if (p == port[i].port
> > -            && listen->type == port[i].type
> > -            && sa->sa_family == port[i].family)
> > +        if (p != port[i].port
> > +            || lsopt->type != port[i].type
> > +            || sa->sa_family != port[i].family)
> >         {
> > -            /* a port is already in the port list */
> > +            continue;
> > +        }
> > 
> > -            port = &port[i];
> > -            goto found;
> > -        }
> > +        /* a port is already in the port list */
> > +
> > +        return ngx_stream_add_addresses(cf, cscf, &port[i], lsopt);
> >     }
> > 
> >     /* add a port to the port list */
> > 
> > -    port = ngx_array_push(ports);
> > +    port = ngx_array_push(cmcf->ports);
> >     if (port == NULL) {
> >         return NGX_ERROR;
> >     }
> > 
> >     port->family = sa->sa_family;
> > -    port->type = listen->type;
> > +    port->type = lsopt->type;
> >     port->port = p;
> > +    port->addrs.elts = NULL;
> > +
> > +    return ngx_stream_add_address(cf, cscf, port, lsopt);
> > +}
> > +
> > +
> > +static ngx_int_t
> > +ngx_stream_add_addresses(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
> > +    ngx_stream_conf_port_t *port, ngx_stream_listen_opt_t *lsopt)
> > +{
> > +    ngx_uint_t               i, default_server, proxy_protocol,
> > +                             protocols, protocols_prev;
> > +    ngx_stream_conf_addr_t  *addr;
> > +#if (NGX_STREAM_SSL)
> > +    ngx_uint_t               ssl;
> > +#endif
> > +
> > +    /*
> > +     * we cannot compare whole sockaddr struct's as kernel
> > +     * may fill some fields in inherited sockaddr struct's
> > +     */
> > +
> > +    addr = port->addrs.elts;
> > +
> > +    for (i = 0; i < port->addrs.nelts; i++) {
> > +
> > +        if (ngx_cmp_sockaddr(lsopt->sockaddr, lsopt->socklen,
> > +                             addr[i].opt.sockaddr,
> > +                             addr[i].opt.socklen, 0)
> > +            != NGX_OK)
> > +        {
> > +            continue;
> > +        }
> > +
> > +        /* the address is already in the address list */
> > +
> > +        if (ngx_stream_add_server(cf, cscf, &addr[i]) != NGX_OK) {
> > +            return NGX_ERROR;
> > +        }
> > +
> > +        /* preserve default_server bit during listen options overwriting */
> > +        default_server = addr[i].opt.default_server;
> > +
> > +        proxy_protocol = lsopt->proxy_protocol || addr[i].opt.proxy_protocol;
> > +        protocols = lsopt->proxy_protocol;
> > +        protocols_prev = addr[i].opt.proxy_protocol;
> > +
> > +#if (NGX_STREAM_SSL)
> > +        ssl = lsopt->ssl || addr[i].opt.ssl;
> > +        protocols |= lsopt->ssl << 1;
> > +        protocols_prev |= addr[i].opt.ssl << 1;
> > +#endif
> > +
> > +        if (lsopt->set) {
> > +
> > +            if (addr[i].opt.set) {
> > +                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > +                                   "duplicate listen options for %V",
> > +                                   &addr[i].opt.addr_text);
> > +                return NGX_ERROR;
> > +            }
> > +
> > +            addr[i].opt = *lsopt;
> > +        }
> > +
> > +        /* check the duplicate "default" server for this address:port */
> > 
> > -    if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
> > -                       sizeof(ngx_stream_conf_addr_t))
> > -        != NGX_OK)
> > -    {
> > -        return NGX_ERROR;
> > +        if (lsopt->default_server) {
> > +
> > +            if (default_server) {
> > +                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > +                                   "a duplicate default server for %V",
> > +                                   &addr[i].opt.addr_text);
> > +                return NGX_ERROR;
> > +            }
> > +
> > +            default_server = 1;
> > +            addr[i].default_server = cscf;
> > +        }
> > +
> > +        /* check for conflicting protocol options */
> > +
> > +        if ((protocols | protocols_prev) != protocols_prev) {
> > +
> > +            /* options added */
> > +
> > +            if ((addr[i].opt.set && !lsopt->set)
> > +                || addr[i].protocols_changed
> > +                || (protocols | protocols_prev) != protocols)
> > +            {
> > +                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
> > +                                   "protocol options redefined for %V",
> > +                                   &addr[i].opt.addr_text);
> > +            }
> > +
> > +            addr[i].protocols = protocols_prev;
> > +            addr[i].protocols_set = 1;
> > +            addr[i].protocols_changed = 1;
> > +
> > +        } else if ((protocols_prev | protocols) != protocols) {
> > +
> > +            /* options removed */
> > +
> > +            if (lsopt->set
> > +                || (addr[i].protocols_set && protocols != addr[i].protocols))
> > +            {
> > +                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
> > +                                   "protocol options redefined for %V",
> > +                                   &addr[i].opt.addr_text);
> > +            }
> > +
> > +            addr[i].protocols = protocols;
> > +            addr[i].protocols_set = 1;
> > +            addr[i].protocols_changed = 1;
> > +
> > +        } else {
> > +
> > +            /* the same options */
> > +
> > +            if ((lsopt->set && addr[i].protocols_changed)
> > +                || (addr[i].protocols_set && protocols != addr[i].protocols))
> > +            {
> > +                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
> > +                                   "protocol options redefined for %V",
> > +                                   &addr[i].opt.addr_text);
> > +            }
> > +
> > +            addr[i].protocols = protocols;
> > +            addr[i].protocols_set = 1;
> > +        }
> > +
> > +        addr[i].opt.default_server = default_server;
> > +        addr[i].opt.proxy_protocol = proxy_protocol;
> > +#if (NGX_STREAM_SSL)
> > +        addr[i].opt.ssl = ssl;
> > +#endif
> > +
> > +        return NGX_OK;
> >     }
> > 
> > -found:
> > +    /* add the address to the addresses list that bound to this port */
> > +
> > +    return ngx_stream_add_address(cf, cscf, port, lsopt);
> > +}
> > +
> > +
> > +/*
> > + * add the server address, the server names and the server core module
> > + * configurations to the port list
> > + */
> > +
> > +static ngx_int_t
> > +ngx_stream_add_address(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
> > +    ngx_stream_conf_port_t *port, ngx_stream_listen_opt_t *lsopt)
> > +{
> > +    ngx_stream_conf_addr_t  *addr;
> > +
> > +    if (port->addrs.elts == NULL) {
> > +        if (ngx_array_init(&port->addrs, cf->temp_pool, 4,
> > +                           sizeof(ngx_stream_conf_addr_t))
> > +            != NGX_OK)
> > +        {
> > +            return NGX_ERROR;
> > +        }
> > +    }
> > 
> >     addr = ngx_array_push(&port->addrs);
> >     if (addr == NULL) {
> >         return NGX_ERROR;
> >     }
> > 
> > -    addr->opt = *listen;
> > +    addr->opt = *lsopt;
> > +    addr->protocols = 0;
> > +    addr->protocols_set = 0;
> > +    addr->protocols_changed = 0;
> > +    addr->hash.buckets = NULL;
> > +    addr->hash.size = 0;
> > +    addr->wc_head = NULL;
> > +    addr->wc_tail = NULL;
> > +#if (NGX_PCRE)
> > +    addr->nregex = 0;
> > +    addr->regex = NULL;
> > +#endif
> > +    addr->default_server = cscf;
> > +    addr->servers.elts = NULL;
> > +
> > +    return ngx_stream_add_server(cf, cscf, addr);
> > +}
> > +
> > +
> > +/* add the server core module configuration to the address:port */
> > +
> > +static ngx_int_t
> > +ngx_stream_add_server(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
> > +    ngx_stream_conf_addr_t *addr)
> > +{
> > +    ngx_uint_t                    i;
> > +    ngx_stream_core_srv_conf_t  **server;
> > +
> > +    if (addr->servers.elts == NULL) {
> > +        if (ngx_array_init(&addr->servers, cf->temp_pool, 4,
> > +                           sizeof(ngx_stream_core_srv_conf_t *))
> > +            != NGX_OK)
> > +        {
> > +            return NGX_ERROR;
> > +        }
> > +
> > +    } else {
> > +        server = addr->servers.elts;
> > +        for (i = 0; i < addr->servers.nelts; i++) {
> > +            if (server[i] == cscf) {
> > +                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > +                                   "a duplicate listen %V",
> > +                                   &addr->opt.addr_text);
> > +                return NGX_ERROR;
> > +            }
> > +        }
> > +    }
> > +
> > +    server = ngx_array_push(&addr->servers);
> > +    if (server == NULL) {
> > +        return NGX_ERROR;
> > +    }
> > +
> > +    *server = cscf;
> > 
> >     return NGX_OK;
> > }
> > 
> > 
> > -static char *
> > -ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
> > +static ngx_int_t
> > +ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf,
> > +    ngx_array_t *ports)
> > {
> > -    ngx_uint_t                   i, p, last, bind_wildcard;
> > -    ngx_listening_t             *ls;
> > -    ngx_stream_port_t           *stport;
> > -    ngx_stream_conf_port_t      *port;
> > -    ngx_stream_conf_addr_t      *addr;
> > -    ngx_stream_core_srv_conf_t  *cscf;
> > +    ngx_uint_t               p, a;
> > +    ngx_stream_conf_port_t  *port;
> > +    ngx_stream_conf_addr_t  *addr;
> > +
> > +    if (ports == NULL) {
> > +        return NGX_OK;
> > +    }
> > 
> >     port = ports->elts;
> >     for (p = 0; p < ports->nelts; p++) {
> > @@ -451,175 +681,191 @@ ngx_stream_optimize_servers(ngx_conf_t *
> >         ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
> >                  sizeof(ngx_stream_conf_addr_t), ngx_stream_cmp_conf_addrs);
> > 
> > -        addr = port[p].addrs.elts;
> > -        last = 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
> > +         * check whether all name-based servers have the same
> > +         * configuration as a default server for given address:port
> >          */
> > 
> > -        if (addr[last - 1].opt.wildcard) {
> > -            addr[last - 1].opt.bind = 1;
> > -            bind_wildcard = 1;
> > +        addr = port[p].addrs.elts;
> > +        for (a = 0; a < port[p].addrs.nelts; a++) {
> > 
> > -        } else {
> > -            bind_wildcard = 0;
> > +            if (addr[a].servers.nelts > 1
> > +#if (NGX_PCRE)
> > +                || addr[a].default_server->captures
> > +#endif
> > +               )
> > +            {
> > +                if (ngx_stream_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
> > +                    return NGX_ERROR;
> > +                }
> > +            }
> >         }
> > 
> > -        i = 0;
> > -
> > -        while (i < last) {
> > -
> > -            if (bind_wildcard && !addr[i].opt.bind) {
> > -                i++;
> > -                continue;
> > -            }
> > -
> > -            ls = ngx_create_listening(cf, addr[i].opt.sockaddr,
> > -                                      addr[i].opt.socklen);
> > -            if (ls == NULL) {
> > -                return NGX_CONF_ERROR;
> > -            }
> > -
> > -            ls->addr_ntop = 1;
> > -            ls->handler = ngx_stream_init_connection;
> > -            ls->pool_size = 256;
> > -            ls->type = addr[i].opt.type;
> > -
> > -            cscf = addr->opt.ctx->srv_conf[ngx_stream_core_module.ctx_index];
> > -
> > -            ls->logp = cscf->error_log;
> > -            ls->log.data = &ls->addr_text;
> > -            ls->log.handler = ngx_accept_log_error;
> > -
> > -            ls->backlog = addr[i].opt.backlog;
> > -            ls->rcvbuf = addr[i].opt.rcvbuf;
> > -            ls->sndbuf = addr[i].opt.sndbuf;
> > -
> > -            ls->wildcard = addr[i].opt.wildcard;
> > -
> > -            ls->keepalive = addr[i].opt.so_keepalive;
> > -#if (NGX_HAVE_KEEPALIVE_TUNABLE)
> > -            ls->keepidle = addr[i].opt.tcp_keepidle;
> > -            ls->keepintvl = addr[i].opt.tcp_keepintvl;
> > -            ls->keepcnt = addr[i].opt.tcp_keepcnt;
> > -#endif
> > -
> > -#if (NGX_HAVE_INET6)
> > -            ls->ipv6only = addr[i].opt.ipv6only;
> > -#endif
> > -
> > -#if (NGX_HAVE_TCP_FASTOPEN)
> > -            ls->fastopen = addr[i].opt.fastopen;
> > -#endif
> > -
> > -#if (NGX_HAVE_REUSEPORT)
> > -            ls->reuseport = addr[i].opt.reuseport;
> > -#endif
> > -
> > -            stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));
> > -            if (stport == NULL) {
> > -                return NGX_CONF_ERROR;
> > -            }
> > -
> > -            ls->servers = stport;
> > -
> > -            stport->naddrs = i + 1;
> > -
> > -            switch (ls->sockaddr->sa_family) {
> > -#if (NGX_HAVE_INET6)
> > -            case AF_INET6:
> > -                if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) {
> > -                    return NGX_CONF_ERROR;
> > -                }
> > -                break;
> > -#endif
> > -            default: /* AF_INET */
> > -                if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {
> > -                    return NGX_CONF_ERROR;
> > -                }
> > -                break;
> > -            }
> > -
> > -            addr++;
> > -            last--;
> > +        if (ngx_stream_init_listening(cf, &port[p]) != NGX_OK) {
> > +            return NGX_ERROR;
> >         }
> >     }
> > 
> > -    return NGX_CONF_OK;
> > -}
> > -
> > -
> > -static ngx_int_t
> > -ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
> > -    ngx_stream_conf_addr_t *addr)
> > -{
> > -    ngx_uint_t             i;
> > -    struct sockaddr_in    *sin;
> > -    ngx_stream_in_addr_t  *addrs;
> > -
> > -    stport->addrs = ngx_pcalloc(cf->pool,
> > -                                stport->naddrs * sizeof(ngx_stream_in_addr_t));
> > -    if (stport->addrs == NULL) {
> > -        return NGX_ERROR;
> > -    }
> > -
> > -    addrs = stport->addrs;
> > -
> > -    for (i = 0; i < stport->naddrs; i++) {
> > -
> > -        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
> > -        addrs[i].addr = sin->sin_addr.s_addr;
> > -
> > -        addrs[i].conf.ctx = addr[i].opt.ctx;
> > -#if (NGX_STREAM_SSL)
> > -        addrs[i].conf.ssl = addr[i].opt.ssl;
> > -#endif
> > -        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
> > -        addrs[i].conf.addr_text = addr[i].opt.addr_text;
> > -    }
> > -
> >     return NGX_OK;
> > }
> > 
> > 
> > -#if (NGX_HAVE_INET6)
> > -
> > static ngx_int_t
> > -ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
> > +ngx_stream_server_names(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf,
> >     ngx_stream_conf_addr_t *addr)
> > {
> > -    ngx_uint_t              i;
> > -    struct sockaddr_in6    *sin6;
> > -    ngx_stream_in6_addr_t  *addrs6;
> > +    ngx_int_t                     rc;
> > +    ngx_uint_t                    n, s;
> > +    ngx_hash_init_t               hash;
> > +    ngx_hash_keys_arrays_t        ha;
> > +    ngx_stream_server_name_t     *name;
> > +    ngx_stream_core_srv_conf_t  **cscfp;
> > +#if (NGX_PCRE)
> > +    ngx_uint_t                    regex, i;
> > 
> > -    stport->addrs = ngx_pcalloc(cf->pool,
> > -                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));
> > -    if (stport->addrs == NULL) {
> > +    regex = 0;
> > +#endif
> > +
> > +    ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
> > +
> > +    ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
> > +    if (ha.temp_pool == NULL) {
> >         return NGX_ERROR;
> >     }
> > 
> > -    addrs6 = stport->addrs;
> > +    ha.pool = cf->pool;
> > +
> > +    if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
> > +        goto failed;
> > +    }
> > +
> > +    cscfp = addr->servers.elts;
> > +
> > +    for (s = 0; s < addr->servers.nelts; s++) {
> > +
> > +        name = cscfp[s]->server_names.elts;
> > +
> > +        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
> > 
> > -    for (i = 0; i < stport->naddrs; i++) {
> > +#if (NGX_PCRE)
> > +            if (name[n].regex) {
> > +                regex++;
> > +                continue;
> > +            }
> > +#endif
> > 
> > -        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
> > -        addrs6[i].addr6 = sin6->sin6_addr;
> > +            rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,
> > +                                  NGX_HASH_WILDCARD_KEY);
> > +
> > +            if (rc == NGX_ERROR) {
> > +                goto failed;
> > +            }
> > 
> > -        addrs6[i].conf.ctx = addr[i].opt.ctx;
> > -#if (NGX_STREAM_SSL)
> > -        addrs6[i].conf.ssl = addr[i].opt.ssl;
> > -#endif
> > -        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
> > -        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
> > +            if (rc == NGX_DECLINED) {
> > +                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
> > +                              "invalid server name or wildcard \"%V\" on %V",
> > +                              &name[n].name, &addr->opt.addr_text);
> > +                goto failed;
> > +            }
> > +
> > +            if (rc == NGX_BUSY) {
> > +                ngx_log_error(NGX_LOG_WARN, cf->log, 0,
> > +                              "conflicting server name \"%V\" on %V, ignored",
> > +                              &name[n].name, &addr->opt.addr_text);
> > +            }
> > +        }
> > +    }
> > +
> > +    hash.key = ngx_hash_key_lc;
> > +    hash.max_size = cmcf->server_names_hash_max_size;
> > +    hash.bucket_size = cmcf->server_names_hash_bucket_size;
> > +    hash.name = "server_names_hash";
> > +    hash.pool = cf->pool;
> > +
> > +    if (ha.keys.nelts) {
> > +        hash.hash = &addr->hash;
> > +        hash.temp_pool = NULL;
> > +
> > +        if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {
> > +            goto failed;
> > +        }
> >     }
> > 
> > -    return NGX_OK;
> > -}
> > +    if (ha.dns_wc_head.nelts) {
> > +
> > +        ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,
> > +                  sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards);
> > +
> > +        hash.hash = NULL;
> > +        hash.temp_pool = ha.temp_pool;
> > +
> > +        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
> > +                                   ha.dns_wc_head.nelts)
> > +            != NGX_OK)
> > +        {
> > +            goto failed;
> > +        }
> > +
> > +        addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;
> > +    }
> > +
> > +    if (ha.dns_wc_tail.nelts) {
> > +
> > +        ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,
> > +                  sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards);
> > +
> > +        hash.hash = NULL;
> > +        hash.temp_pool = ha.temp_pool;
> > +
> > +        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
> > +                                   ha.dns_wc_tail.nelts)
> > +            != NGX_OK)
> > +        {
> > +            goto failed;
> > +        }
> > +
> > +        addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;
> > +    }
> > +
> > +    ngx_destroy_pool(ha.temp_pool);
> > +
> > +#if (NGX_PCRE)
> > +
> > +    if (regex == 0) {
> > +        return NGX_OK;
> > +    }
> > +
> > +    addr->nregex = regex;
> > +    addr->regex = ngx_palloc(cf->pool,
> > +                             regex * sizeof(ngx_stream_server_name_t));
> > +    if (addr->regex == NULL) {
> > +        return NGX_ERROR;
> > +    }
> > +
> > +    i = 0;
> > +
> > +    for (s = 0; s < addr->servers.nelts; s++) {
> > +
> > +        name = cscfp[s]->server_names.elts;
> > +
> > +        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
> > +            if (name[n].regex) {
> > +                addr->regex[i++] = name[n];
> > +            }
> > +        }
> > +    }
> > 
> > #endif
> > 
> > +    return NGX_OK;
> > +
> > +failed:
> > +
> > +    ngx_destroy_pool(ha.temp_pool);
> > +
> > +    return NGX_ERROR;
> > +}
> > +
> > 
> > static ngx_int_t
> > ngx_stream_cmp_conf_addrs(const void *one, const void *two)
> > @@ -630,12 +876,12 @@ ngx_stream_cmp_conf_addrs(const void *on
> >     second = (ngx_stream_conf_addr_t *) two;
> > 
> >     if (first->opt.wildcard) {
> > -        /* a wildcard must be the last resort, shift it to the end */
> > +        /* a wildcard address must be the last resort, shift it to the end */
> >         return 1;
> >     }
> > 
> >     if (second->opt.wildcard) {
> > -        /* a wildcard must be the last resort, shift it to the end */
> > +        /* a wildcard address must be the last resort, shift it to the end */
> >         return -1;
> >     }
> > 
> > @@ -653,3 +899,289 @@ ngx_stream_cmp_conf_addrs(const void *on
> > 
> >     return 0;
> > }
> > +
> > +
> > +static int ngx_libc_cdecl
> > +ngx_stream_cmp_dns_wildcards(const void *one, const void *two)
> > +{
> > +    ngx_hash_key_t  *first, *second;
> > +
> > +    first = (ngx_hash_key_t *) one;
> > +    second = (ngx_hash_key_t *) two;
> > +
> > +    return ngx_dns_strcmp(first->key.data, second->key.data);
> > +}
> > +
> > +
> > +static ngx_int_t
> > +ngx_stream_init_listening(ngx_conf_t *cf, ngx_stream_conf_port_t *port)
> > +{
> > +    ngx_uint_t               i, last, bind_wildcard;
> > +    ngx_listening_t         *ls;
> > +    ngx_stream_port_t       *hport;
> 
> Here and below you renamed "stport" (as in "stream port") back to
> "hport" (as in "http port"), which apparently doesn't belong here.
> 
> > +    ngx_stream_conf_addr_t  *addr;
> > +
> > +    addr = port->addrs.elts;
> > +    last = port->addrs.nelts;
> > +
> > +    /*
> > +     * If there is a binding to an "*:port" then we need to bind() to
> > +     * the "*:port" only and ignore other implicit bindings.  The bindings
> > +     * have been already sorted: explicit bindings are on the start, then
> > +     * implicit bindings go, and wildcard binding is in the end.
> > +     */
> > +
> > +    if (addr[last - 1].opt.wildcard) {
> > +        addr[last - 1].opt.bind = 1;
> > +        bind_wildcard = 1;
> > +
> > +    } else {
> > +        bind_wildcard = 0;
> > +    }
> > +
> > +    i = 0;
> > +
> > +    while (i < last) {
> > +
> > +        if (bind_wildcard && !addr[i].opt.bind) {
> > +            i++;
> > +            continue;
> > +        }
> > +
> > +        ls = ngx_stream_add_listening(cf, &addr[i]);
> > +        if (ls == NULL) {
> > +            return NGX_ERROR;
> > +        }
> > +
> > +        hport = ngx_pcalloc(cf->pool, sizeof(ngx_stream_port_t));
> > +        if (hport == NULL) {
> > +            return NGX_ERROR;
> > +        }
> > +
> > +        ls->servers = hport;
> > +
> > +        hport->naddrs = i + 1;
> > +
> > +        switch (ls->sockaddr->sa_family) {
> > +
> > +#if (NGX_HAVE_INET6)
> > +        case AF_INET6:
> > +            if (ngx_stream_add_addrs6(cf, hport, addr) != NGX_OK) {
> > +                return NGX_ERROR;
> > +            }
> > +            break;
> > +#endif
> > +        default: /* AF_INET */
> > +            if (ngx_stream_add_addrs(cf, hport, addr) != NGX_OK) {
> > +                return NGX_ERROR;
> > +            }
> > +            break;
> > +        }
> > +
> > +        addr++;
> > +        last--;
> > +    }
> > +
> > +    return NGX_OK;
> > +}
> > +
> > +
> > +static ngx_listening_t *
> > +ngx_stream_add_listening(ngx_conf_t *cf, ngx_stream_conf_addr_t *addr)
> > +{
> > +    ngx_listening_t             *ls;
> > +    ngx_stream_core_srv_conf_t  *cscf;
> > +
> > +    ls = ngx_create_listening(cf, addr->opt.sockaddr, addr->opt.socklen);
> > +    if (ls == NULL) {
> > +        return NULL;
> > +    }
> > +
> > +    ls->addr_ntop = 1;
> > +
> > +    ls->handler = ngx_stream_init_connection;
> > +
> > +    cscf = addr->default_server;
> > +    ls->pool_size = 256;
> 
> Nitpicking.
> Current code has the following initialization order:
> 
>             ls->addr_ntop = 1;
>             ls->handler = ngx_stream_init_connection;
>             ls->pool_size = 256;
>             ls->type = addr[i].opt.type
> 
>             cscf = addr->opt.ctx->srv_conf[ngx_stream_core_module.ctx_index];
> 
> Besides "type", it makes sense to keep this order in the new code, as well:
> 
>     ls->handler = ngx_stream_init_connection;
> 
>     ls->pool_size = 256;
> 
>     cscf = addr->default_server;
> 
>     ls->logp = cscf->error_log;
> 
> > +
> > +    ls->logp = cscf->error_log;
> > +    ls->log.data = &ls->addr_text;
> > +    ls->log.handler = ngx_accept_log_error;
> > +
> 
> > +#if (NGX_WIN32)
> > +    {
> > +    ngx_iocp_conf_t  *iocpcf = NULL;
> > +
> > +    if (ngx_get_conf(cf->cycle->conf_ctx, ngx_events_module)) {
> > +        iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
> > +    }
> > +    if (iocpcf && iocpcf->acceptex_read) {
> > +        ls->post_accept_buffer_size = cscf->client_header_buffer_size;
> > +    }
> > +    }
> > +#endif
> 
> This part is out of scope of this change, and apparently it won't compile.
> 
> > +
> > +    ls->type = addr->opt.type;
> > +    ls->backlog = addr->opt.backlog;
> > +    ls->rcvbuf = addr->opt.rcvbuf;
> > +    ls->sndbuf = addr->opt.sndbuf;
> > +
> > +    ls->keepalive = addr->opt.so_keepalive;
> > +#if (NGX_HAVE_KEEPALIVE_TUNABLE)
> > +    ls->keepidle = addr->opt.tcp_keepidle;
> > +    ls->keepintvl = addr->opt.tcp_keepintvl;
> > +    ls->keepcnt = addr->opt.tcp_keepcnt;
> > +#endif
> > +
> > +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
> > +    ls->accept_filter = addr->opt.accept_filter;
> > +#endif
> > +
> > +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
> > +    ls->deferred_accept = addr->opt.deferred_accept;
> > +#endif
> > +
> > +#if (NGX_HAVE_INET6)
> > +    ls->ipv6only = addr->opt.ipv6only;
> > +#endif
> > +
> > +#if (NGX_HAVE_SETFIB)
> > +    ls->setfib = addr->opt.setfib;
> > +#endif
> 
> This introduces accept_filter, deferred_accept, and setfib fields,
> which is out of scope of this change.  Anyway, this is useless
> without corresponding support in ngx_stream_core_listen().

We need to catch up with all such missing functionality in Stream in future.

> > +#if (NGX_HAVE_TCP_FASTOPEN)
> > +    ls->fastopen = addr->opt.fastopen;
> > +#endif
> > +
> > +#if (NGX_HAVE_REUSEPORT)
> > +    ls->reuseport = addr->opt.reuseport;
> > +#endif
> > +
> > +    ls->wildcard = addr->opt.wildcard;
> > +
> > +    return ls;
> > +}
> > +
> > +
> > +static ngx_int_t
> > +ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *hport,
> > +    ngx_stream_conf_addr_t *addr)
> > +{
> > +    ngx_uint_t                   i;
> > +    struct sockaddr_in          *sin;
> > +    ngx_stream_in_addr_t        *addrs;
> > +    ngx_stream_virtual_names_t  *vn;
> > +
> > +    hport->addrs = ngx_pcalloc(cf->pool,
> > +                               hport->naddrs * sizeof(ngx_stream_in_addr_t));
> > +    if (hport->addrs == NULL) {
> > +        return NGX_ERROR;
> > +    }
> > +
> > +    addrs = hport->addrs;
> > +
> > +    for (i = 0; i < hport->naddrs; i++) {
> > +
> > +        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
> > +        addrs[i].addr = sin->sin_addr.s_addr;
> > +        addrs[i].conf.default_server = addr[i].default_server;
> > +#if (NGX_STREAM_SSL)
> > +        addrs[i].conf.ssl = addr[i].opt.ssl;
> > +#endif
> > +        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
> > +
> > +        if (addr[i].hash.buckets == NULL
> > +            && (addr[i].wc_head == NULL
> > +                || addr[i].wc_head->hash.buckets == NULL)
> > +            && (addr[i].wc_tail == NULL
> > +                || addr[i].wc_tail->hash.buckets == NULL)
> > +#if (NGX_PCRE)
> > +            && addr[i].nregex == 0
> > +#endif
> > +            )
> > +        {
> > +            continue;
> > +        }
> > +
> > +        vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t));
> > +        if (vn == NULL) {
> > +            return NGX_ERROR;
> > +        }
> > +
> > +        addrs[i].conf.virtual_names = vn;
> > +
> > +        vn->names.hash = addr[i].hash;
> > +        vn->names.wc_head = addr[i].wc_head;
> > +        vn->names.wc_tail = addr[i].wc_tail;
> > +#if (NGX_PCRE)
> > +        vn->nregex = addr[i].nregex;
> > +        vn->regex = addr[i].regex;
> > +#endif
> > +    }
> > +
> > +    return NGX_OK;
> > +}
> > +
> > +
> > +#if (NGX_HAVE_INET6)
> > +
> > +static ngx_int_t
> > +ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *hport,
> > +    ngx_stream_conf_addr_t *addr)
> > +{
> > +    ngx_uint_t                   i;
> > +    struct sockaddr_in6         *sin6;
> > +    ngx_stream_in6_addr_t       *addrs6;
> > +    ngx_stream_virtual_names_t  *vn;
> > +
> > +    hport->addrs = ngx_pcalloc(cf->pool,
> > +                               hport->naddrs * sizeof(ngx_stream_in6_addr_t));
> > +    if (hport->addrs == NULL) {
> > +        return NGX_ERROR;
> > +    }
> > +
> > +    addrs6 = hport->addrs;
> > +
> > +    for (i = 0; i < hport->naddrs; i++) {
> > +
> > +        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
> > +        addrs6[i].addr6 = sin6->sin6_addr;
> > +        addrs6[i].conf.default_server = addr[i].default_server;
> > +#if (NGX_STREAM_SSL)
> > +        addrs6[i].conf.ssl = addr[i].opt.ssl;
> > +#endif
> > +        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
> > +
> > +        if (addr[i].hash.buckets == NULL
> > +            && (addr[i].wc_head == NULL
> > +                || addr[i].wc_head->hash.buckets == NULL)
> > +            && (addr[i].wc_tail == NULL
> > +                || addr[i].wc_tail->hash.buckets == NULL)
> > +#if (NGX_PCRE)
> > +            && addr[i].nregex == 0
> > +#endif
> > +            )
> > +        {
> > +            continue;
> > +        }
> > +
> > +        vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t));
> > +        if (vn == NULL) {
> > +            return NGX_ERROR;
> > +        }
> > +
> > +        addrs6[i].conf.virtual_names = vn;
> > +
> > +        vn->names.hash = addr[i].hash;
> > +        vn->names.wc_head = addr[i].wc_head;
> > +        vn->names.wc_tail = addr[i].wc_tail;
> > +#if (NGX_PCRE)
> > +        vn->nregex = addr[i].nregex;
> > +        vn->regex = addr[i].regex;
> > +#endif
> > +    }
> > +
> > +    return NGX_OK;
> > +}
> > +
> > +#endif
> > diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h
> > --- a/src/stream/ngx_stream.h
> > +++ b/src/stream/ngx_stream.h
> > @@ -45,74 +45,39 @@ typedef struct {
> >     socklen_t                      socklen;
> >     ngx_str_t                      addr_text;
> > 
> > -    /* server ctx */
> > -    ngx_stream_conf_ctx_t         *ctx;
> > -
> > +    unsigned                       set:1;
> > +    unsigned                       default_server:1;
> >     unsigned                       bind:1;
> >     unsigned                       wildcard:1;
> >     unsigned                       ssl:1;
> > #if (NGX_HAVE_INET6)
> >     unsigned                       ipv6only:1;
> > #endif
> > +    unsigned                       deferred_accept:1;
> >     unsigned                       reuseport:1;
> >     unsigned                       so_keepalive:2;
> >     unsigned                       proxy_protocol:1;
> > +
> > +    int                            backlog;
> > +    int                            rcvbuf;
> > +    int                            sndbuf;
> > +    int                            type;
> > +#if (NGX_HAVE_SETFIB)
> > +    int                            setfib;
> > +#endif
> > +#if (NGX_HAVE_TCP_FASTOPEN)
> > +    int                            fastopen;
> > +#endif
> > #if (NGX_HAVE_KEEPALIVE_TUNABLE)
> >     int                            tcp_keepidle;
> >     int                            tcp_keepintvl;
> >     int                            tcp_keepcnt;
> > #endif
> > -    int                            backlog;
> > -    int                            rcvbuf;
> > -    int                            sndbuf;
> > -#if (NGX_HAVE_TCP_FASTOPEN)
> > -    int                            fastopen;
> > +
> > +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
> > +    char                          *accept_filter;
> > #endif
> 
> Besides introducing unused fields, this part reshuffles
> backlog .. type fields without a reason.  It can be as minimal as:
> 
> diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h
> --- a/src/stream/ngx_stream.h
> +++ b/src/stream/ngx_stream.h
> @@ -45,9 +45,8 @@ typedef struct {
>      socklen_t                      socklen;
>      ngx_str_t                      addr_text;
>  
> -    /* server ctx */
> -    ngx_stream_conf_ctx_t         *ctx;
> -
> +    unsigned                       set:1;
> +    unsigned                       default_server:1;
>      unsigned                       bind:1;
>      unsigned                       wildcard:1;
>      unsigned                       ssl:1;
> 
> > -    int                            type;
> > -} ngx_stream_listen_t;
> > -
> > -
> > -typedef struct {
> > -    ngx_stream_conf_ctx_t         *ctx;
> > -    ngx_str_t                      addr_text;
> > -    unsigned                       ssl:1;
> > -    unsigned                       proxy_protocol:1;
> > -} ngx_stream_addr_conf_t;
> > -
> > -typedef struct {
> > -    in_addr_t                      addr;
> > -    ngx_stream_addr_conf_t         conf;
> > -} ngx_stream_in_addr_t;
> > -
> > -
> > -#if (NGX_HAVE_INET6)
> > -
> > -typedef struct {
> > -    struct in6_addr                addr6;
> > -    ngx_stream_addr_conf_t         conf;
> > -} ngx_stream_in6_addr_t;
> > -
> > -#endif
> > -
> > -
> > -typedef struct {
> > -    /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
> > -    void                          *addrs;
> > -    ngx_uint_t                     naddrs;
> > -} ngx_stream_port_t;
> > -
> > -
> > -typedef struct {
> > -    int                            family;
> > -    int                            type;
> > -    in_port_t                      port;
> > -    ngx_array_t                    addrs; /* array of ngx_stream_conf_addr_t */
> > -} ngx_stream_conf_port_t;
> > -
> > -
> > -typedef struct {
> > -    ngx_stream_listen_t            opt;
> > -} ngx_stream_conf_addr_t;
> > +} ngx_stream_listen_opt_t;
> > 
> > 
> > typedef enum {
> > @@ -153,7 +118,6 @@ typedef struct {
> > 
> > typedef struct {
> >     ngx_array_t                    servers;     /* ngx_stream_core_srv_conf_t */
> > -    ngx_array_t                    listen;      /* ngx_stream_listen_t */
> > 
> >     ngx_stream_phase_engine_t      phase_engine;
> > 
> > @@ -163,16 +127,24 @@ typedef struct {
> >     ngx_array_t                    prefix_variables; /* ngx_stream_variable_t */
> >     ngx_uint_t                     ncaptures;
> > 
> > +    ngx_uint_t                     server_names_hash_max_size;
> > +    ngx_uint_t                     server_names_hash_bucket_size;
> > +
> >     ngx_uint_t                     variables_hash_max_size;
> >     ngx_uint_t                     variables_hash_bucket_size;
> > 
> >     ngx_hash_keys_arrays_t        *variables_keys;
> > 
> > +    ngx_array_t                   *ports;
> > +
> >     ngx_stream_phase_t             phases[NGX_STREAM_LOG_PHASE + 1];
> > } ngx_stream_core_main_conf_t;
> > 
> > 
> > typedef struct {
> > +    /* array of the ngx_stream_server_name_t, "server_name" directive */
> > +    ngx_array_t                    server_names;
> > +
> >     ngx_stream_content_handler_pt  handler;
> > 
> >     ngx_stream_conf_ctx_t         *ctx;
> > @@ -180,6 +152,8 @@ typedef struct {
> >     u_char                        *file_name;
> >     ngx_uint_t                     line;
> > 
> > +    ngx_str_t                      server_name;
> > +
> >     ngx_flag_t                     tcp_nodelay;
> >     size_t                         preread_buffer_size;
> >     ngx_msec_t                     preread_timeout;
> > @@ -191,10 +165,99 @@ typedef struct {
> > 
> >     ngx_msec_t                     proxy_protocol_timeout;
> > 
> > -    ngx_uint_t                     listen;  /* unsigned  listen:1; */
> > +    unsigned                       listen:1;
> > +#if (NGX_PCRE)
> > +    unsigned                       captures:1;
> > +#endif
> > } ngx_stream_core_srv_conf_t;
> > 
> > 
> > +/* list of structures to find core_srv_conf quickly at run time */
> > +
> > +
> > +typedef struct {
> > +#if (NGX_PCRE)
> > +    ngx_stream_regex_t            *regex;
> > +#endif
> > +    ngx_stream_core_srv_conf_t    *server;   /* virtual name server conf */
> > +    ngx_str_t                      name;
> > +} ngx_stream_server_name_t;
> > +
> > +
> > +typedef struct {
> > +    ngx_hash_combined_t            names;
> > +
> > +    ngx_uint_t                     nregex;
> > +    ngx_stream_server_name_t      *regex;
> > +} ngx_stream_virtual_names_t;
> > +
> > +
> > +typedef struct {
> > +    /* the default server configuration for this address:port */
> > +    ngx_stream_core_srv_conf_t    *default_server;
> > +
> > +    ngx_stream_virtual_names_t    *virtual_names;
> > +
> > +    ngx_str_t                      addr_text;
> 
> This field is now unused.
> 
> > +    unsigned                       ssl:1;
> > +    unsigned                       proxy_protocol:1;
> > +} ngx_stream_addr_conf_t;
> > +
> > +
> > +typedef struct {
> > +    in_addr_t                      addr;
> > +    ngx_stream_addr_conf_t         conf;
> > +} ngx_stream_in_addr_t;
> > +
> > +
> > +#if (NGX_HAVE_INET6)
> > +
> > +typedef struct {
> > +    struct in6_addr                addr6;
> > +    ngx_stream_addr_conf_t         conf;
> > +} ngx_stream_in6_addr_t;
> > +
> > +#endif
> > +
> > +
> > +typedef struct {
> > +    /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
> > +    void                          *addrs;
> > +    ngx_uint_t                     naddrs;
> > +} ngx_stream_port_t;
> > +
> > +
> > +typedef struct {
> > +    int                            family;
> > +    int                            type;
> > +    in_port_t                      port;
> > +    ngx_array_t                    addrs; /* array of ngx_stream_conf_addr_t */
> > +} ngx_stream_conf_port_t;
> > +
> > +
> > +typedef struct {
> > +    ngx_stream_listen_opt_t        opt;
> > +
> > +    unsigned                       protocols:3;
> > +    unsigned                       protocols_set:1;
> > +    unsigned                       protocols_changed:1;
> > +
> > +    ngx_hash_t                     hash;
> > +    ngx_hash_wildcard_t           *wc_head;
> > +    ngx_hash_wildcard_t           *wc_tail;
> > +
> > +#if (NGX_PCRE)
> > +    ngx_uint_t                     nregex;
> > +    ngx_stream_server_name_t      *regex;
> > +#endif
> > +
> > +    /* the default server configuration for this address:port */
> > +    ngx_stream_core_srv_conf_t    *default_server;
> > +    ngx_array_t                    servers;
> > +                                     /* array of ngx_stream_core_srv_conf_t */
> 
> misaligned to the right side (off by one)
> 
> > +} ngx_stream_conf_addr_t;
> > +
> > +
> > struct ngx_stream_session_s {
> >     uint32_t                       signature;         /* "STRM" */
> > 
> > @@ -210,6 +273,8 @@ struct ngx_stream_session_s {
> >     void                         **main_conf;
> >     void                         **srv_conf;
> > 
> > +    ngx_stream_virtual_names_t    *virtual_names;
> > +
> >     ngx_stream_upstream_t         *upstream;
> >     ngx_array_t                   *upstream_states;
> >                                            /* of ngx_stream_upstream_state_t */
> > @@ -283,6 +348,8 @@ typedef struct {
> > #define NGX_STREAM_WRITE_BUFFERED  0x10
> > 
> > 
> > +ngx_int_t ngx_stream_add_listen(ngx_conf_t *cf,
> > +    ngx_stream_core_srv_conf_t *cscf, ngx_stream_listen_opt_t *lsopt);
> 
> This deserves two blank lines to divide configuration and
> runtime functions, and somewhat similar to http.
> 
> > void ngx_stream_core_run_phases(ngx_stream_session_t *s);
> > ngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s,
> >     ngx_stream_phase_handler_t *ph);
> > @@ -290,6 +357,10 @@ ngx_int_t ngx_stream_core_preread_phase(
> >     ngx_stream_phase_handler_t *ph);
> > ngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s,
> >     ngx_stream_phase_handler_t *ph);
> 
> This deserves a blank line.
> 
> > +ngx_int_t ngx_stream_find_virtual_server(ngx_stream_session_t *s,
> > +    ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp);
> > +ngx_int_t ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool,
> > +    ngx_uint_t alloc);
> 
> Apparently, it makes sense to reverse-order these functions
> to make them appear in the order they are called.
> 
> > 
> > 
> > void ngx_stream_init_connection(ngx_connection_t *c);
> > diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c
> > --- a/src/stream/ngx_stream_core_module.c
> > +++ b/src/stream/ngx_stream_core_module.c
> > @@ -26,6 +26,8 @@ static char *ngx_stream_core_server(ngx_
> >     void *conf);
> > static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
> >     void *conf);
> > +static char *ngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd,
> > +    void *conf);
> > static char *ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
> >     void *conf);
> > 
> > @@ -46,6 +48,20 @@ static ngx_command_t  ngx_stream_core_co
> >       offsetof(ngx_stream_core_main_conf_t, variables_hash_bucket_size),
> >       NULL },
> > 
> > +    { ngx_string("server_names_hash_max_size"),
> > +      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
> > +      ngx_conf_set_num_slot,
> > +      NGX_STREAM_MAIN_CONF_OFFSET,
> > +      offsetof(ngx_stream_core_main_conf_t, server_names_hash_max_size),
> > +      NULL },
> > +
> > +    { ngx_string("server_names_hash_bucket_size"),
> > +      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
> > +      ngx_conf_set_num_slot,
> > +      NGX_STREAM_MAIN_CONF_OFFSET,
> > +      offsetof(ngx_stream_core_main_conf_t, server_names_hash_bucket_size),
> > +      NULL },
> > +
> >     { ngx_string("server"),
> >       NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
> >       ngx_stream_core_server,
> > @@ -60,6 +76,13 @@ static ngx_command_t  ngx_stream_core_co
> >       0,
> >       NULL },
> > 
> > +    { ngx_string("server_name"),
> > +      NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
> > +      ngx_stream_core_server_name,
> > +      NGX_STREAM_SRV_CONF_OFFSET,
> > +      0,
> > +      NULL },
> > +
> >     { ngx_string("error_log"),
> >       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
> >       ngx_stream_core_error_log,
> > @@ -413,6 +436,149 @@ ngx_stream_core_content_phase(ngx_stream
> > }
> > 
> > 
> > +ngx_int_t
> > +ngx_stream_find_virtual_server(ngx_stream_session_t *s,
> > +    ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp)
> > +{
> > +    ngx_stream_core_srv_conf_t  *cscf;
> > +
> > +    if (s->virtual_names == NULL) {
> > +        return NGX_DECLINED;
> > +    }
> > +
> > +    cscf = ngx_hash_find_combined(&s->virtual_names->names,
> > +                                  ngx_hash_key(host->data, host->len),
> > +                                  host->data, host->len);
> > +
> > +    if (cscf) {
> > +        *cscfp = cscf;
> > +        return NGX_OK;
> > +    }
> > +
> > +#if (NGX_PCRE)
> > +
> > +    if (host->len && s->virtual_names->nregex) {
> > +        ngx_int_t                  n;
> > +        ngx_uint_t                 i;
> > +        ngx_stream_server_name_t  *sn;
> > +
> > +        sn = s->virtual_names->regex;
> > +
> > +        for (i = 0; i < s->virtual_names->nregex; i++) {
> > +
> > +            n = ngx_stream_regex_exec(s, sn[i].regex, host);
> > +
> > +            if (n == NGX_DECLINED) {
> > +                continue;
> > +            }
> > +
> > +            if (n == NGX_OK) {
> > +                *cscfp = sn[i].server;
> > +                return NGX_OK;
> > +            }
> > +
> > +            return NGX_ERROR;
> > +        }
> > +    }
> > +
> > +#endif /* NGX_PCRE */
> > +
> > +    return NGX_DECLINED;
> > +}
> > +
> > +
> > +ngx_int_t
> > +ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
> > +{
> > +    u_char  *h, ch;
> > +    size_t   i, dot_pos, host_len;
> > +
> > +    enum {
> > +        sw_usual = 0,
> > +        sw_literal,
> > +        sw_rest
> > +    } state;
> > +
> > +    dot_pos = host->len;
> > +    host_len = host->len;
> > +
> > +    h = host->data;
> > +
> > +    state = sw_usual;
> > +
> > +    for (i = 0; i < host->len; i++) {
> > +        ch = h[i];
> > +
> > +        switch (ch) {
> > +
> > +        case '.':
> > +            if (dot_pos == i - 1) {
> > +                return NGX_DECLINED;
> > +            }
> > +            dot_pos = i;
> > +            break;
> > +
> > +        case ':':
> > +            if (state == sw_usual) {
> > +                host_len = i;
> > +                state = sw_rest;
> > +            }
> > +            break;
> > +
> > +        case '[':
> > +            if (i == 0) {
> > +                state = sw_literal;
> > +            }
> > +            break;
> > +
> > +        case ']':
> > +            if (state == sw_literal) {
> > +                host_len = i + 1;
> > +                state = sw_rest;
> > +            }
> > +            break;
> > +
> > +        default:
> > +
> > +            if (ngx_path_separator(ch)) {
> > +                return NGX_DECLINED;
> > +            }
> > +
> > +            if (ch <= 0x20 || ch == 0x7f) {
> > +                return NGX_DECLINED;
> > +            }
> > +
> > +            if (ch >= 'A' && ch <= 'Z') {
> > +                alloc = 1;
> > +            }
> > +
> > +            break;
> > +        }
> > +    }
> > +
> > +    if (dot_pos == host_len - 1) {
> > +        host_len--;
> > +    }
> > +
> > +    if (host_len == 0) {
> > +        return NGX_DECLINED;
> > +    }
> > +
> > +    if (alloc) {
> > +        host->data = ngx_pnalloc(pool, host_len);
> > +        if (host->data == NULL) {
> > +            return NGX_ERROR;
> > +        }
> > +
> > +        ngx_strlow(host->data, h, host_len);
> > +    }
> > +
> > +    host->len = host_len;
> > +
> > +    return NGX_OK;
> > +}
> 
> Same here.
> 
> > +
> > +
> > static ngx_int_t
> > ngx_stream_core_preconfiguration(ngx_conf_t *cf)
> > {
> > @@ -437,11 +603,8 @@ ngx_stream_core_create_main_conf(ngx_con
> >         return NULL;
> >     }
> > 
> > -    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_stream_listen_t))
> > -        != NGX_OK)
> > -    {
> > -        return NULL;
> > -    }
> > +    cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;
> > +    cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;
> > 
> >     cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;
> >     cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;
> > @@ -455,6 +618,14 @@ ngx_stream_core_init_main_conf(ngx_conf_
> > {
> >     ngx_stream_core_main_conf_t *cmcf = conf;
> > 
> > +    ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);
> > +    ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,
> > +                             ngx_cacheline_size);
> > +
> > +    cmcf->server_names_hash_bucket_size =
> > +            ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);
> > +
> > +
> 
> extra blank line
> 
> >     ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);
> >     ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);
> > 
> > @@ -486,6 +657,13 @@ ngx_stream_core_create_srv_conf(ngx_conf
> >      *     cscf->error_log = NULL;
> >      */
> > 
> > +    if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,
> > +                       sizeof(ngx_stream_server_name_t))
> > +        != NGX_OK)
> > +    {
> > +        return NULL;
> > +    }
> > +
> >     cscf->file_name = cf->conf_file->file.name.data;
> >     cscf->line = cf->conf_file->line;
> >     cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
> > @@ -504,6 +682,9 @@ ngx_stream_core_merge_srv_conf(ngx_conf_
> >     ngx_stream_core_srv_conf_t *prev = parent;
> >     ngx_stream_core_srv_conf_t *conf = child;
> > 
> > +    ngx_str_t                  name;
> > +    ngx_stream_server_name_t  *sn;
> > +
> >     ngx_conf_merge_msec_value(conf->resolver_timeout,
> >                               prev->resolver_timeout, 30000);
> > 
> > @@ -551,6 +732,37 @@ ngx_stream_core_merge_srv_conf(ngx_conf_
> >     ngx_conf_merge_msec_value(conf->preread_timeout,
> >                               prev->preread_timeout, 30000);
> > 
> > +    if (conf->server_names.nelts == 0) {
> > +        /* the array has 4 empty preallocated elements, so push cannot fail */
> > +        sn = ngx_array_push(&conf->server_names);
> > +#if (NGX_PCRE)
> > +        sn->regex = NULL;
> > +#endif
> > +        sn->server = conf;
> > +        ngx_str_set(&sn->name, "");
> > +    }
> > +
> > +    sn = conf->server_names.elts;
> > +    name = sn[0].name;
> > +
> > +#if (NGX_PCRE)
> > +    if (sn->regex) {
> > +        name.len++;
> > +        name.data--;
> > +    } else
> > +#endif
> > +
> > +    if (name.data[0] == '.') {
> > +        name.len--;
> > +        name.data++;
> > +    }
> > +
> > +    conf->server_name.len = name.len;
> > +    conf->server_name.data = ngx_pstrdup(cf->pool, &name);
> > +    if (conf->server_name.data == NULL) {
> > +        return NGX_CONF_ERROR;
> > +    }
> > +
> >     return NGX_CONF_OK;
> > }
> > 
> > @@ -650,11 +862,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> > {
> >     ngx_stream_core_srv_conf_t  *cscf = conf;
> > 
> > -    ngx_str_t                    *value, size;
> > -    ngx_url_t                     u;
> > -    ngx_uint_t                    i, n, backlog;
> > -    ngx_stream_listen_t          *ls, *als, *nls;
> > -    ngx_stream_core_main_conf_t  *cmcf;
> > +    ngx_str_t                *value, size;
> > +    ngx_url_t                 u;
> > +    ngx_uint_t                i, n, backlog;
> > +    ngx_stream_listen_opt_t   lsopt;
> > 
> >     cscf->listen = 1;
> > 
> > @@ -675,51 +886,48 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >         return NGX_CONF_ERROR;
> >     }
> > 
> > -    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
> > -
> > -    ls = ngx_array_push(&cmcf->listen);
> > -    if (ls == NULL) {
> > -        return NGX_CONF_ERROR;
> > -    }
> > -
> > -    ngx_memzero(ls, sizeof(ngx_stream_listen_t));
> > +    ngx_memzero(&lsopt, sizeof(ngx_stream_listen_opt_t));
> > 
> > -    ls->backlog = NGX_LISTEN_BACKLOG;
> > -    ls->rcvbuf = -1;
> > -    ls->sndbuf = -1;
> > -    ls->type = SOCK_STREAM;
> > -    ls->ctx = cf->ctx;
> > -
> > +    lsopt.backlog = NGX_LISTEN_BACKLOG;
> > +    lsopt.type = SOCK_STREAM;
> > +    lsopt.rcvbuf = -1;
> > +    lsopt.sndbuf = -1;
> > #if (NGX_HAVE_TCP_FASTOPEN)
> > -    ls->fastopen = -1;
> > +    lsopt.fastopen = -1;
> > #endif
> > -
> > #if (NGX_HAVE_INET6)
> > -    ls->ipv6only = 1;
> > +    lsopt.ipv6only = 1;
> > #endif
> > 
> >     backlog = 0;
> > 
> >     for (i = 2; i < cf->args->nelts; i++) {
> > 
> > +        if (ngx_strcmp(value[i].data, "default_server") == 0
> > +            || ngx_strcmp(value[i].data, "default") == 0)
> 
> I don't think we should reintroduce "default" legacy in stream.
> 
> > +        {
> > +            lsopt.default_server = 1;
> > +            continue;
> > +        }
> > +
> > #if !(NGX_WIN32)
> >         if (ngx_strcmp(value[i].data, "udp") == 0) {
> > -            ls->type = SOCK_DGRAM;
> > +            lsopt.type = SOCK_DGRAM;
> >             continue;
> >         }
> > #endif
> > 
> >         if (ngx_strcmp(value[i].data, "bind") == 0) {
> > -            ls->bind = 1;
> > +            lsopt.bind = 1;
> 
> Note that here and below, setting lsopt.set is missing.
> This renders unusable duplicate socket-level listen parameter
> checks in ngx_stream_add_addresses().
> 
> Aside from that, there are several unimproved error messages in
> ngx_stream_core_listen() such as "bind ipv6only is not supported".
> They were fixed once in http in 1b05b9bbcebf, but similar fixes
> were missed in mail at the time.  Then stream was based on mail,
> they reappeared there.  It makes sense to fix them separately.

Yes, this will be a part of the cathup process later.

> >             continue;
> >         }
> > 
> > #if (NGX_HAVE_TCP_FASTOPEN)
> >         if (ngx_strncmp(value[i].data, "fastopen=", 9) == 0) {
> > -            ls->fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);
> > -            ls->bind = 1;
> > +            lsopt.fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);
> > +            lsopt.bind = 1;
> > 
> > -            if (ls->fastopen == NGX_ERROR) {
> > +            if (lsopt.fastopen == NGX_ERROR) {
> >                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> >                                    "invalid fastopen \"%V\"", &value[i]);
> >                 return NGX_CONF_ERROR;
> > @@ -730,10 +938,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> > #endif
> > 
> >         if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) {
> > -            ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
> > -            ls->bind = 1;
> > +            lsopt.backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
> > +            lsopt.bind = 1;
> > 
> > -            if (ls->backlog == NGX_ERROR || ls->backlog == 0) {
> > +            if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {
> >                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> >                                    "invalid backlog \"%V\"", &value[i]);
> >                 return NGX_CONF_ERROR;
> > @@ -748,10 +956,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >             size.len = value[i].len - 7;
> >             size.data = value[i].data + 7;
> > 
> > -            ls->rcvbuf = ngx_parse_size(&size);
> > -            ls->bind = 1;
> > +            lsopt.rcvbuf = ngx_parse_size(&size);
> > +            lsopt.bind = 1;
> > 
> > -            if (ls->rcvbuf == NGX_ERROR) {
> > +            if (lsopt.rcvbuf == NGX_ERROR) {
> >                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> >                                    "invalid rcvbuf \"%V\"", &value[i]);
> >                 return NGX_CONF_ERROR;
> > @@ -764,10 +972,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >             size.len = value[i].len - 7;
> >             size.data = value[i].data + 7;
> > 
> > -            ls->sndbuf = ngx_parse_size(&size);
> > -            ls->bind = 1;
> > +            lsopt.sndbuf = ngx_parse_size(&size);
> > +            lsopt.bind = 1;
> > 
> > -            if (ls->sndbuf == NGX_ERROR) {
> > +            if (lsopt.sndbuf == NGX_ERROR) {
> >                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> >                                    "invalid sndbuf \"%V\"", &value[i]);
> >                 return NGX_CONF_ERROR;
> > @@ -779,10 +987,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >         if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
> > #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
> >             if (ngx_strcmp(&value[i].data[10], "n") == 0) {
> > -                ls->ipv6only = 1;
> > +                lsopt.ipv6only = 1;
> > 
> >             } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
> > -                ls->ipv6only = 0;
> > +                lsopt.ipv6only = 0;
> > 
> >             } else {
> >                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > @@ -791,7 +999,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >                 return NGX_CONF_ERROR;
> >             }
> > 
> > -            ls->bind = 1;
> > +            lsopt.bind = 1;
> >             continue;
> > #else
> >             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > @@ -803,8 +1011,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> > 
> >         if (ngx_strcmp(value[i].data, "reuseport") == 0) {
> > #if (NGX_HAVE_REUSEPORT)
> > -            ls->reuseport = 1;
> > -            ls->bind = 1;
> > +            lsopt.reuseport = 1;
> > +            lsopt.bind = 1;
> > #else
> >             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> >                                "reuseport is not supported "
> > @@ -824,7 +1032,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >             sslcf->file = cf->conf_file->file.name.data;
> >             sslcf->line = cf->conf_file->line;
> > 
> > -            ls->ssl = 1;
> > +            lsopt.ssl = 1;
> 
> 
> Note that your change keeps sslcf->listen,
> checked in ngx_stream_core_merge_srv_conf().
> 
> Currently, without virtual servers support, this is perfectly
> fine because if you didn't specify the listen ssl parameter,
> then no need to create ssl context and check/load certificates.
> With virtual servers support though, sslcf->listen makes harm,
> because you cannot specify non-default servers with ssl
> parameter, but without certificates, which is pretty valid:
> 
>     server {
>         listen       127.0.0.1:8091 ssl;
>         server_name  foo;
>         return       FOO;
> 
>         ssl_certificate_key localhost.key;
>         ssl_certificate localhost.crt;
>     }
> 
>     server {
>         listen       127.0.0.1:8091 ssl;
>         server_name  bar;
>         return       BAR;
>     }
> 
> nginx: [emerg] no "ssl_certificate" is defined for the "listen ... ssl" directive
> 
> So it should be removed and replaced with appropriate certificate checks
> in ngx_stream_core_merge_srv_conf().  I propose to take the checks from
> ngx_http_core_merge_srv_conf().  Additionally, this will buy us the missing
> "ssl_reject_handshake" functionality, to selectively disable SSL handshakes
> in virtual servers based on SNI.
> 
> > 
> >             continue;
> > #else
> > @@ -838,10 +1046,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >         if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
> > 
> >             if (ngx_strcmp(&value[i].data[13], "on") == 0) {
> > -                ls->so_keepalive = 1;
> > +                lsopt.so_keepalive = 1;
> > 
> >             } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
> > -                ls->so_keepalive = 2;
> > +                lsopt.so_keepalive = 2;
> > 
> >             } else {
> > 
> > @@ -860,8 +1068,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >                 if (p > s.data) {
> >                     s.len = p - s.data;
> > 
> > -                    ls->tcp_keepidle = ngx_parse_time(&s, 1);
> > -                    if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
> > +                    lsopt.tcp_keepidle = ngx_parse_time(&s, 1);
> > +                    if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) {
> >                         goto invalid_so_keepalive;
> >                     }
> >                 }
> > @@ -876,8 +1084,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >                 if (p > s.data) {
> >                     s.len = p - s.data;
> > 
> > -                    ls->tcp_keepintvl = ngx_parse_time(&s, 1);
> > -                    if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
> > +                    lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);
> > +                    if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) {
> >                         goto invalid_so_keepalive;
> >                     }
> >                 }
> > @@ -887,19 +1095,19 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >                 if (s.data < end) {
> >                     s.len = end - s.data;
> > 
> > -                    ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
> > -                    if (ls->tcp_keepcnt == NGX_ERROR) {
> > +                    lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len);
> > +                    if (lsopt.tcp_keepcnt == NGX_ERROR) {
> >                         goto invalid_so_keepalive;
> >                     }
> >                 }
> > 
> > -                if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
> > -                    && ls->tcp_keepcnt == 0)
> > +                if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0
> > +                    && lsopt.tcp_keepcnt == 0)
> >                 {
> >                     goto invalid_so_keepalive;
> >                 }
> > 
> > -                ls->so_keepalive = 1;
> > +                lsopt.so_keepalive = 1;
> > 
> > #else
> > 
> > @@ -911,7 +1119,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> > #endif
> >             }
> > 
> > -            ls->bind = 1;
> > +            lsopt.bind = 1;
> > 
> >             continue;
> > 
> > @@ -926,7 +1134,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >         }
> > 
> >         if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) {
> > -            ls->proxy_protocol = 1;
> > +            lsopt.proxy_protocol = 1;
> >             continue;
> >         }
> > 
> > @@ -935,27 +1143,27 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >         return NGX_CONF_ERROR;
> >     }
> > 
> > -    if (ls->type == SOCK_DGRAM) {
> > +    if (lsopt.type == SOCK_DGRAM) {
> >         if (backlog) {
> >             return "\"backlog\" parameter is incompatible with \"udp\"";
> >         }
> > 
> > #if (NGX_STREAM_SSL)
> > -        if (ls->ssl) {
> > +        if (lsopt.ssl) {
> >             return "\"ssl\" parameter is incompatible with \"udp\"";
> >         }
> > #endif
> > 
> > -        if (ls->so_keepalive) {
> > +        if (lsopt.so_keepalive) {
> >             return "\"so_keepalive\" parameter is incompatible with \"udp\"";
> >         }
> > 
> > -        if (ls->proxy_protocol) {
> > +        if (lsopt.proxy_protocol) {
> >             return "\"proxy_protocol\" parameter is incompatible with \"udp\"";
> >         }
> > 
> > #if (NGX_HAVE_TCP_FASTOPEN)
> > -        if (ls->fastopen != -1) {
> > +        if (lsopt.fastopen != -1) {
> >             return "\"fastopen\" parameter is incompatible with \"udp\"";
> >         }
> > #endif
> > @@ -972,40 +1180,12 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> >             }
> >         }
> > 
> > -        if (n != 0) {
> > -            nls = ngx_array_push(&cmcf->listen);
> > -            if (nls == NULL) {
> > -                return NGX_CONF_ERROR;
> > -            }
> > -
> > -            *nls = *ls;
> > -
> > -        } else {
> > -            nls = ls;
> > -        }
> > -
> > -        nls->sockaddr = u.addrs[n].sockaddr;
> > -        nls->socklen = u.addrs[n].socklen;
> > -        nls->addr_text = u.addrs[n].name;
> > -        nls->wildcard = ngx_inet_wildcard(nls->sockaddr);
> > +        lsopt.sockaddr = u.addrs[n].sockaddr;
> > +        lsopt.socklen = u.addrs[n].socklen;
> > +        lsopt.addr_text = u.addrs[n].name;
> > +        lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr);
> > 
> > -        als = cmcf->listen.elts;
> > -
> > -        for (i = 0; i < cmcf->listen.nelts - 1; i++) {
> > -            if (nls->type != als[i].type) {
> > -                continue;
> > -            }
> > -
> > -            if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,
> > -                                 nls->sockaddr, nls->socklen, 1)
> > -                != NGX_OK)
> > -            {
> > -                continue;
> > -            }
> > -
> > -            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > -                               "duplicate \"%V\" address and port pair",
> > -                               &nls->addr_text);
> > +        if (ngx_stream_add_listen(cf, cscf, &lsopt) != NGX_OK) {
> >             return NGX_CONF_ERROR;
> >         }
> > 
> > @@ -1018,6 +1198,107 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
> > 
> > 
> > static char *
> > +ngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> > +{
> > +    ngx_stream_core_srv_conf_t *cscf = conf;
> > +
> > +    u_char                    ch;
> > +    ngx_str_t                *value;
> > +    ngx_uint_t                i;
> > +    ngx_stream_server_name_t  *sn;
> 
> wrong indentation
> 
> > +
> > +    value = cf->args->elts;
> > +
> > +    for (i = 1; i < cf->args->nelts; i++) {
> > +
> > +        ch = value[i].data[0];
> > +
> > +        if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))
> > +            || (ch == '.' && value[i].len < 2))
> > +        {
> > +            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > +                               "server name \"%V\" is invalid", &value[i]);
> > +            return NGX_CONF_ERROR;
> > +        }
> > +
> > +        if (ngx_strchr(value[i].data, '/')) {
> > +            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
> > +                               "server name \"%V\" has suspicious symbols",
> > +                               &value[i]);
> > +        }
> > +
> > +        sn = ngx_array_push(&cscf->server_names);
> > +        if (sn == NULL) {
> > +            return NGX_CONF_ERROR;
> > +        }
> > +
> > +#if (NGX_PCRE)
> > +        sn->regex = NULL;
> > +#endif
> > +        sn->server = cscf;
> > +
> > +        if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {
> > +            sn->name = cf->cycle->hostname;
> > +
> > +        } else {
> > +            sn->name = value[i];
> > +        }
> > +
> > +        if (value[i].data[0] != '~') {
> > +            ngx_strlow(sn->name.data, sn->name.data, sn->name.len);
> > +            continue;
> > +        }
> > +
> > +#if (NGX_PCRE)
> > +        {
> > +        u_char               *p;
> > +        ngx_regex_compile_t   rc;
> > +        u_char                errstr[NGX_MAX_CONF_ERRSTR];
> > +
> > +        if (value[i].len == 1) {
> > +            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > +                               "empty regex in server name \"%V\"", &value[i]);
> > +            return NGX_CONF_ERROR;
> > +        }
> > +
> > +        value[i].len--;
> > +        value[i].data++;
> > +
> > +        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
> > +
> > +        rc.pattern = value[i];
> > +        rc.err.len = NGX_MAX_CONF_ERRSTR;
> > +        rc.err.data = errstr;
> > +
> > +        for (p = value[i].data; p < value[i].data + value[i].len; p++) {
> > +            if (*p >= 'A' && *p <= 'Z') {
> > +                rc.options = NGX_REGEX_CASELESS;
> > +                break;
> > +            }
> > +        }
> > +
> > +        sn->regex = ngx_stream_regex_compile(cf, &rc);
> > +        if (sn->regex == NULL) {
> > +            return NGX_CONF_ERROR;
> > +        }
> > +
> > +        sn->name = value[i];
> > +        cscf->captures = (rc.captures > 0);
> > +        }
> > +#else
> > +        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > +                           "using regex \"%V\" "
> > +                           "requires PCRE library", &value[i]);
> > +
> > +        return NGX_CONF_ERROR;
> > +#endif
> > +    }
> > +
> > +    return NGX_CONF_OK;
> > +}
> > +
> > +
> > +static char *
> > ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> > {
> >     ngx_stream_core_srv_conf_t  *cscf = conf;
> > diff --git a/src/stream/ngx_stream_handler.c b/src/stream/ngx_stream_handler.c
> > --- a/src/stream/ngx_stream_handler.c
> > +++ b/src/stream/ngx_stream_handler.c
> > @@ -30,6 +30,7 @@ ngx_stream_init_connection(ngx_connectio
> >     struct sockaddr_in           *sin;
> >     ngx_stream_in_addr_t         *addr;
> >     ngx_stream_session_t         *s;
> > +    ngx_stream_conf_ctx_t        *ctx;
> >     ngx_stream_addr_conf_t       *addr_conf;
> > #if (NGX_HAVE_INET6)
> >     struct sockaddr_in6          *sin6;
> > @@ -121,9 +122,12 @@ ngx_stream_init_connection(ngx_connectio
> >         return;
> >     }
> > 
> > +    ctx = addr_conf->default_server->ctx;
> > +
> >     s->signature = NGX_STREAM_MODULE;
> > -    s->main_conf = addr_conf->ctx->main_conf;
> > -    s->srv_conf = addr_conf->ctx->srv_conf;
> > +    s->main_conf = ctx->main_conf;
> > +    s->srv_conf = ctx->srv_conf;
> > +    s->virtual_names = addr_conf->virtual_names;
> > 
> > #if (NGX_STREAM_SSL)
> >     s->ssl = addr_conf->ssl;
> > @@ -144,7 +148,7 @@ ngx_stream_init_connection(ngx_connectio
> > 
> >     ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA %sclient %*s connected to %V",
> >                   c->number, c->type == SOCK_DGRAM ? "udp " : "",
> > -                  len, text, &addr_conf->addr_text);
> > +                  len, text, &c->listening->addr_text);
> > 
> >     c->log->connection = c->number;
> >     c->log->handler = ngx_stream_log_error;
> > diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
> > --- a/src/stream/ngx_stream_ssl_module.c
> > +++ b/src/stream/ngx_stream_ssl_module.c
> > @@ -458,7 +458,104 @@ ngx_stream_ssl_handshake_handler(ngx_con
> > static int
> > ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
> > {
> > +    ngx_int_t                    rc;
> > +    ngx_str_t                    host;
> > +    const char                  *servername;
> > +    ngx_connection_t            *c;
> > +    ngx_stream_session_t        *s;
> > +    ngx_stream_ssl_conf_t       *sscf;
> 
> Note that stream (as well as mail) consistently uses sslcf naming
> for keeping ssl configuration, unlike in http.  Probably it makes
> sense for a separate sweeping change with renaming sslcf to sscf.

Agree.  Also, it makes sense to rename ngx_stream_ssl_conf_t to
ngx_stream_ssl_srv_conf_t.

> > +    ngx_stream_core_srv_conf_t  *cscf;
> > +
> > +    c = ngx_ssl_get_connection(ssl_conn);
> > +
> > +    if (c->ssl->handshaked) {
> > +        *ad = SSL_AD_NO_RENEGOTIATION;
> > +        return SSL_TLSEXT_ERR_ALERT_FATAL;
> > +    }
> > +
> > +    s = c->data;
> > +
> > +    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
> > +
> > +    if (servername == NULL) {
> > +        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
> > +                       "SSL server name: null");
> > +        goto done;
> > +    }
> > +
> > +    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
> > +                   "SSL server name: \"%s\"", servername);
> > +
> > +    host.len = ngx_strlen(servername);
> > +
> > +    if (host.len == 0) {
> > +        goto done;
> > +    }
> > +
> > +    host.data = (u_char *) servername;
> > +
> > +    rc = ngx_stream_validate_host(&host, c->pool, 1);
> > +
> > +    if (rc == NGX_ERROR) {
> > +        goto error;
> > +    }
> > +
> > +    if (rc == NGX_DECLINED) {
> > +        goto done;
> > +    }
> > +
> > +    rc = ngx_stream_find_virtual_server(s, &host, &cscf);
> > +
> > +    if (rc == NGX_ERROR) {
> > +        goto error;
> > +    }
> > +
> > +    if (rc == NGX_DECLINED) {
> > +        goto done;
> > +    }
> > +
> > +    s->srv_conf = cscf->ctx->srv_conf;
> > +
> > +    sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
> 
> Looks like a copy-paste error from http, where connection log
> is set based on the location configuration.
> Here it just makes sense to move setting sscf closer to its use.
> 
> > +
> > +    ngx_set_connection_log(c, cscf->error_log);
> > +
> > +    if (sscf->ssl.ctx) {
> > +        if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
> > +            goto error;
> > +        }
> > +
> > +        /*
> > +         * SSL_set_SSL_CTX() only changes certs as of 1.0.0d
> > +         * adjust other things we care about
> > +         */
> > +
> > +        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),
> > +                       SSL_CTX_get_verify_callback(sscf->ssl.ctx));
> > +
> > +        SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));
> > +
> > +#if OPENSSL_VERSION_NUMBER >= 0x009080dfL
> > +        /* only in 0.9.8m+ */
> > +        SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &
> > +                                    ~SSL_CTX_get_options(sscf->ssl.ctx));
> > +#endif
> > +
> > +        SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));
> > +
> > +#ifdef SSL_OP_NO_RENEGOTIATION
> > +        SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION);
> > +#endif
> > +    }
> > +
> > +done:
> > +
> 
> The reject_handshake functionality is missing there,
> it could be added in this change or separately
> (see below for a proposed addendum).
> 
> >     return SSL_TLSEXT_ERR_OK;
> > +
> > +error:
> > +
> > +    *ad = SSL_AD_INTERNAL_ERROR;
> > +    return SSL_TLSEXT_ERR_ALERT_FATAL;
> > }
> > 
> > #endif
> > diff --git a/src/stream/ngx_stream_ssl_preread_module.c b/src/stream/ngx_stream_ssl_preread_module.c
> > --- a/src/stream/ngx_stream_ssl_preread_module.c
> > +++ b/src/stream/ngx_stream_ssl_preread_module.c
> > @@ -33,6 +33,8 @@ typedef struct {
> > static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s);
> > static ngx_int_t ngx_stream_ssl_preread_parse_record(
> >     ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last);
> > +static ngx_int_t ngx_stream_ssl_preread_servername(ngx_stream_session_t *s,
> > +    ngx_str_t *servername);
> > static ngx_int_t ngx_stream_ssl_preread_protocol_variable(
> >     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
> > static ngx_int_t ngx_stream_ssl_preread_server_name_variable(
> > @@ -187,6 +189,10 @@ ngx_stream_ssl_preread_handler(ngx_strea
> >             return NGX_DECLINED;
> >         }
> > 
> > +        if (rc == NGX_OK) {
> > +            return ngx_stream_ssl_preread_servername(s, &ctx->host);
> > +        }
> > +
> >         if (rc != NGX_AGAIN) {
> >             return rc;
> >         }
> > @@ -404,9 +410,6 @@ ngx_stream_ssl_preread_parse_record(ngx_
> >         case sw_sni_host:
> >             ctx->host.len = (p[1] << 8) + p[2];
> > 
> > -            ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
> > -                           "ssl preread: SNI hostname \"%V\"", &ctx->host);
> > -
> >             state = sw_ext;
> >             dst = NULL;
> >             size = ext;
> > @@ -497,6 +500,56 @@ ngx_stream_ssl_preread_parse_record(ngx_
> > 
> > 
> > static ngx_int_t
> > +ngx_stream_ssl_preread_servername(ngx_stream_session_t *s,
> > +    ngx_str_t *servername)
> > +{
> > +    ngx_int_t                    rc;
> > +    ngx_str_t                    host;
> > +    ngx_connection_t            *c;
> > +    ngx_stream_core_srv_conf_t  *cscf;
> > +
> > +    c = s->connection;
> > +
> > +    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
> > +                   "SSL preread server name: \"%V\"", servername);
> > +
> > +    if (servername->len == 0) {
> > +        return NGX_OK;
> > +    }
> > +
> > +    host = *servername;
> > +
> > +    rc = ngx_stream_validate_host(&host, c->pool, 1);
> > +
> > +    if (rc == NGX_ERROR) {
> > +        return NGX_ERROR;
> > +    }
> > +
> > +    if (rc == NGX_DECLINED) {
> > +        return NGX_OK;
> > +    }
> > +
> > +    rc = ngx_stream_find_virtual_server(s, &host, &cscf);
> > +
> > +    if (rc == NGX_ERROR) {
> > +        return NGX_ERROR;
> > +    }
> > +
> > +    if (rc == NGX_DECLINED) {
> > +        return NGX_OK;
> > +    }
> > +
> > +    s->srv_conf = cscf->ctx->srv_conf;
> > +
> > +    cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
> 
> The server configuration is already obtained in
> ngx_stream_find_virtual_server(), no need to do this again.
> 
> > +
> > +    ngx_set_connection_log(c, cscf->error_log);
> > +
> > +    return NGX_OK;
> > +}
> > +
> > +
> > +static ngx_int_t
> > ngx_stream_ssl_preread_protocol_variable(ngx_stream_session_t *s,
> >     ngx_variable_value_t *v, uintptr_t data)
> > {
> 
> Together, this makes the following update on top of your change:
> 
> diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c
> --- a/src/stream/ngx_stream.c
> +++ b/src/stream/ngx_stream.c
> @@ -92,7 +92,7 @@ static char *
>  ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
>  {
>      char                          *rv;
> -    ngx_uint_t                     m, mi, s;
> +    ngx_uint_t                     mi, m, s;
>      ngx_conf_t                     pcf;
>      ngx_stream_module_t           *module;
>      ngx_stream_conf_ctx_t         *ctx;
> @@ -918,7 +918,7 @@ ngx_stream_init_listening(ngx_conf_t *cf
>  {
>      ngx_uint_t               i, last, bind_wildcard;
>      ngx_listening_t         *ls;
> -    ngx_stream_port_t       *hport;
> +    ngx_stream_port_t       *stport;
>      ngx_stream_conf_addr_t  *addr;
>  
>      addr = port->addrs.elts;
> @@ -953,26 +953,26 @@ ngx_stream_init_listening(ngx_conf_t *cf
>              return NGX_ERROR;
>          }
>  
> -        hport = ngx_pcalloc(cf->pool, sizeof(ngx_stream_port_t));
> -        if (hport == NULL) {
> +        stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));

You lost "c" in ngx_pcalloc() for no apparent reason.

> +        if (stport == NULL) {
>              return NGX_ERROR;
>          }
>  
> -        ls->servers = hport;
> +        ls->servers = stport;
>  
> -        hport->naddrs = i + 1;
> +        stport->naddrs = i + 1;
>  
>          switch (ls->sockaddr->sa_family) {
>  
>  #if (NGX_HAVE_INET6)
>          case AF_INET6:
> -            if (ngx_stream_add_addrs6(cf, hport, addr) != NGX_OK) {
> +            if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) {
>                  return NGX_ERROR;
>              }
>              break;
>  #endif
>          default: /* AF_INET */
> -            if (ngx_stream_add_addrs(cf, hport, addr) != NGX_OK) {
> +            if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {
>                  return NGX_ERROR;
>              }
>              break;
> @@ -1001,26 +1001,14 @@ ngx_stream_add_listening(ngx_conf_t *cf,
>  
>      ls->handler = ngx_stream_init_connection;
>  
> +    ls->pool_size = 256;
> +
>      cscf = addr->default_server;
> -    ls->pool_size = 256;
>  
>      ls->logp = cscf->error_log;
>      ls->log.data = &ls->addr_text;
>      ls->log.handler = ngx_accept_log_error;
>  
> -#if (NGX_WIN32)
> -    {
> -    ngx_iocp_conf_t  *iocpcf = NULL;
> -
> -    if (ngx_get_conf(cf->cycle->conf_ctx, ngx_events_module)) {
> -        iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
> -    }
> -    if (iocpcf && iocpcf->acceptex_read) {
> -        ls->post_accept_buffer_size = cscf->client_header_buffer_size;
> -    }
> -    }
> -#endif
> -
>      ls->type = addr->opt.type;
>      ls->backlog = addr->opt.backlog;
>      ls->rcvbuf = addr->opt.rcvbuf;
> @@ -1033,22 +1021,10 @@ ngx_stream_add_listening(ngx_conf_t *cf,
>      ls->keepcnt = addr->opt.tcp_keepcnt;
>  #endif
>  
> -#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
> -    ls->accept_filter = addr->opt.accept_filter;
> -#endif
> -
> -#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
> -    ls->deferred_accept = addr->opt.deferred_accept;
> -#endif
> -
>  #if (NGX_HAVE_INET6)
>      ls->ipv6only = addr->opt.ipv6only;
>  #endif
>  
> -#if (NGX_HAVE_SETFIB)
> -    ls->setfib = addr->opt.setfib;
> -#endif
> -
>  #if (NGX_HAVE_TCP_FASTOPEN)
>      ls->fastopen = addr->opt.fastopen;
>  #endif
> @@ -1064,7 +1040,7 @@ ngx_stream_add_listening(ngx_conf_t *cf,
>  
>  
>  static ngx_int_t
> -ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *hport,
> +ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
>      ngx_stream_conf_addr_t *addr)
>  {
>      ngx_uint_t                   i;
> @@ -1072,15 +1048,15 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx
>      ngx_stream_in_addr_t        *addrs;
>      ngx_stream_virtual_names_t  *vn;
>  
> -    hport->addrs = ngx_pcalloc(cf->pool,
> -                               hport->naddrs * sizeof(ngx_stream_in_addr_t));
> -    if (hport->addrs == NULL) {
> +    stport->addrs = ngx_pcalloc(cf->pool,
> +                                stport->naddrs * sizeof(ngx_stream_in_addr_t));
> +    if (stport->addrs == NULL) {
>          return NGX_ERROR;
>      }
>  
> -    addrs = hport->addrs;
> +    addrs = stport->addrs;
>  
> -    for (i = 0; i < hport->naddrs; i++) {
> +    for (i = 0; i < stport->naddrs; i++) {
>  
>          sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
>          addrs[i].addr = sin->sin_addr.s_addr;
> @@ -1126,7 +1102,7 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx
>  #if (NGX_HAVE_INET6)
>  
>  static ngx_int_t
> -ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *hport,
> +ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
>      ngx_stream_conf_addr_t *addr)
>  {
>      ngx_uint_t                   i;
> @@ -1134,15 +1110,15 @@ ngx_stream_add_addrs6(ngx_conf_t *cf, ng
>      ngx_stream_in6_addr_t       *addrs6;
>      ngx_stream_virtual_names_t  *vn;
>  
> -    hport->addrs = ngx_pcalloc(cf->pool,
> -                               hport->naddrs * sizeof(ngx_stream_in6_addr_t));
> -    if (hport->addrs == NULL) {
> +    stport->addrs = ngx_pcalloc(cf->pool,
> +                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));
> +    if (stport->addrs == NULL) {
>          return NGX_ERROR;
>      }
>  
> -    addrs6 = hport->addrs;
> +    addrs6 = stport->addrs;
>  
> -    for (i = 0; i < hport->naddrs; i++) {
> +    for (i = 0; i < stport->naddrs; i++) {
>  
>          sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
>          addrs6[i].addr6 = sin6->sin6_addr;
> diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h
> --- a/src/stream/ngx_stream.h
> +++ b/src/stream/ngx_stream.h
> @@ -53,30 +53,21 @@ typedef struct {
>  #if (NGX_HAVE_INET6)
>      unsigned                       ipv6only:1;
>  #endif
> -    unsigned                       deferred_accept:1;
>      unsigned                       reuseport:1;
>      unsigned                       so_keepalive:2;
>      unsigned                       proxy_protocol:1;
> -
> -    int                            backlog;
> -    int                            rcvbuf;
> -    int                            sndbuf;
> -    int                            type;
> -#if (NGX_HAVE_SETFIB)
> -    int                            setfib;
> -#endif
> -#if (NGX_HAVE_TCP_FASTOPEN)
> -    int                            fastopen;
> -#endif
>  #if (NGX_HAVE_KEEPALIVE_TUNABLE)
>      int                            tcp_keepidle;
>      int                            tcp_keepintvl;
>      int                            tcp_keepcnt;
>  #endif
> -
> -#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
> -    char                          *accept_filter;
> +    int                            backlog;
> +    int                            rcvbuf;
> +    int                            sndbuf;
> +#if (NGX_HAVE_TCP_FASTOPEN)
> +    int                            fastopen;
>  #endif
> +    int                            type;
>  } ngx_stream_listen_opt_t;
>  
>  
> @@ -198,7 +189,6 @@ typedef struct {
>  
>      ngx_stream_virtual_names_t    *virtual_names;
>  
> -    ngx_str_t                      addr_text;
>      unsigned                       ssl:1;
>      unsigned                       proxy_protocol:1;
>  } ngx_stream_addr_conf_t;
> @@ -254,7 +244,7 @@ typedef struct {
>      /* the default server configuration for this address:port */
>      ngx_stream_core_srv_conf_t    *default_server;
>      ngx_array_t                    servers;
> -                                     /* array of ngx_stream_core_srv_conf_t */
> +                                      /* array of ngx_stream_core_srv_conf_t */
>  } ngx_stream_conf_addr_t;
>  
>  
> @@ -350,6 +340,8 @@ typedef struct {
>  
>  ngx_int_t ngx_stream_add_listen(ngx_conf_t *cf,
>      ngx_stream_core_srv_conf_t *cscf, ngx_stream_listen_opt_t *lsopt);
> +
> +

Not sure if two lines look good here.  I'd leave just one like below.

>  void ngx_stream_core_run_phases(ngx_stream_session_t *s);
>  ngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s,
>      ngx_stream_phase_handler_t *ph);
> @@ -357,11 +349,11 @@ ngx_int_t ngx_stream_core_preread_phase(
>      ngx_stream_phase_handler_t *ph);
>  ngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s,
>      ngx_stream_phase_handler_t *ph);
> +
> +ngx_int_t ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool,
> +    ngx_uint_t alloc);
>  ngx_int_t ngx_stream_find_virtual_server(ngx_stream_session_t *s,
>      ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp);
> -ngx_int_t ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool,
> -    ngx_uint_t alloc);
> -
>  
>  void ngx_stream_init_connection(ngx_connection_t *c);
>  void ngx_stream_session_handler(ngx_event_t *rev);
> diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c
> --- a/src/stream/ngx_stream_core_module.c
> +++ b/src/stream/ngx_stream_core_module.c
> @@ -437,57 +437,6 @@ ngx_stream_core_content_phase(ngx_stream
>  
>  
>  ngx_int_t
> -ngx_stream_find_virtual_server(ngx_stream_session_t *s,
> -    ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp)
> -{
> -    ngx_stream_core_srv_conf_t  *cscf;
> -
> -    if (s->virtual_names == NULL) {
> -        return NGX_DECLINED;
> -    }
> -
> -    cscf = ngx_hash_find_combined(&s->virtual_names->names,
> -                                  ngx_hash_key(host->data, host->len),
> -                                  host->data, host->len);
> -
> -    if (cscf) {
> -        *cscfp = cscf;
> -        return NGX_OK;
> -    }
> -
> -#if (NGX_PCRE)
> -
> -    if (host->len && s->virtual_names->nregex) {
> -        ngx_int_t                  n;
> -        ngx_uint_t                 i;
> -        ngx_stream_server_name_t  *sn;
> -
> -        sn = s->virtual_names->regex;
> -
> -        for (i = 0; i < s->virtual_names->nregex; i++) {
> -
> -            n = ngx_stream_regex_exec(s, sn[i].regex, host);
> -
> -            if (n == NGX_DECLINED) {
> -                continue;
> -            }
> -
> -            if (n == NGX_OK) {
> -                *cscfp = sn[i].server;
> -                return NGX_OK;
> -            }
> -
> -            return NGX_ERROR;
> -        }
> -    }
> -
> -#endif /* NGX_PCRE */
> -
> -    return NGX_DECLINED;
> -}
> -
> -
> -ngx_int_t
>  ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
>  {
>      u_char  *h, ch;
> @@ -579,6 +528,57 @@ ngx_stream_validate_host(ngx_str_t *host
>  }
>  
>  
> +ngx_int_t
> +ngx_stream_find_virtual_server(ngx_stream_session_t *s,
> +    ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp)
> +{
> +    ngx_stream_core_srv_conf_t  *cscf;
> +
> +    if (s->virtual_names == NULL) {
> +        return NGX_DECLINED;
> +    }
> +
> +    cscf = ngx_hash_find_combined(&s->virtual_names->names,
> +                                  ngx_hash_key(host->data, host->len),
> +                                  host->data, host->len);
> +
> +    if (cscf) {
> +        *cscfp = cscf;
> +        return NGX_OK;
> +    }
> +
> +#if (NGX_PCRE)
> +
> +    if (host->len && s->virtual_names->nregex) {
> +        ngx_int_t                  n;
> +        ngx_uint_t                 i;
> +        ngx_stream_server_name_t  *sn;
> +
> +        sn = s->virtual_names->regex;
> +
> +        for (i = 0; i < s->virtual_names->nregex; i++) {
> +
> +            n = ngx_stream_regex_exec(s, sn[i].regex, host);
> +
> +            if (n == NGX_DECLINED) {
> +                continue;
> +            }
> +
> +            if (n == NGX_OK) {
> +                *cscfp = sn[i].server;
> +                return NGX_OK;
> +            }
> +
> +            return NGX_ERROR;
> +        }
> +    }
> +
> +#endif /* NGX_PCRE */
> +
> +    return NGX_DECLINED;
> +}
> +
> +
>  static ngx_int_t
>  ngx_stream_core_preconfiguration(ngx_conf_t *cf)
>  {
> @@ -625,7 +625,6 @@ ngx_stream_core_init_main_conf(ngx_conf_
>      cmcf->server_names_hash_bucket_size =
>              ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);
>  
> -

In HTTP we do have this empty line.

>      ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);
>      ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);
>  
> @@ -864,7 +863,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>  
>      ngx_str_t                *value, size;
>      ngx_url_t                 u;
> -    ngx_uint_t                i, n, backlog;
> +    ngx_uint_t                n, i, backlog;
>      ngx_stream_listen_opt_t   lsopt;
>  
>      cscf->listen = 1;
> @@ -903,9 +902,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>  
>      for (i = 2; i < cf->args->nelts; i++) {
>  
> -        if (ngx_strcmp(value[i].data, "default_server") == 0
> -            || ngx_strcmp(value[i].data, "default") == 0)
> -        {
> +        if (ngx_strcmp(value[i].data, "default_server") == 0) {
>              lsopt.default_server = 1;
>              continue;
>          }
> @@ -918,6 +915,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>  #endif
>  
>          if (ngx_strcmp(value[i].data, "bind") == 0) {
> +            lsopt.set = 1;
>              lsopt.bind = 1;
>              continue;
>          }
> @@ -925,6 +923,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>  #if (NGX_HAVE_TCP_FASTOPEN)
>          if (ngx_strncmp(value[i].data, "fastopen=", 9) == 0) {
>              lsopt.fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);
> +            lsopt.set = 1;
>              lsopt.bind = 1;
>  
>              if (lsopt.fastopen == NGX_ERROR) {
> @@ -939,6 +938,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>  
>          if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) {
>              lsopt.backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
> +            lsopt.set = 1;
>              lsopt.bind = 1;
>  
>              if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {
> @@ -957,6 +957,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>              size.data = value[i].data + 7;
>  
>              lsopt.rcvbuf = ngx_parse_size(&size);
> +            lsopt.set = 1;
>              lsopt.bind = 1;
>  
>              if (lsopt.rcvbuf == NGX_ERROR) {
> @@ -973,6 +974,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>              size.data = value[i].data + 7;
>  
>              lsopt.sndbuf = ngx_parse_size(&size);
> +            lsopt.set = 1;
>              lsopt.bind = 1;
>  
>              if (lsopt.sndbuf == NGX_ERROR) {
> @@ -999,11 +1001,13 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>                  return NGX_CONF_ERROR;
>              }
>  
> +            lsopt.set = 1;
>              lsopt.bind = 1;
> +
>              continue;
>  #else
>              ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> -                               "bind ipv6only is not supported "
> +                               "ipv6only is not supported "

Let's update text in a separate patch.

>                                 "on this platform");
>              return NGX_CONF_ERROR;
>  #endif
> @@ -1012,6 +1016,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>          if (ngx_strcmp(value[i].data, "reuseport") == 0) {
>  #if (NGX_HAVE_REUSEPORT)
>              lsopt.reuseport = 1;
> +            lsopt.set = 1;
>              lsopt.bind = 1;
>  #else
>              ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> @@ -1023,17 +1028,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>  
>          if (ngx_strcmp(value[i].data, "ssl") == 0) {
>  #if (NGX_STREAM_SSL)
> -            ngx_stream_ssl_conf_t  *sslcf;
> -
> -            sslcf = ngx_stream_conf_get_module_srv_conf(cf,
> -                                                        ngx_stream_ssl_module);
> -
> -            sslcf->listen = 1;
> -            sslcf->file = cf->conf_file->file.name.data;
> -            sslcf->line = cf->conf_file->line;
> -
>              lsopt.ssl = 1;
> -
>              continue;
>  #else
>              ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> @@ -1119,6 +1114,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>  #endif
>              }
>  
> +            lsopt.set = 1;
>              lsopt.bind = 1;
>  
>              continue;
> @@ -1139,7 +1135,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
>          }
>  
>          ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> -                           "the invalid \"%V\" parameter", &value[i]);
> +                           "invalid parameter \"%V\"", &value[i]);

Again, text change.

>          return NGX_CONF_ERROR;
>      }
>  
> @@ -1202,9 +1198,9 @@ ngx_stream_core_server_name(ngx_conf_t *
>  {
>      ngx_stream_core_srv_conf_t *cscf = conf;
>  
> -    u_char                    ch;
> -    ngx_str_t                *value;
> -    ngx_uint_t                i;
> +    u_char                     ch;
> +    ngx_str_t                 *value;
> +    ngx_uint_t                 i;
>      ngx_stream_server_name_t  *sn;
>  
>      value = cf->args->elts;
> diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
> --- a/src/stream/ngx_stream_ssl_module.c
> +++ b/src/stream/ngx_stream_ssl_module.c
> @@ -219,6 +219,13 @@ static ngx_command_t  ngx_stream_ssl_com
>        offsetof(ngx_stream_ssl_conf_t, conf_commands),
>        &ngx_stream_ssl_conf_command_post },
>  
> +    { ngx_string("ssl_reject_handshake"),
> +      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
> +      ngx_conf_set_flag_slot,
> +      NGX_STREAM_SRV_CONF_OFFSET,
> +      offsetof(ngx_stream_ssl_conf_t, reject_handshake),
> +      NULL },
> +
>      { ngx_string("ssl_alpn"),
>        NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
>        ngx_stream_ssl_alpn,
> @@ -463,7 +470,7 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t
>      const char                  *servername;
>      ngx_connection_t            *c;
>      ngx_stream_session_t        *s;
> -    ngx_stream_ssl_conf_t       *sscf;
> +    ngx_stream_ssl_conf_t       *sslcf;

Why?  I though, you like "sscf" more :)
I suggest that we use the better name "sscf" here.

>      ngx_stream_core_srv_conf_t  *cscf;
>  
>      c = ngx_ssl_get_connection(ssl_conn);
> @@ -516,12 +523,12 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t
>  
>      s->srv_conf = cscf->ctx->srv_conf;
>  
> -    sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
> -
>      ngx_set_connection_log(c, cscf->error_log);
>  
> -    if (sscf->ssl.ctx) {
> -        if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
> +    sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
> +
> +    if (sslcf->ssl.ctx) {
> +        if (SSL_set_SSL_CTX(ssl_conn, sslcf->ssl.ctx) == NULL) {
>              goto error;
>          }
>  
> @@ -530,18 +537,19 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t
>           * adjust other things we care about
>           */
>  
> -        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),
> -                       SSL_CTX_get_verify_callback(sscf->ssl.ctx));
> +        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sslcf->ssl.ctx),
> +                       SSL_CTX_get_verify_callback(sslcf->ssl.ctx));
>  
> -        SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));
> +        SSL_set_verify_depth(ssl_conn,
> +                             SSL_CTX_get_verify_depth(sslcf->ssl.ctx));
>  
>  #if OPENSSL_VERSION_NUMBER >= 0x009080dfL
>          /* only in 0.9.8m+ */
>          SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &
> -                                    ~SSL_CTX_get_options(sscf->ssl.ctx));
> +                                    ~SSL_CTX_get_options(sslcf->ssl.ctx));
>  #endif
>  
> -        SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));
> +        SSL_set_options(ssl_conn, SSL_CTX_get_options(sslcf->ssl.ctx));
>  
>  #ifdef SSL_OP_NO_RENEGOTIATION
>          SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION);
> @@ -550,6 +558,14 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t
>  
>  done:
>  
> +    sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
> +
> +    if (sslcf->reject_handshake) {
> +        c->ssl->handshake_rejected = 1;
> +        *ad = SSL_AD_UNRECOGNIZED_NAME;
> +        return SSL_TLSEXT_ERR_ALERT_FATAL;
> +    }
> +
>      return SSL_TLSEXT_ERR_OK;
>  
>  error:
> @@ -752,7 +768,6 @@ ngx_stream_ssl_create_conf(ngx_conf_t *c
>      /*
>       * set by ngx_pcalloc():
>       *
> -     *     scf->listen = 0;
>       *     scf->protocols = 0;
>       *     scf->certificate_values = NULL;
>       *     scf->dhparam = { 0, NULL };
> @@ -771,6 +786,7 @@ ngx_stream_ssl_create_conf(ngx_conf_t *c
>      scf->passwords = NGX_CONF_UNSET_PTR;
>      scf->conf_commands = NGX_CONF_UNSET_PTR;
>      scf->prefer_server_ciphers = NGX_CONF_UNSET;
> +    scf->reject_handshake = NGX_CONF_UNSET;
>      scf->verify = NGX_CONF_UNSET_UINT;
>      scf->verify_depth = NGX_CONF_UNSET_UINT;
>      scf->builtin_session_cache = NGX_CONF_UNSET;
> @@ -799,6 +815,8 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
>      ngx_conf_merge_value(conf->prefer_server_ciphers,
>                           prev->prefer_server_ciphers, 0);
>  
> +    ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
> +
>      ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
>                           (NGX_CONF_BITMASK_SET
>                            |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
> @@ -832,35 +850,21 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
>  
>      conf->ssl.log = cf->log;
>  
> -    if (!conf->listen) {
> -        return NGX_CONF_OK;
> -    }
> -
> -    if (conf->certificates == NULL) {
> -        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
> -                      "no \"ssl_certificate\" is defined for "
> -                      "the \"listen ... ssl\" directive in %s:%ui",
> -                      conf->file, conf->line);
> -        return NGX_CONF_ERROR;
> -    }
> +    if (conf->certificates) {
>  
> -    if (conf->certificate_keys == NULL) {
> -        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
> -                      "no \"ssl_certificate_key\" is defined for "
> -                      "the \"listen ... ssl\" directive in %s:%ui",
> -                      conf->file, conf->line);
> -        return NGX_CONF_ERROR;
> -    }
> +        if (conf->certificate_keys == NULL
> +            || conf->certificate_keys->nelts < conf->certificates->nelts)
> +        {
> +            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
> +                          "no \"ssl_certificate_key\" is defined "
> +                          "for certificate \"%V\"",
> +                          ((ngx_str_t *) conf->certificates->elts)
> +                          + conf->certificates->nelts - 1);
> +            return NGX_CONF_ERROR;
> +        }
>  
> -    if (conf->certificate_keys->nelts < conf->certificates->nelts) {
> -        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
> -                      "no \"ssl_certificate_key\" is defined "
> -                      "for certificate \"%V\" and "
> -                      "the \"listen ... ssl\" directive in %s:%ui",
> -                      ((ngx_str_t *) conf->certificates->elts)
> -                      + conf->certificates->nelts - 1,
> -                      conf->file, conf->line);
> -        return NGX_CONF_ERROR;
> +    } else if (!conf->reject_handshake) {
> +        return NGX_CONF_OK;
>      }
>  
>      if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
> @@ -915,7 +919,7 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
>          return NGX_CONF_ERROR;
>  #endif
>  
> -    } else {
> +    } else if (conf->certificates) {
>  
>          /* configure certificates */
>  
> @@ -1014,6 +1018,10 @@ ngx_stream_ssl_compile_certificates(ngx_
>      ngx_stream_complex_value_t          *cv;
>      ngx_stream_compile_complex_value_t   ccv;
>  
> +    if (conf->certificates == NULL) {
> +        return NGX_OK;
> +    }
> +
>      cert = conf->certificates->elts;
>      key = conf->certificate_keys->elts;
>      nelts = conf->certificates->nelts;
> @@ -1292,8 +1300,13 @@ ngx_stream_ssl_conf_command_check(ngx_co
>  static ngx_int_t
>  ngx_stream_ssl_init(ngx_conf_t *cf)
>  {
> -    ngx_stream_handler_pt        *h;
> -    ngx_stream_core_main_conf_t  *cmcf;
> +    ngx_uint_t                     a, p, s;
> +    ngx_stream_handler_pt         *h;
> +    ngx_stream_ssl_conf_t         *sslcf;

Again, let's use sscf here.  This way the diff to http is smaller.

> +    ngx_stream_conf_addr_t        *addr;
> +    ngx_stream_conf_port_t        *port;
> +    ngx_stream_core_srv_conf_t   **cscfp, *cscf;
> +    ngx_stream_core_main_conf_t   *cmcf;
>  
>      cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
>  
> @@ -1304,5 +1317,58 @@ ngx_stream_ssl_init(ngx_conf_t *cf)
>  
>      *h = ngx_stream_ssl_handler;
>  
> +    if (cmcf->ports == NULL) {
> +        return NGX_OK;
> +    }
> +
> +    port = cmcf->ports->elts;
> +    for (p = 0; p < cmcf->ports->nelts; p++) {
> +
> +        addr = port[p].addrs.elts;
> +        for (a = 0; a < port[p].addrs.nelts; a++) {
> +
> +            if (!addr[a].opt.ssl) {
> +                continue;
> +            }
> +
> +            cscf = addr[a].default_server;
> +            sslcf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
> +
> +            if (sslcf->certificates) {
> +                continue;
> +            }
> +
> +            if (!sslcf->reject_handshake) {
> +                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
> +                              "no \"ssl_certificate\" is defined for "
> +                              "the \"listen ... ssl\" directive in %s:%ui",
> +                              cscf->file_name, cscf->line);
> +                return NGX_ERROR;
> +            }
> +
> +            /*
> +             * if no certificates are defined in the default server,
> +             * check all non-default server blocks
> +             */
> +
> +            cscfp = addr[a].servers.elts;
> +            for (s = 0; s < addr[a].servers.nelts; s++) {
> +
> +                cscf = cscfp[s];
> +                sslcf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
> +
> +                if (sslcf->certificates || sslcf->reject_handshake) {
> +                    continue;
> +                }
> +
> +                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
> +                              "no \"ssl_certificate\" is defined for "
> +                              "the \"listen ... ssl\" directive in %s:%ui",
> +                              cscf->file_name, cscf->line);
> +                return NGX_ERROR;
> +            }
> +        }
> +    }
> +
>      return NGX_OK;
>  }
> diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
> --- a/src/stream/ngx_stream_ssl_module.h
> +++ b/src/stream/ngx_stream_ssl_module.h
> @@ -18,10 +18,10 @@ typedef struct {
>      ngx_msec_t       handshake_timeout;
>  
>      ngx_flag_t       prefer_server_ciphers;
> +    ngx_flag_t       reject_handshake;
>  
>      ngx_ssl_t        ssl;
>  
> -    ngx_uint_t       listen;
>      ngx_uint_t       protocols;
>  
>      ngx_uint_t       verify;
> @@ -53,9 +53,6 @@ typedef struct {
>  
>      ngx_flag_t       session_tickets;
>      ngx_array_t     *session_ticket_keys;
> -
> -    u_char          *file;
> -    ngx_uint_t       line;
>  } ngx_stream_ssl_conf_t;
>  
>  
> diff --git a/src/stream/ngx_stream_ssl_preread_module.c b/src/stream/ngx_stream_ssl_preread_module.c
> --- a/src/stream/ngx_stream_ssl_preread_module.c
> +++ b/src/stream/ngx_stream_ssl_preread_module.c
> @@ -541,8 +541,6 @@ ngx_stream_ssl_preread_servername(ngx_st
>  
>      s->srv_conf = cscf->ctx->srv_conf;
>  
> -    cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
> -
>      ngx_set_connection_log(c, cscf->error_log);
>  
>      return NGX_OK;
> 
> -- 
> Sergey Kandaurov
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> https://mailman.nginx.org/mailman/listinfo/nginx-devel

New patch attached.

--
Roman Arutyunyan
-------------- next part --------------
# HG changeset patch
# User Roman Arutyunyan <arut at nginx.com>
# Date 1702532489 -14400
#      Thu Dec 14 09:41:29 2023 +0400
# Node ID a390e18b664e7ba678417ef6e40d94c37e89c2f7
# Parent  844486cdd43a32d10b78493d7e7b80e9e2239d7e
Stream: virtual servers.

Server name is taken either from ngx_stream_ssl_module or
ngx_stream_ssl_preread_module.

The change adds "default" parameter to the "listen" directive, as well as the
following directives: "server_names_hash_max_size",
"server_names_hash_bucket_size", "server_name" and "ssl_reject_handshake".

diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c
--- a/src/stream/ngx_stream.c
+++ b/src/stream/ngx_stream.c
@@ -16,16 +16,34 @@ static ngx_int_t ngx_stream_init_phases(
     ngx_stream_core_main_conf_t *cmcf);
 static ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf,
     ngx_stream_core_main_conf_t *cmcf);
-static ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
-    ngx_stream_listen_t *listen);
-static char *ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
+
+static ngx_int_t ngx_stream_add_addresses(ngx_conf_t *cf,
+    ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port,
+    ngx_stream_listen_opt_t *lsopt);
+static ngx_int_t ngx_stream_add_address(ngx_conf_t *cf,
+    ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port,
+    ngx_stream_listen_opt_t *lsopt);
+static ngx_int_t ngx_stream_add_server(ngx_conf_t *cf,
+    ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_addr_t *addr);
+
+static ngx_int_t ngx_stream_optimize_servers(ngx_conf_t *cf,
+    ngx_stream_core_main_conf_t *cmcf, ngx_array_t *ports);
+static ngx_int_t ngx_stream_server_names(ngx_conf_t *cf,
+    ngx_stream_core_main_conf_t *cmcf, ngx_stream_conf_addr_t *addr);
+static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
+static int ngx_libc_cdecl ngx_stream_cmp_dns_wildcards(const void *one,
+    const void *two);
+
+static ngx_int_t ngx_stream_init_listening(ngx_conf_t *cf,
+    ngx_stream_conf_port_t *port);
+static ngx_listening_t *ngx_stream_add_listening(ngx_conf_t *cf,
+    ngx_stream_conf_addr_t *addr);
 static ngx_int_t ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
     ngx_stream_conf_addr_t *addr);
 #if (NGX_HAVE_INET6)
 static ngx_int_t ngx_stream_add_addrs6(ngx_conf_t *cf,
     ngx_stream_port_t *stport, ngx_stream_conf_addr_t *addr);
 #endif
-static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
 
 
 ngx_uint_t  ngx_stream_max_module;
@@ -74,10 +92,8 @@ static char *
 ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                          *rv;
-    ngx_uint_t                     i, m, mi, s;
+    ngx_uint_t                     mi, m, s;
     ngx_conf_t                     pcf;
-    ngx_array_t                    ports;
-    ngx_stream_listen_t           *listen;
     ngx_stream_module_t           *module;
     ngx_stream_conf_ctx_t         *ctx;
     ngx_stream_core_srv_conf_t   **cscfp;
@@ -251,21 +267,13 @@ ngx_stream_block(ngx_conf_t *cf, ngx_com
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_stream_conf_port_t))
-        != NGX_OK)
-    {
+    /* optimize the lists of ports, addresses and server names */
+
+    if (ngx_stream_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
-    listen = cmcf->listen.elts;
-
-    for (i = 0; i < cmcf->listen.nelts; i++) {
-        if (ngx_stream_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
-            return NGX_CONF_ERROR;
-        }
-    }
-
-    return ngx_stream_optimize_servers(cf, &ports);
+    return NGX_CONF_OK;
 }
 
 
@@ -377,73 +385,295 @@ ngx_stream_init_phase_handlers(ngx_conf_
 }
 
 
-static ngx_int_t
-ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
-    ngx_stream_listen_t *listen)
+ngx_int_t
+ngx_stream_add_listen(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
+    ngx_stream_listen_opt_t *lsopt)
 {
-    in_port_t                p;
-    ngx_uint_t               i;
-    struct sockaddr         *sa;
-    ngx_stream_conf_port_t  *port;
-    ngx_stream_conf_addr_t  *addr;
+    in_port_t                     p;
+    ngx_uint_t                    i;
+    struct sockaddr              *sa;
+    ngx_stream_conf_port_t       *port;
+    ngx_stream_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
 
-    sa = listen->sockaddr;
+    if (cmcf->ports == NULL) {
+        cmcf->ports = ngx_array_create(cf->temp_pool, 2,
+                                       sizeof(ngx_stream_conf_port_t));
+        if (cmcf->ports == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    sa = lsopt->sockaddr;
     p = ngx_inet_get_port(sa);
 
-    port = ports->elts;
-    for (i = 0; i < ports->nelts; i++) {
+    port = cmcf->ports->elts;
+    for (i = 0; i < cmcf->ports->nelts; i++) {
 
-        if (p == port[i].port
-            && listen->type == port[i].type
-            && sa->sa_family == port[i].family)
+        if (p != port[i].port
+            || lsopt->type != port[i].type
+            || sa->sa_family != port[i].family)
         {
-            /* a port is already in the port list */
+            continue;
+        }
 
-            port = &port[i];
-            goto found;
-        }
+        /* a port is already in the port list */
+
+        return ngx_stream_add_addresses(cf, cscf, &port[i], lsopt);
     }
 
     /* add a port to the port list */
 
-    port = ngx_array_push(ports);
+    port = ngx_array_push(cmcf->ports);
     if (port == NULL) {
         return NGX_ERROR;
     }
 
     port->family = sa->sa_family;
-    port->type = listen->type;
+    port->type = lsopt->type;
     port->port = p;
+    port->addrs.elts = NULL;
+
+    return ngx_stream_add_address(cf, cscf, port, lsopt);
+}
+
+
+static ngx_int_t
+ngx_stream_add_addresses(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
+    ngx_stream_conf_port_t *port, ngx_stream_listen_opt_t *lsopt)
+{
+    ngx_uint_t               i, default_server, proxy_protocol,
+                             protocols, protocols_prev;
+    ngx_stream_conf_addr_t  *addr;
+#if (NGX_STREAM_SSL)
+    ngx_uint_t               ssl;
+#endif
+
+    /*
+     * we cannot compare whole sockaddr struct's as kernel
+     * may fill some fields in inherited sockaddr struct's
+     */
+
+    addr = port->addrs.elts;
+
+    for (i = 0; i < port->addrs.nelts; i++) {
+
+        if (ngx_cmp_sockaddr(lsopt->sockaddr, lsopt->socklen,
+                             addr[i].opt.sockaddr,
+                             addr[i].opt.socklen, 0)
+            != NGX_OK)
+        {
+            continue;
+        }
+
+        /* the address is already in the address list */
+
+        if (ngx_stream_add_server(cf, cscf, &addr[i]) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        /* preserve default_server bit during listen options overwriting */
+        default_server = addr[i].opt.default_server;
+
+        proxy_protocol = lsopt->proxy_protocol || addr[i].opt.proxy_protocol;
+        protocols = lsopt->proxy_protocol;
+        protocols_prev = addr[i].opt.proxy_protocol;
+
+#if (NGX_STREAM_SSL)
+        ssl = lsopt->ssl || addr[i].opt.ssl;
+        protocols |= lsopt->ssl << 1;
+        protocols_prev |= addr[i].opt.ssl << 1;
+#endif
+
+        if (lsopt->set) {
+
+            if (addr[i].opt.set) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "duplicate listen options for %V",
+                                   &addr[i].opt.addr_text);
+                return NGX_ERROR;
+            }
+
+            addr[i].opt = *lsopt;
+        }
+
+        /* check the duplicate "default" server for this address:port */
 
-    if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
-                       sizeof(ngx_stream_conf_addr_t))
-        != NGX_OK)
-    {
-        return NGX_ERROR;
+        if (lsopt->default_server) {
+
+            if (default_server) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "a duplicate default server for %V",
+                                   &addr[i].opt.addr_text);
+                return NGX_ERROR;
+            }
+
+            default_server = 1;
+            addr[i].default_server = cscf;
+        }
+
+        /* check for conflicting protocol options */
+
+        if ((protocols | protocols_prev) != protocols_prev) {
+
+            /* options added */
+
+            if ((addr[i].opt.set && !lsopt->set)
+                || addr[i].protocols_changed
+                || (protocols | protocols_prev) != protocols)
+            {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "protocol options redefined for %V",
+                                   &addr[i].opt.addr_text);
+            }
+
+            addr[i].protocols = protocols_prev;
+            addr[i].protocols_set = 1;
+            addr[i].protocols_changed = 1;
+
+        } else if ((protocols_prev | protocols) != protocols) {
+
+            /* options removed */
+
+            if (lsopt->set
+                || (addr[i].protocols_set && protocols != addr[i].protocols))
+            {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "protocol options redefined for %V",
+                                   &addr[i].opt.addr_text);
+            }
+
+            addr[i].protocols = protocols;
+            addr[i].protocols_set = 1;
+            addr[i].protocols_changed = 1;
+
+        } else {
+
+            /* the same options */
+
+            if ((lsopt->set && addr[i].protocols_changed)
+                || (addr[i].protocols_set && protocols != addr[i].protocols))
+            {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "protocol options redefined for %V",
+                                   &addr[i].opt.addr_text);
+            }
+
+            addr[i].protocols = protocols;
+            addr[i].protocols_set = 1;
+        }
+
+        addr[i].opt.default_server = default_server;
+        addr[i].opt.proxy_protocol = proxy_protocol;
+#if (NGX_STREAM_SSL)
+        addr[i].opt.ssl = ssl;
+#endif
+
+        return NGX_OK;
     }
 
-found:
+    /* add the address to the addresses list that bound to this port */
+
+    return ngx_stream_add_address(cf, cscf, port, lsopt);
+}
+
+
+/*
+ * add the server address, the server names and the server core module
+ * configurations to the port list
+ */
+
+static ngx_int_t
+ngx_stream_add_address(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
+    ngx_stream_conf_port_t *port, ngx_stream_listen_opt_t *lsopt)
+{
+    ngx_stream_conf_addr_t  *addr;
+
+    if (port->addrs.elts == NULL) {
+        if (ngx_array_init(&port->addrs, cf->temp_pool, 4,
+                           sizeof(ngx_stream_conf_addr_t))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+    }
 
     addr = ngx_array_push(&port->addrs);
     if (addr == NULL) {
         return NGX_ERROR;
     }
 
-    addr->opt = *listen;
+    addr->opt = *lsopt;
+    addr->protocols = 0;
+    addr->protocols_set = 0;
+    addr->protocols_changed = 0;
+    addr->hash.buckets = NULL;
+    addr->hash.size = 0;
+    addr->wc_head = NULL;
+    addr->wc_tail = NULL;
+#if (NGX_PCRE)
+    addr->nregex = 0;
+    addr->regex = NULL;
+#endif
+    addr->default_server = cscf;
+    addr->servers.elts = NULL;
+
+    return ngx_stream_add_server(cf, cscf, addr);
+}
+
+
+/* add the server core module configuration to the address:port */
+
+static ngx_int_t
+ngx_stream_add_server(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,
+    ngx_stream_conf_addr_t *addr)
+{
+    ngx_uint_t                    i;
+    ngx_stream_core_srv_conf_t  **server;
+
+    if (addr->servers.elts == NULL) {
+        if (ngx_array_init(&addr->servers, cf->temp_pool, 4,
+                           sizeof(ngx_stream_core_srv_conf_t *))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
+        }
+
+    } else {
+        server = addr->servers.elts;
+        for (i = 0; i < addr->servers.nelts; i++) {
+            if (server[i] == cscf) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "a duplicate listen %V",
+                                   &addr->opt.addr_text);
+                return NGX_ERROR;
+            }
+        }
+    }
+
+    server = ngx_array_push(&addr->servers);
+    if (server == NULL) {
+        return NGX_ERROR;
+    }
+
+    *server = cscf;
 
     return NGX_OK;
 }
 
 
-static char *
-ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
+static ngx_int_t
+ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf,
+    ngx_array_t *ports)
 {
-    ngx_uint_t                   i, p, last, bind_wildcard;
-    ngx_listening_t             *ls;
-    ngx_stream_port_t           *stport;
-    ngx_stream_conf_port_t      *port;
-    ngx_stream_conf_addr_t      *addr;
-    ngx_stream_core_srv_conf_t  *cscf;
+    ngx_uint_t               p, a;
+    ngx_stream_conf_port_t  *port;
+    ngx_stream_conf_addr_t  *addr;
+
+    if (ports == NULL) {
+        return NGX_OK;
+    }
 
     port = ports->elts;
     for (p = 0; p < ports->nelts; p++) {
@@ -451,175 +681,191 @@ ngx_stream_optimize_servers(ngx_conf_t *
         ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
                  sizeof(ngx_stream_conf_addr_t), ngx_stream_cmp_conf_addrs);
 
-        addr = port[p].addrs.elts;
-        last = 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
+         * check whether all name-based servers have the same
+         * configuration as a default server for given address:port
          */
 
-        if (addr[last - 1].opt.wildcard) {
-            addr[last - 1].opt.bind = 1;
-            bind_wildcard = 1;
+        addr = port[p].addrs.elts;
+        for (a = 0; a < port[p].addrs.nelts; a++) {
 
-        } else {
-            bind_wildcard = 0;
+            if (addr[a].servers.nelts > 1
+#if (NGX_PCRE)
+                || addr[a].default_server->captures
+#endif
+               )
+            {
+                if (ngx_stream_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+            }
         }
 
-        i = 0;
-
-        while (i < last) {
-
-            if (bind_wildcard && !addr[i].opt.bind) {
-                i++;
-                continue;
-            }
-
-            ls = ngx_create_listening(cf, addr[i].opt.sockaddr,
-                                      addr[i].opt.socklen);
-            if (ls == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            ls->addr_ntop = 1;
-            ls->handler = ngx_stream_init_connection;
-            ls->pool_size = 256;
-            ls->type = addr[i].opt.type;
-
-            cscf = addr->opt.ctx->srv_conf[ngx_stream_core_module.ctx_index];
-
-            ls->logp = cscf->error_log;
-            ls->log.data = &ls->addr_text;
-            ls->log.handler = ngx_accept_log_error;
-
-            ls->backlog = addr[i].opt.backlog;
-            ls->rcvbuf = addr[i].opt.rcvbuf;
-            ls->sndbuf = addr[i].opt.sndbuf;
-
-            ls->wildcard = addr[i].opt.wildcard;
-
-            ls->keepalive = addr[i].opt.so_keepalive;
-#if (NGX_HAVE_KEEPALIVE_TUNABLE)
-            ls->keepidle = addr[i].opt.tcp_keepidle;
-            ls->keepintvl = addr[i].opt.tcp_keepintvl;
-            ls->keepcnt = addr[i].opt.tcp_keepcnt;
-#endif
-
-#if (NGX_HAVE_INET6)
-            ls->ipv6only = addr[i].opt.ipv6only;
-#endif
-
-#if (NGX_HAVE_TCP_FASTOPEN)
-            ls->fastopen = addr[i].opt.fastopen;
-#endif
-
-#if (NGX_HAVE_REUSEPORT)
-            ls->reuseport = addr[i].opt.reuseport;
-#endif
-
-            stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));
-            if (stport == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            ls->servers = stport;
-
-            stport->naddrs = i + 1;
-
-            switch (ls->sockaddr->sa_family) {
-#if (NGX_HAVE_INET6)
-            case AF_INET6:
-                if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) {
-                    return NGX_CONF_ERROR;
-                }
-                break;
-#endif
-            default: /* AF_INET */
-                if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {
-                    return NGX_CONF_ERROR;
-                }
-                break;
-            }
-
-            addr++;
-            last--;
+        if (ngx_stream_init_listening(cf, &port[p]) != NGX_OK) {
+            return NGX_ERROR;
         }
     }
 
-    return NGX_CONF_OK;
-}
-
-
-static ngx_int_t
-ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
-    ngx_stream_conf_addr_t *addr)
-{
-    ngx_uint_t             i;
-    struct sockaddr_in    *sin;
-    ngx_stream_in_addr_t  *addrs;
-
-    stport->addrs = ngx_pcalloc(cf->pool,
-                                stport->naddrs * sizeof(ngx_stream_in_addr_t));
-    if (stport->addrs == NULL) {
-        return NGX_ERROR;
-    }
-
-    addrs = stport->addrs;
-
-    for (i = 0; i < stport->naddrs; i++) {
-
-        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
-        addrs[i].addr = sin->sin_addr.s_addr;
-
-        addrs[i].conf.ctx = addr[i].opt.ctx;
-#if (NGX_STREAM_SSL)
-        addrs[i].conf.ssl = addr[i].opt.ssl;
-#endif
-        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
-        addrs[i].conf.addr_text = addr[i].opt.addr_text;
-    }
-
     return NGX_OK;
 }
 
 
-#if (NGX_HAVE_INET6)
-
 static ngx_int_t
-ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
+ngx_stream_server_names(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf,
     ngx_stream_conf_addr_t *addr)
 {
-    ngx_uint_t              i;
-    struct sockaddr_in6    *sin6;
-    ngx_stream_in6_addr_t  *addrs6;
+    ngx_int_t                     rc;
+    ngx_uint_t                    n, s;
+    ngx_hash_init_t               hash;
+    ngx_hash_keys_arrays_t        ha;
+    ngx_stream_server_name_t     *name;
+    ngx_stream_core_srv_conf_t  **cscfp;
+#if (NGX_PCRE)
+    ngx_uint_t                    regex, i;
 
-    stport->addrs = ngx_pcalloc(cf->pool,
-                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));
-    if (stport->addrs == NULL) {
+    regex = 0;
+#endif
+
+    ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
+
+    ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
+    if (ha.temp_pool == NULL) {
         return NGX_ERROR;
     }
 
-    addrs6 = stport->addrs;
+    ha.pool = cf->pool;
+
+    if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
+        goto failed;
+    }
+
+    cscfp = addr->servers.elts;
+
+    for (s = 0; s < addr->servers.nelts; s++) {
+
+        name = cscfp[s]->server_names.elts;
+
+        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
 
-    for (i = 0; i < stport->naddrs; i++) {
+#if (NGX_PCRE)
+            if (name[n].regex) {
+                regex++;
+                continue;
+            }
+#endif
 
-        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
-        addrs6[i].addr6 = sin6->sin6_addr;
+            rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,
+                                  NGX_HASH_WILDCARD_KEY);
+
+            if (rc == NGX_ERROR) {
+                goto failed;
+            }
 
-        addrs6[i].conf.ctx = addr[i].opt.ctx;
-#if (NGX_STREAM_SSL)
-        addrs6[i].conf.ssl = addr[i].opt.ssl;
-#endif
-        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
-        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
+            if (rc == NGX_DECLINED) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "invalid server name or wildcard \"%V\" on %V",
+                              &name[n].name, &addr->opt.addr_text);
+                goto failed;
+            }
+
+            if (rc == NGX_BUSY) {
+                ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+                              "conflicting server name \"%V\" on %V, ignored",
+                              &name[n].name, &addr->opt.addr_text);
+            }
+        }
+    }
+
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = cmcf->server_names_hash_max_size;
+    hash.bucket_size = cmcf->server_names_hash_bucket_size;
+    hash.name = "server_names_hash";
+    hash.pool = cf->pool;
+
+    if (ha.keys.nelts) {
+        hash.hash = &addr->hash;
+        hash.temp_pool = NULL;
+
+        if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {
+            goto failed;
+        }
     }
 
-    return NGX_OK;
-}
+    if (ha.dns_wc_head.nelts) {
+
+        ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,
+                  sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = ha.temp_pool;
+
+        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
+                                   ha.dns_wc_head.nelts)
+            != NGX_OK)
+        {
+            goto failed;
+        }
+
+        addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    if (ha.dns_wc_tail.nelts) {
+
+        ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,
+                  sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = ha.temp_pool;
+
+        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
+                                   ha.dns_wc_tail.nelts)
+            != NGX_OK)
+        {
+            goto failed;
+        }
+
+        addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    ngx_destroy_pool(ha.temp_pool);
+
+#if (NGX_PCRE)
+
+    if (regex == 0) {
+        return NGX_OK;
+    }
+
+    addr->nregex = regex;
+    addr->regex = ngx_palloc(cf->pool,
+                             regex * sizeof(ngx_stream_server_name_t));
+    if (addr->regex == NULL) {
+        return NGX_ERROR;
+    }
+
+    i = 0;
+
+    for (s = 0; s < addr->servers.nelts; s++) {
+
+        name = cscfp[s]->server_names.elts;
+
+        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
+            if (name[n].regex) {
+                addr->regex[i++] = name[n];
+            }
+        }
+    }
 
 #endif
 
+    return NGX_OK;
+
+failed:
+
+    ngx_destroy_pool(ha.temp_pool);
+
+    return NGX_ERROR;
+}
+
 
 static ngx_int_t
 ngx_stream_cmp_conf_addrs(const void *one, const void *two)
@@ -630,12 +876,12 @@ ngx_stream_cmp_conf_addrs(const void *on
     second = (ngx_stream_conf_addr_t *) two;
 
     if (first->opt.wildcard) {
-        /* a wildcard must be the last resort, shift it to the end */
+        /* a wildcard address must be the last resort, shift it to the end */
         return 1;
     }
 
     if (second->opt.wildcard) {
-        /* a wildcard must be the last resort, shift it to the end */
+        /* a wildcard address must be the last resort, shift it to the end */
         return -1;
     }
 
@@ -653,3 +899,265 @@ ngx_stream_cmp_conf_addrs(const void *on
 
     return 0;
 }
+
+
+static int ngx_libc_cdecl
+ngx_stream_cmp_dns_wildcards(const void *one, const void *two)
+{
+    ngx_hash_key_t  *first, *second;
+
+    first = (ngx_hash_key_t *) one;
+    second = (ngx_hash_key_t *) two;
+
+    return ngx_dns_strcmp(first->key.data, second->key.data);
+}
+
+
+static ngx_int_t
+ngx_stream_init_listening(ngx_conf_t *cf, ngx_stream_conf_port_t *port)
+{
+    ngx_uint_t               i, last, bind_wildcard;
+    ngx_listening_t         *ls;
+    ngx_stream_port_t       *stport;
+    ngx_stream_conf_addr_t  *addr;
+
+    addr = port->addrs.elts;
+    last = port->addrs.nelts;
+
+    /*
+     * If there is a binding to an "*:port" then we need to bind() to
+     * the "*:port" only and ignore other implicit bindings.  The bindings
+     * have been already sorted: explicit bindings are on the start, then
+     * implicit bindings go, and wildcard binding is in the end.
+     */
+
+    if (addr[last - 1].opt.wildcard) {
+        addr[last - 1].opt.bind = 1;
+        bind_wildcard = 1;
+
+    } else {
+        bind_wildcard = 0;
+    }
+
+    i = 0;
+
+    while (i < last) {
+
+        if (bind_wildcard && !addr[i].opt.bind) {
+            i++;
+            continue;
+        }
+
+        ls = ngx_stream_add_listening(cf, &addr[i]);
+        if (ls == NULL) {
+            return NGX_ERROR;
+        }
+
+        stport = ngx_pcalloc(cf->pool, sizeof(ngx_stream_port_t));
+        if (stport == NULL) {
+            return NGX_ERROR;
+        }
+
+        ls->servers = stport;
+
+        stport->naddrs = i + 1;
+
+        switch (ls->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) {
+                return NGX_ERROR;
+            }
+            break;
+#endif
+        default: /* AF_INET */
+            if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {
+                return NGX_ERROR;
+            }
+            break;
+        }
+
+        addr++;
+        last--;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_listening_t *
+ngx_stream_add_listening(ngx_conf_t *cf, ngx_stream_conf_addr_t *addr)
+{
+    ngx_listening_t             *ls;
+    ngx_stream_core_srv_conf_t  *cscf;
+
+    ls = ngx_create_listening(cf, addr->opt.sockaddr, addr->opt.socklen);
+    if (ls == NULL) {
+        return NULL;
+    }
+
+    ls->addr_ntop = 1;
+
+    ls->handler = ngx_stream_init_connection;
+
+    ls->pool_size = 256;
+
+    cscf = addr->default_server;
+
+    ls->logp = cscf->error_log;
+    ls->log.data = &ls->addr_text;
+    ls->log.handler = ngx_accept_log_error;
+
+    ls->type = addr->opt.type;
+    ls->backlog = addr->opt.backlog;
+    ls->rcvbuf = addr->opt.rcvbuf;
+    ls->sndbuf = addr->opt.sndbuf;
+
+    ls->keepalive = addr->opt.so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+    ls->keepidle = addr->opt.tcp_keepidle;
+    ls->keepintvl = addr->opt.tcp_keepintvl;
+    ls->keepcnt = addr->opt.tcp_keepcnt;
+#endif
+
+#if (NGX_HAVE_INET6)
+    ls->ipv6only = addr->opt.ipv6only;
+#endif
+
+#if (NGX_HAVE_TCP_FASTOPEN)
+    ls->fastopen = addr->opt.fastopen;
+#endif
+
+#if (NGX_HAVE_REUSEPORT)
+    ls->reuseport = addr->opt.reuseport;
+#endif
+
+    ls->wildcard = addr->opt.wildcard;
+
+    return ls;
+}
+
+
+static ngx_int_t
+ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
+    ngx_stream_conf_addr_t *addr)
+{
+    ngx_uint_t                   i;
+    struct sockaddr_in          *sin;
+    ngx_stream_in_addr_t        *addrs;
+    ngx_stream_virtual_names_t  *vn;
+
+    stport->addrs = ngx_pcalloc(cf->pool,
+                                stport->naddrs * sizeof(ngx_stream_in_addr_t));
+    if (stport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs = stport->addrs;
+
+    for (i = 0; i < stport->naddrs; i++) {
+
+        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
+        addrs[i].addr = sin->sin_addr.s_addr;
+        addrs[i].conf.default_server = addr[i].default_server;
+#if (NGX_STREAM_SSL)
+        addrs[i].conf.ssl = addr[i].opt.ssl;
+#endif
+        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
+
+        if (addr[i].hash.buckets == NULL
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL)
+            && (addr[i].wc_tail == NULL
+                || addr[i].wc_tail->hash.buckets == NULL)
+#if (NGX_PCRE)
+            && addr[i].nregex == 0
+#endif
+            )
+        {
+            continue;
+        }
+
+        vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t));
+        if (vn == NULL) {
+            return NGX_ERROR;
+        }
+
+        addrs[i].conf.virtual_names = vn;
+
+        vn->names.hash = addr[i].hash;
+        vn->names.wc_head = addr[i].wc_head;
+        vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+        vn->nregex = addr[i].nregex;
+        vn->regex = addr[i].regex;
+#endif
+    }
+
+    return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
+    ngx_stream_conf_addr_t *addr)
+{
+    ngx_uint_t                   i;
+    struct sockaddr_in6         *sin6;
+    ngx_stream_in6_addr_t       *addrs6;
+    ngx_stream_virtual_names_t  *vn;
+
+    stport->addrs = ngx_pcalloc(cf->pool,
+                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));
+    if (stport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs6 = stport->addrs;
+
+    for (i = 0; i < stport->naddrs; i++) {
+
+        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
+        addrs6[i].addr6 = sin6->sin6_addr;
+        addrs6[i].conf.default_server = addr[i].default_server;
+#if (NGX_STREAM_SSL)
+        addrs6[i].conf.ssl = addr[i].opt.ssl;
+#endif
+        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
+
+        if (addr[i].hash.buckets == NULL
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL)
+            && (addr[i].wc_tail == NULL
+                || addr[i].wc_tail->hash.buckets == NULL)
+#if (NGX_PCRE)
+            && addr[i].nregex == 0
+#endif
+            )
+        {
+            continue;
+        }
+
+        vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t));
+        if (vn == NULL) {
+            return NGX_ERROR;
+        }
+
+        addrs6[i].conf.virtual_names = vn;
+
+        vn->names.hash = addr[i].hash;
+        vn->names.wc_head = addr[i].wc_head;
+        vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+        vn->nregex = addr[i].nregex;
+        vn->regex = addr[i].regex;
+#endif
+    }
+
+    return NGX_OK;
+}
+
+#endif
diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h
--- a/src/stream/ngx_stream.h
+++ b/src/stream/ngx_stream.h
@@ -45,9 +45,8 @@ typedef struct {
     socklen_t                      socklen;
     ngx_str_t                      addr_text;
 
-    /* server ctx */
-    ngx_stream_conf_ctx_t         *ctx;
-
+    unsigned                       set:1;
+    unsigned                       default_server:1;
     unsigned                       bind:1;
     unsigned                       wildcard:1;
     unsigned                       ssl:1;
@@ -69,50 +68,7 @@ typedef struct {
     int                            fastopen;
 #endif
     int                            type;
-} ngx_stream_listen_t;
-
-
-typedef struct {
-    ngx_stream_conf_ctx_t         *ctx;
-    ngx_str_t                      addr_text;
-    unsigned                       ssl:1;
-    unsigned                       proxy_protocol:1;
-} ngx_stream_addr_conf_t;
-
-typedef struct {
-    in_addr_t                      addr;
-    ngx_stream_addr_conf_t         conf;
-} ngx_stream_in_addr_t;
-
-
-#if (NGX_HAVE_INET6)
-
-typedef struct {
-    struct in6_addr                addr6;
-    ngx_stream_addr_conf_t         conf;
-} ngx_stream_in6_addr_t;
-
-#endif
-
-
-typedef struct {
-    /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
-    void                          *addrs;
-    ngx_uint_t                     naddrs;
-} ngx_stream_port_t;
-
-
-typedef struct {
-    int                            family;
-    int                            type;
-    in_port_t                      port;
-    ngx_array_t                    addrs; /* array of ngx_stream_conf_addr_t */
-} ngx_stream_conf_port_t;
-
-
-typedef struct {
-    ngx_stream_listen_t            opt;
-} ngx_stream_conf_addr_t;
+} ngx_stream_listen_opt_t;
 
 
 typedef enum {
@@ -153,7 +109,6 @@ typedef struct {
 
 typedef struct {
     ngx_array_t                    servers;     /* ngx_stream_core_srv_conf_t */
-    ngx_array_t                    listen;      /* ngx_stream_listen_t */
 
     ngx_stream_phase_engine_t      phase_engine;
 
@@ -163,16 +118,24 @@ typedef struct {
     ngx_array_t                    prefix_variables; /* ngx_stream_variable_t */
     ngx_uint_t                     ncaptures;
 
+    ngx_uint_t                     server_names_hash_max_size;
+    ngx_uint_t                     server_names_hash_bucket_size;
+
     ngx_uint_t                     variables_hash_max_size;
     ngx_uint_t                     variables_hash_bucket_size;
 
     ngx_hash_keys_arrays_t        *variables_keys;
 
+    ngx_array_t                   *ports;
+
     ngx_stream_phase_t             phases[NGX_STREAM_LOG_PHASE + 1];
 } ngx_stream_core_main_conf_t;
 
 
 typedef struct {
+    /* array of the ngx_stream_server_name_t, "server_name" directive */
+    ngx_array_t                    server_names;
+
     ngx_stream_content_handler_pt  handler;
 
     ngx_stream_conf_ctx_t         *ctx;
@@ -180,6 +143,8 @@ typedef struct {
     u_char                        *file_name;
     ngx_uint_t                     line;
 
+    ngx_str_t                      server_name;
+
     ngx_flag_t                     tcp_nodelay;
     size_t                         preread_buffer_size;
     ngx_msec_t                     preread_timeout;
@@ -191,10 +156,98 @@ typedef struct {
 
     ngx_msec_t                     proxy_protocol_timeout;
 
-    ngx_uint_t                     listen;  /* unsigned  listen:1; */
+    unsigned                       listen:1;
+#if (NGX_PCRE)
+    unsigned                       captures:1;
+#endif
 } ngx_stream_core_srv_conf_t;
 
 
+/* list of structures to find core_srv_conf quickly at run time */
+
+
+typedef struct {
+#if (NGX_PCRE)
+    ngx_stream_regex_t            *regex;
+#endif
+    ngx_stream_core_srv_conf_t    *server;   /* virtual name server conf */
+    ngx_str_t                      name;
+} ngx_stream_server_name_t;
+
+
+typedef struct {
+    ngx_hash_combined_t            names;
+
+    ngx_uint_t                     nregex;
+    ngx_stream_server_name_t      *regex;
+} ngx_stream_virtual_names_t;
+
+
+typedef struct {
+    /* the default server configuration for this address:port */
+    ngx_stream_core_srv_conf_t    *default_server;
+
+    ngx_stream_virtual_names_t    *virtual_names;
+
+    unsigned                       ssl:1;
+    unsigned                       proxy_protocol:1;
+} ngx_stream_addr_conf_t;
+
+
+typedef struct {
+    in_addr_t                      addr;
+    ngx_stream_addr_conf_t         conf;
+} ngx_stream_in_addr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+    struct in6_addr                addr6;
+    ngx_stream_addr_conf_t         conf;
+} ngx_stream_in6_addr_t;
+
+#endif
+
+
+typedef struct {
+    /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
+    void                          *addrs;
+    ngx_uint_t                     naddrs;
+} ngx_stream_port_t;
+
+
+typedef struct {
+    int                            family;
+    int                            type;
+    in_port_t                      port;
+    ngx_array_t                    addrs; /* array of ngx_stream_conf_addr_t */
+} ngx_stream_conf_port_t;
+
+
+typedef struct {
+    ngx_stream_listen_opt_t        opt;
+
+    unsigned                       protocols:3;
+    unsigned                       protocols_set:1;
+    unsigned                       protocols_changed:1;
+
+    ngx_hash_t                     hash;
+    ngx_hash_wildcard_t           *wc_head;
+    ngx_hash_wildcard_t           *wc_tail;
+
+#if (NGX_PCRE)
+    ngx_uint_t                     nregex;
+    ngx_stream_server_name_t      *regex;
+#endif
+
+    /* the default server configuration for this address:port */
+    ngx_stream_core_srv_conf_t    *default_server;
+    ngx_array_t                    servers;
+                                      /* array of ngx_stream_core_srv_conf_t */
+} ngx_stream_conf_addr_t;
+
+
 struct ngx_stream_session_s {
     uint32_t                       signature;         /* "STRM" */
 
@@ -210,6 +263,8 @@ struct ngx_stream_session_s {
     void                         **main_conf;
     void                         **srv_conf;
 
+    ngx_stream_virtual_names_t    *virtual_names;
+
     ngx_stream_upstream_t         *upstream;
     ngx_array_t                   *upstream_states;
                                            /* of ngx_stream_upstream_state_t */
@@ -283,6 +338,9 @@ typedef struct {
 #define NGX_STREAM_WRITE_BUFFERED  0x10
 
 
+ngx_int_t ngx_stream_add_listen(ngx_conf_t *cf,
+    ngx_stream_core_srv_conf_t *cscf, ngx_stream_listen_opt_t *lsopt);
+
 void ngx_stream_core_run_phases(ngx_stream_session_t *s);
 ngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s,
     ngx_stream_phase_handler_t *ph);
@@ -291,6 +349,10 @@ ngx_int_t ngx_stream_core_preread_phase(
 ngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s,
     ngx_stream_phase_handler_t *ph);
 
+ngx_int_t ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool,
+    ngx_uint_t alloc);
+ngx_int_t ngx_stream_find_virtual_server(ngx_stream_session_t *s,
+    ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp);
 
 void ngx_stream_init_connection(ngx_connection_t *c);
 void ngx_stream_session_handler(ngx_event_t *rev);
diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c
--- a/src/stream/ngx_stream_core_module.c
+++ b/src/stream/ngx_stream_core_module.c
@@ -26,6 +26,8 @@ static char *ngx_stream_core_server(ngx_
     void *conf);
 static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
@@ -46,6 +48,20 @@ static ngx_command_t  ngx_stream_core_co
       offsetof(ngx_stream_core_main_conf_t, variables_hash_bucket_size),
       NULL },
 
+    { ngx_string("server_names_hash_max_size"),
+      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_STREAM_MAIN_CONF_OFFSET,
+      offsetof(ngx_stream_core_main_conf_t, server_names_hash_max_size),
+      NULL },
+
+    { ngx_string("server_names_hash_bucket_size"),
+      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_num_slot,
+      NGX_STREAM_MAIN_CONF_OFFSET,
+      offsetof(ngx_stream_core_main_conf_t, server_names_hash_bucket_size),
+      NULL },
+
     { ngx_string("server"),
       NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
       ngx_stream_core_server,
@@ -60,6 +76,13 @@ static ngx_command_t  ngx_stream_core_co
       0,
       NULL },
 
+    { ngx_string("server_name"),
+      NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+      ngx_stream_core_server_name,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("error_log"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
       ngx_stream_core_error_log,
@@ -418,6 +441,149 @@ ngx_stream_core_content_phase(ngx_stream
 }
 
 
+ngx_int_t
+ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
+{
+    u_char  *h, ch;
+    size_t   i, dot_pos, host_len;
+
+    enum {
+        sw_usual = 0,
+        sw_literal,
+        sw_rest
+    } state;
+
+    dot_pos = host->len;
+    host_len = host->len;
+
+    h = host->data;
+
+    state = sw_usual;
+
+    for (i = 0; i < host->len; i++) {
+        ch = h[i];
+
+        switch (ch) {
+
+        case '.':
+            if (dot_pos == i - 1) {
+                return NGX_DECLINED;
+            }
+            dot_pos = i;
+            break;
+
+        case ':':
+            if (state == sw_usual) {
+                host_len = i;
+                state = sw_rest;
+            }
+            break;
+
+        case '[':
+            if (i == 0) {
+                state = sw_literal;
+            }
+            break;
+
+        case ']':
+            if (state == sw_literal) {
+                host_len = i + 1;
+                state = sw_rest;
+            }
+            break;
+
+        default:
+
+            if (ngx_path_separator(ch)) {
+                return NGX_DECLINED;
+            }
+
+            if (ch <= 0x20 || ch == 0x7f) {
+                return NGX_DECLINED;
+            }
+
+            if (ch >= 'A' && ch <= 'Z') {
+                alloc = 1;
+            }
+
+            break;
+        }
+    }
+
+    if (dot_pos == host_len - 1) {
+        host_len--;
+    }
+
+    if (host_len == 0) {
+        return NGX_DECLINED;
+    }
+
+    if (alloc) {
+        host->data = ngx_pnalloc(pool, host_len);
+        if (host->data == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_strlow(host->data, h, host_len);
+    }
+
+    host->len = host_len;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_stream_find_virtual_server(ngx_stream_session_t *s,
+    ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp)
+{
+    ngx_stream_core_srv_conf_t  *cscf;
+
+    if (s->virtual_names == NULL) {
+        return NGX_DECLINED;
+    }
+
+    cscf = ngx_hash_find_combined(&s->virtual_names->names,
+                                  ngx_hash_key(host->data, host->len),
+                                  host->data, host->len);
+
+    if (cscf) {
+        *cscfp = cscf;
+        return NGX_OK;
+    }
+
+#if (NGX_PCRE)
+
+    if (host->len && s->virtual_names->nregex) {
+        ngx_int_t                  n;
+        ngx_uint_t                 i;
+        ngx_stream_server_name_t  *sn;
+
+        sn = s->virtual_names->regex;
+
+        for (i = 0; i < s->virtual_names->nregex; i++) {
+
+            n = ngx_stream_regex_exec(s, sn[i].regex, host);
+
+            if (n == NGX_DECLINED) {
+                continue;
+            }
+
+            if (n == NGX_OK) {
+                *cscfp = sn[i].server;
+                return NGX_OK;
+            }
+
+            return NGX_ERROR;
+        }
+    }
+
+#endif /* NGX_PCRE */
+
+    return NGX_DECLINED;
+}
+
+
 static ngx_int_t
 ngx_stream_core_preconfiguration(ngx_conf_t *cf)
 {
@@ -442,11 +608,8 @@ ngx_stream_core_create_main_conf(ngx_con
         return NULL;
     }
 
-    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_stream_listen_t))
-        != NGX_OK)
-    {
-        return NULL;
-    }
+    cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;
+    cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;
 
     cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;
     cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;
@@ -460,6 +623,14 @@ ngx_stream_core_init_main_conf(ngx_conf_
 {
     ngx_stream_core_main_conf_t *cmcf = conf;
 
+    ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);
+    ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,
+                             ngx_cacheline_size);
+
+    cmcf->server_names_hash_bucket_size =
+            ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);
+
+
     ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);
     ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);
 
@@ -491,6 +662,13 @@ ngx_stream_core_create_srv_conf(ngx_conf
      *     cscf->error_log = NULL;
      */
 
+    if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,
+                       sizeof(ngx_stream_server_name_t))
+        != NGX_OK)
+    {
+        return NULL;
+    }
+
     cscf->file_name = cf->conf_file->file.name.data;
     cscf->line = cf->conf_file->line;
     cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
@@ -509,6 +687,9 @@ ngx_stream_core_merge_srv_conf(ngx_conf_
     ngx_stream_core_srv_conf_t *prev = parent;
     ngx_stream_core_srv_conf_t *conf = child;
 
+    ngx_str_t                  name;
+    ngx_stream_server_name_t  *sn;
+
     ngx_conf_merge_msec_value(conf->resolver_timeout,
                               prev->resolver_timeout, 30000);
 
@@ -556,6 +737,37 @@ ngx_stream_core_merge_srv_conf(ngx_conf_
     ngx_conf_merge_msec_value(conf->preread_timeout,
                               prev->preread_timeout, 30000);
 
+    if (conf->server_names.nelts == 0) {
+        /* the array has 4 empty preallocated elements, so push cannot fail */
+        sn = ngx_array_push(&conf->server_names);
+#if (NGX_PCRE)
+        sn->regex = NULL;
+#endif
+        sn->server = conf;
+        ngx_str_set(&sn->name, "");
+    }
+
+    sn = conf->server_names.elts;
+    name = sn[0].name;
+
+#if (NGX_PCRE)
+    if (sn->regex) {
+        name.len++;
+        name.data--;
+    } else
+#endif
+
+    if (name.data[0] == '.') {
+        name.len--;
+        name.data++;
+    }
+
+    conf->server_name.len = name.len;
+    conf->server_name.data = ngx_pstrdup(cf->pool, &name);
+    if (conf->server_name.data == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -655,11 +867,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
 {
     ngx_stream_core_srv_conf_t  *cscf = conf;
 
-    ngx_str_t                    *value, size;
-    ngx_url_t                     u;
-    ngx_uint_t                    i, n, backlog;
-    ngx_stream_listen_t          *ls, *als, *nls;
-    ngx_stream_core_main_conf_t  *cmcf;
+    ngx_str_t                *value, size;
+    ngx_url_t                 u;
+    ngx_uint_t                n, i, backlog;
+    ngx_stream_listen_opt_t   lsopt;
 
     cscf->listen = 1;
 
@@ -680,51 +891,48 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
         return NGX_CONF_ERROR;
     }
 
-    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
-
-    ls = ngx_array_push(&cmcf->listen);
-    if (ls == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    ngx_memzero(ls, sizeof(ngx_stream_listen_t));
+    ngx_memzero(&lsopt, sizeof(ngx_stream_listen_opt_t));
 
-    ls->backlog = NGX_LISTEN_BACKLOG;
-    ls->rcvbuf = -1;
-    ls->sndbuf = -1;
-    ls->type = SOCK_STREAM;
-    ls->ctx = cf->ctx;
-
+    lsopt.backlog = NGX_LISTEN_BACKLOG;
+    lsopt.type = SOCK_STREAM;
+    lsopt.rcvbuf = -1;
+    lsopt.sndbuf = -1;
 #if (NGX_HAVE_TCP_FASTOPEN)
-    ls->fastopen = -1;
+    lsopt.fastopen = -1;
 #endif
-
 #if (NGX_HAVE_INET6)
-    ls->ipv6only = 1;
+    lsopt.ipv6only = 1;
 #endif
 
     backlog = 0;
 
     for (i = 2; i < cf->args->nelts; i++) {
 
+        if (ngx_strcmp(value[i].data, "default_server") == 0) {
+            lsopt.default_server = 1;
+            continue;
+        }
+
 #if !(NGX_WIN32)
         if (ngx_strcmp(value[i].data, "udp") == 0) {
-            ls->type = SOCK_DGRAM;
+            lsopt.type = SOCK_DGRAM;
             continue;
         }
 #endif
 
         if (ngx_strcmp(value[i].data, "bind") == 0) {
-            ls->bind = 1;
+            lsopt.set = 1;
+            lsopt.bind = 1;
             continue;
         }
 
 #if (NGX_HAVE_TCP_FASTOPEN)
         if (ngx_strncmp(value[i].data, "fastopen=", 9) == 0) {
-            ls->fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);
-            ls->bind = 1;
+            lsopt.fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);
+            lsopt.set = 1;
+            lsopt.bind = 1;
 
-            if (ls->fastopen == NGX_ERROR) {
+            if (lsopt.fastopen == NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "invalid fastopen \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
@@ -735,10 +943,11 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
 #endif
 
         if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) {
-            ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
-            ls->bind = 1;
+            lsopt.backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
+            lsopt.set = 1;
+            lsopt.bind = 1;
 
-            if (ls->backlog == NGX_ERROR || ls->backlog == 0) {
+            if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "invalid backlog \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
@@ -753,10 +962,11 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
             size.len = value[i].len - 7;
             size.data = value[i].data + 7;
 
-            ls->rcvbuf = ngx_parse_size(&size);
-            ls->bind = 1;
+            lsopt.rcvbuf = ngx_parse_size(&size);
+            lsopt.set = 1;
+            lsopt.bind = 1;
 
-            if (ls->rcvbuf == NGX_ERROR) {
+            if (lsopt.rcvbuf == NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "invalid rcvbuf \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
@@ -769,10 +979,11 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
             size.len = value[i].len - 7;
             size.data = value[i].data + 7;
 
-            ls->sndbuf = ngx_parse_size(&size);
-            ls->bind = 1;
+            lsopt.sndbuf = ngx_parse_size(&size);
+            lsopt.set = 1;
+            lsopt.bind = 1;
 
-            if (ls->sndbuf == NGX_ERROR) {
+            if (lsopt.sndbuf == NGX_ERROR) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "invalid sndbuf \"%V\"", &value[i]);
                 return NGX_CONF_ERROR;
@@ -784,10 +995,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
         if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
             if (ngx_strcmp(&value[i].data[10], "n") == 0) {
-                ls->ipv6only = 1;
+                lsopt.ipv6only = 1;
 
             } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
-                ls->ipv6only = 0;
+                lsopt.ipv6only = 0;
 
             } else {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -796,7 +1007,9 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
                 return NGX_CONF_ERROR;
             }
 
-            ls->bind = 1;
+            lsopt.set = 1;
+            lsopt.bind = 1;
+
             continue;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -808,8 +1021,9 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
 
         if (ngx_strcmp(value[i].data, "reuseport") == 0) {
 #if (NGX_HAVE_REUSEPORT)
-            ls->reuseport = 1;
-            ls->bind = 1;
+            lsopt.reuseport = 1;
+            lsopt.set = 1;
+            lsopt.bind = 1;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "reuseport is not supported "
@@ -820,17 +1034,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
 
         if (ngx_strcmp(value[i].data, "ssl") == 0) {
 #if (NGX_STREAM_SSL)
-            ngx_stream_ssl_conf_t  *sslcf;
-
-            sslcf = ngx_stream_conf_get_module_srv_conf(cf,
-                                                        ngx_stream_ssl_module);
-
-            sslcf->listen = 1;
-            sslcf->file = cf->conf_file->file.name.data;
-            sslcf->line = cf->conf_file->line;
-
-            ls->ssl = 1;
-
+            lsopt.ssl = 1;
             continue;
 #else
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -843,10 +1047,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
         if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
 
             if (ngx_strcmp(&value[i].data[13], "on") == 0) {
-                ls->so_keepalive = 1;
+                lsopt.so_keepalive = 1;
 
             } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
-                ls->so_keepalive = 2;
+                lsopt.so_keepalive = 2;
 
             } else {
 
@@ -865,8 +1069,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
                 if (p > s.data) {
                     s.len = p - s.data;
 
-                    ls->tcp_keepidle = ngx_parse_time(&s, 1);
-                    if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
+                    lsopt.tcp_keepidle = ngx_parse_time(&s, 1);
+                    if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) {
                         goto invalid_so_keepalive;
                     }
                 }
@@ -881,8 +1085,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
                 if (p > s.data) {
                     s.len = p - s.data;
 
-                    ls->tcp_keepintvl = ngx_parse_time(&s, 1);
-                    if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
+                    lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);
+                    if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) {
                         goto invalid_so_keepalive;
                     }
                 }
@@ -892,19 +1096,19 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
                 if (s.data < end) {
                     s.len = end - s.data;
 
-                    ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
-                    if (ls->tcp_keepcnt == NGX_ERROR) {
+                    lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len);
+                    if (lsopt.tcp_keepcnt == NGX_ERROR) {
                         goto invalid_so_keepalive;
                     }
                 }
 
-                if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
-                    && ls->tcp_keepcnt == 0)
+                if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0
+                    && lsopt.tcp_keepcnt == 0)
                 {
                     goto invalid_so_keepalive;
                 }
 
-                ls->so_keepalive = 1;
+                lsopt.so_keepalive = 1;
 
 #else
 
@@ -916,7 +1120,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
 #endif
             }
 
-            ls->bind = 1;
+            lsopt.set = 1;
+            lsopt.bind = 1;
 
             continue;
 
@@ -931,7 +1136,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
         }
 
         if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) {
-            ls->proxy_protocol = 1;
+            lsopt.proxy_protocol = 1;
             continue;
         }
 
@@ -940,27 +1145,27 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
         return NGX_CONF_ERROR;
     }
 
-    if (ls->type == SOCK_DGRAM) {
+    if (lsopt.type == SOCK_DGRAM) {
         if (backlog) {
             return "\"backlog\" parameter is incompatible with \"udp\"";
         }
 
 #if (NGX_STREAM_SSL)
-        if (ls->ssl) {
+        if (lsopt.ssl) {
             return "\"ssl\" parameter is incompatible with \"udp\"";
         }
 #endif
 
-        if (ls->so_keepalive) {
+        if (lsopt.so_keepalive) {
             return "\"so_keepalive\" parameter is incompatible with \"udp\"";
         }
 
-        if (ls->proxy_protocol) {
+        if (lsopt.proxy_protocol) {
             return "\"proxy_protocol\" parameter is incompatible with \"udp\"";
         }
 
 #if (NGX_HAVE_TCP_FASTOPEN)
-        if (ls->fastopen != -1) {
+        if (lsopt.fastopen != -1) {
             return "\"fastopen\" parameter is incompatible with \"udp\"";
         }
 #endif
@@ -977,40 +1182,12 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
             }
         }
 
-        if (n != 0) {
-            nls = ngx_array_push(&cmcf->listen);
-            if (nls == NULL) {
-                return NGX_CONF_ERROR;
-            }
-
-            *nls = *ls;
-
-        } else {
-            nls = ls;
-        }
-
-        nls->sockaddr = u.addrs[n].sockaddr;
-        nls->socklen = u.addrs[n].socklen;
-        nls->addr_text = u.addrs[n].name;
-        nls->wildcard = ngx_inet_wildcard(nls->sockaddr);
+        lsopt.sockaddr = u.addrs[n].sockaddr;
+        lsopt.socklen = u.addrs[n].socklen;
+        lsopt.addr_text = u.addrs[n].name;
+        lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr);
 
-        als = cmcf->listen.elts;
-
-        for (i = 0; i < cmcf->listen.nelts - 1; i++) {
-            if (nls->type != als[i].type) {
-                continue;
-            }
-
-            if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,
-                                 nls->sockaddr, nls->socklen, 1)
-                != NGX_OK)
-            {
-                continue;
-            }
-
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "duplicate \"%V\" address and port pair",
-                               &nls->addr_text);
+        if (ngx_stream_add_listen(cf, cscf, &lsopt) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
 
@@ -1023,6 +1200,107 @@ ngx_stream_core_listen(ngx_conf_t *cf, n
 
 
 static char *
+ngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_stream_core_srv_conf_t *cscf = conf;
+
+    u_char                     ch;
+    ngx_str_t                 *value;
+    ngx_uint_t                 i;
+    ngx_stream_server_name_t  *sn;
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        ch = value[i].data[0];
+
+        if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))
+            || (ch == '.' && value[i].len < 2))
+        {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "server name \"%V\" is invalid", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_strchr(value[i].data, '/')) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "server name \"%V\" has suspicious symbols",
+                               &value[i]);
+        }
+
+        sn = ngx_array_push(&cscf->server_names);
+        if (sn == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+#if (NGX_PCRE)
+        sn->regex = NULL;
+#endif
+        sn->server = cscf;
+
+        if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {
+            sn->name = cf->cycle->hostname;
+
+        } else {
+            sn->name = value[i];
+        }
+
+        if (value[i].data[0] != '~') {
+            ngx_strlow(sn->name.data, sn->name.data, sn->name.len);
+            continue;
+        }
+
+#if (NGX_PCRE)
+        {
+        u_char               *p;
+        ngx_regex_compile_t   rc;
+        u_char                errstr[NGX_MAX_CONF_ERRSTR];
+
+        if (value[i].len == 1) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "empty regex in server name \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        value[i].len--;
+        value[i].data++;
+
+        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+        rc.pattern = value[i];
+        rc.err.len = NGX_MAX_CONF_ERRSTR;
+        rc.err.data = errstr;
+
+        for (p = value[i].data; p < value[i].data + value[i].len; p++) {
+            if (*p >= 'A' && *p <= 'Z') {
+                rc.options = NGX_REGEX_CASELESS;
+                break;
+            }
+        }
+
+        sn->regex = ngx_stream_regex_compile(cf, &rc);
+        if (sn->regex == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        sn->name = value[i];
+        cscf->captures = (rc.captures > 0);
+        }
+#else
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "using regex \"%V\" "
+                           "requires PCRE library", &value[i]);
+
+        return NGX_CONF_ERROR;
+#endif
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
 ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_stream_core_srv_conf_t  *cscf = conf;
diff --git a/src/stream/ngx_stream_handler.c b/src/stream/ngx_stream_handler.c
--- a/src/stream/ngx_stream_handler.c
+++ b/src/stream/ngx_stream_handler.c
@@ -30,6 +30,7 @@ ngx_stream_init_connection(ngx_connectio
     struct sockaddr_in           *sin;
     ngx_stream_in_addr_t         *addr;
     ngx_stream_session_t         *s;
+    ngx_stream_conf_ctx_t        *ctx;
     ngx_stream_addr_conf_t       *addr_conf;
 #if (NGX_HAVE_INET6)
     struct sockaddr_in6          *sin6;
@@ -121,9 +122,12 @@ ngx_stream_init_connection(ngx_connectio
         return;
     }
 
+    ctx = addr_conf->default_server->ctx;
+
     s->signature = NGX_STREAM_MODULE;
-    s->main_conf = addr_conf->ctx->main_conf;
-    s->srv_conf = addr_conf->ctx->srv_conf;
+    s->main_conf = ctx->main_conf;
+    s->srv_conf = ctx->srv_conf;
+    s->virtual_names = addr_conf->virtual_names;
 
 #if (NGX_STREAM_SSL)
     s->ssl = addr_conf->ssl;
@@ -144,7 +148,7 @@ ngx_stream_init_connection(ngx_connectio
 
     ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA %sclient %*s connected to %V",
                   c->number, c->type == SOCK_DGRAM ? "udp " : "",
-                  len, text, &addr_conf->addr_text);
+                  len, text, &c->listening->addr_text);
 
     c->log->connection = c->number;
     c->log->handler = ngx_stream_log_error;
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -219,6 +219,13 @@ static ngx_command_t  ngx_stream_ssl_com
       offsetof(ngx_stream_ssl_conf_t, conf_commands),
       &ngx_stream_ssl_conf_command_post },
 
+    { ngx_string("ssl_reject_handshake"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      offsetof(ngx_stream_ssl_conf_t, reject_handshake),
+      NULL },
+
     { ngx_string("ssl_alpn"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
       ngx_stream_ssl_alpn,
@@ -458,7 +465,112 @@ ngx_stream_ssl_handshake_handler(ngx_con
 static int
 ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
 {
+    ngx_int_t                    rc;
+    ngx_str_t                    host;
+    const char                  *servername;
+    ngx_connection_t            *c;
+    ngx_stream_session_t        *s;
+    ngx_stream_ssl_conf_t       *sscf;
+    ngx_stream_core_srv_conf_t  *cscf;
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    if (c->ssl->handshaked) {
+        *ad = SSL_AD_NO_RENEGOTIATION;
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+
+    s = c->data;
+
+    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
+
+    if (servername == NULL) {
+        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                       "SSL server name: null");
+        goto done;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                   "SSL server name: \"%s\"", servername);
+
+    host.len = ngx_strlen(servername);
+
+    if (host.len == 0) {
+        goto done;
+    }
+
+    host.data = (u_char *) servername;
+
+    rc = ngx_stream_validate_host(&host, c->pool, 1);
+
+    if (rc == NGX_ERROR) {
+        goto error;
+    }
+
+    if (rc == NGX_DECLINED) {
+        goto done;
+    }
+
+    rc = ngx_stream_find_virtual_server(s, &host, &cscf);
+
+    if (rc == NGX_ERROR) {
+        goto error;
+    }
+
+    if (rc == NGX_DECLINED) {
+        goto done;
+    }
+
+    s->srv_conf = cscf->ctx->srv_conf;
+
+    ngx_set_connection_log(c, cscf->error_log);
+
+    sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
+
+    if (sscf->ssl.ctx) {
+        if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
+            goto error;
+        }
+
+        /*
+         * SSL_set_SSL_CTX() only changes certs as of 1.0.0d
+         * adjust other things we care about
+         */
+
+        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),
+                       SSL_CTX_get_verify_callback(sscf->ssl.ctx));
+
+        SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));
+
+#if OPENSSL_VERSION_NUMBER >= 0x009080dfL
+        /* only in 0.9.8m+ */
+        SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &
+                                    ~SSL_CTX_get_options(sscf->ssl.ctx));
+#endif
+
+        SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));
+
+#ifdef SSL_OP_NO_RENEGOTIATION
+        SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION);
+#endif
+    }
+
+done:
+
+    sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
+
+    if (sscf->reject_handshake) {
+        c->ssl->handshake_rejected = 1;
+        *ad = SSL_AD_UNRECOGNIZED_NAME;
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+
     return SSL_TLSEXT_ERR_OK;
+
+error:
+
+    *ad = SSL_AD_INTERNAL_ERROR;
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
 }
 
 #endif
@@ -655,7 +767,6 @@ ngx_stream_ssl_create_conf(ngx_conf_t *c
     /*
      * set by ngx_pcalloc():
      *
-     *     scf->listen = 0;
      *     scf->protocols = 0;
      *     scf->certificate_values = NULL;
      *     scf->dhparam = { 0, NULL };
@@ -674,6 +785,7 @@ ngx_stream_ssl_create_conf(ngx_conf_t *c
     scf->passwords = NGX_CONF_UNSET_PTR;
     scf->conf_commands = NGX_CONF_UNSET_PTR;
     scf->prefer_server_ciphers = NGX_CONF_UNSET;
+    scf->reject_handshake = NGX_CONF_UNSET;
     scf->verify = NGX_CONF_UNSET_UINT;
     scf->verify_depth = NGX_CONF_UNSET_UINT;
     scf->builtin_session_cache = NGX_CONF_UNSET;
@@ -702,6 +814,8 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
     ngx_conf_merge_value(conf->prefer_server_ciphers,
                          prev->prefer_server_ciphers, 0);
 
+    ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
+
     ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
                          (NGX_CONF_BITMASK_SET
                           |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
@@ -735,35 +849,21 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
 
     conf->ssl.log = cf->log;
 
-    if (!conf->listen) {
-        return NGX_CONF_OK;
-    }
-
-    if (conf->certificates == NULL) {
-        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                      "no \"ssl_certificate\" is defined for "
-                      "the \"listen ... ssl\" directive in %s:%ui",
-                      conf->file, conf->line);
-        return NGX_CONF_ERROR;
-    }
+    if (conf->certificates) {
 
-    if (conf->certificate_keys == NULL) {
-        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                      "no \"ssl_certificate_key\" is defined for "
-                      "the \"listen ... ssl\" directive in %s:%ui",
-                      conf->file, conf->line);
-        return NGX_CONF_ERROR;
-    }
+        if (conf->certificate_keys == NULL
+            || conf->certificate_keys->nelts < conf->certificates->nelts)
+        {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no \"ssl_certificate_key\" is defined "
+                          "for certificate \"%V\"",
+                          ((ngx_str_t *) conf->certificates->elts)
+                          + conf->certificates->nelts - 1);
+            return NGX_CONF_ERROR;
+        }
 
-    if (conf->certificate_keys->nelts < conf->certificates->nelts) {
-        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                      "no \"ssl_certificate_key\" is defined "
-                      "for certificate \"%V\" and "
-                      "the \"listen ... ssl\" directive in %s:%ui",
-                      ((ngx_str_t *) conf->certificates->elts)
-                      + conf->certificates->nelts - 1,
-                      conf->file, conf->line);
-        return NGX_CONF_ERROR;
+    } else if (!conf->reject_handshake) {
+        return NGX_CONF_OK;
     }
 
     if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
@@ -818,7 +918,7 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf
         return NGX_CONF_ERROR;
 #endif
 
-    } else {
+    } else if (conf->certificates) {
 
         /* configure certificates */
 
@@ -917,6 +1017,10 @@ ngx_stream_ssl_compile_certificates(ngx_
     ngx_stream_complex_value_t          *cv;
     ngx_stream_compile_complex_value_t   ccv;
 
+    if (conf->certificates == NULL) {
+        return NGX_OK;
+    }
+
     cert = conf->certificates->elts;
     key = conf->certificate_keys->elts;
     nelts = conf->certificates->nelts;
@@ -1195,8 +1299,13 @@ ngx_stream_ssl_conf_command_check(ngx_co
 static ngx_int_t
 ngx_stream_ssl_init(ngx_conf_t *cf)
 {
-    ngx_stream_handler_pt        *h;
-    ngx_stream_core_main_conf_t  *cmcf;
+    ngx_uint_t                     a, p, s;
+    ngx_stream_handler_pt         *h;
+    ngx_stream_ssl_conf_t         *sscf;
+    ngx_stream_conf_addr_t        *addr;
+    ngx_stream_conf_port_t        *port;
+    ngx_stream_core_srv_conf_t   **cscfp, *cscf;
+    ngx_stream_core_main_conf_t   *cmcf;
 
     cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
 
@@ -1207,5 +1316,58 @@ ngx_stream_ssl_init(ngx_conf_t *cf)
 
     *h = ngx_stream_ssl_handler;
 
+    if (cmcf->ports == NULL) {
+        return NGX_OK;
+    }
+
+    port = cmcf->ports->elts;
+    for (p = 0; p < cmcf->ports->nelts; p++) {
+
+        addr = port[p].addrs.elts;
+        for (a = 0; a < port[p].addrs.nelts; a++) {
+
+            if (!addr[a].opt.ssl) {
+                continue;
+            }
+
+            cscf = addr[a].default_server;
+            sscf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
+
+            if (sscf->certificates) {
+                continue;
+            }
+
+            if (!sscf->reject_handshake) {
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "no \"ssl_certificate\" is defined for "
+                              "the \"listen ... ssl\" directive in %s:%ui",
+                              cscf->file_name, cscf->line);
+                return NGX_ERROR;
+            }
+
+            /*
+             * if no certificates are defined in the default server,
+             * check all non-default server blocks
+             */
+
+            cscfp = addr[a].servers.elts;
+            for (s = 0; s < addr[a].servers.nelts; s++) {
+
+                cscf = cscfp[s];
+                sscf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
+
+                if (sscf->certificates || sscf->reject_handshake) {
+                    continue;
+                }
+
+                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                              "no \"ssl_certificate\" is defined for "
+                              "the \"listen ... ssl\" directive in %s:%ui",
+                              cscf->file_name, cscf->line);
+                return NGX_ERROR;
+            }
+        }
+    }
+
     return NGX_OK;
 }
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -18,10 +18,10 @@ typedef struct {
     ngx_msec_t       handshake_timeout;
 
     ngx_flag_t       prefer_server_ciphers;
+    ngx_flag_t       reject_handshake;
 
     ngx_ssl_t        ssl;
 
-    ngx_uint_t       listen;
     ngx_uint_t       protocols;
 
     ngx_uint_t       verify;
@@ -53,9 +53,6 @@ typedef struct {
 
     ngx_flag_t       session_tickets;
     ngx_array_t     *session_ticket_keys;
-
-    u_char          *file;
-    ngx_uint_t       line;
 } ngx_stream_ssl_conf_t;
 
 
diff --git a/src/stream/ngx_stream_ssl_preread_module.c b/src/stream/ngx_stream_ssl_preread_module.c
--- a/src/stream/ngx_stream_ssl_preread_module.c
+++ b/src/stream/ngx_stream_ssl_preread_module.c
@@ -33,6 +33,8 @@ typedef struct {
 static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s);
 static ngx_int_t ngx_stream_ssl_preread_parse_record(
     ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last);
+static ngx_int_t ngx_stream_ssl_preread_servername(ngx_stream_session_t *s,
+    ngx_str_t *servername);
 static ngx_int_t ngx_stream_ssl_preread_protocol_variable(
     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_ssl_preread_server_name_variable(
@@ -187,6 +189,10 @@ ngx_stream_ssl_preread_handler(ngx_strea
             return NGX_DECLINED;
         }
 
+        if (rc == NGX_OK) {
+            return ngx_stream_ssl_preread_servername(s, &ctx->host);
+        }
+
         if (rc != NGX_AGAIN) {
             return rc;
         }
@@ -404,9 +410,6 @@ ngx_stream_ssl_preread_parse_record(ngx_
         case sw_sni_host:
             ctx->host.len = (p[1] << 8) + p[2];
 
-            ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
-                           "ssl preread: SNI hostname \"%V\"", &ctx->host);
-
             state = sw_ext;
             dst = NULL;
             size = ext;
@@ -497,6 +500,54 @@ ngx_stream_ssl_preread_parse_record(ngx_
 
 
 static ngx_int_t
+ngx_stream_ssl_preread_servername(ngx_stream_session_t *s,
+    ngx_str_t *servername)
+{
+    ngx_int_t                    rc;
+    ngx_str_t                    host;
+    ngx_connection_t            *c;
+    ngx_stream_core_srv_conf_t  *cscf;
+
+    c = s->connection;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                   "SSL preread server name: \"%V\"", servername);
+
+    if (servername->len == 0) {
+        return NGX_OK;
+    }
+
+    host = *servername;
+
+    rc = ngx_stream_validate_host(&host, c->pool, 1);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DECLINED) {
+        return NGX_OK;
+    }
+
+    rc = ngx_stream_find_virtual_server(s, &host, &cscf);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DECLINED) {
+        return NGX_OK;
+    }
+
+    s->srv_conf = cscf->ctx->srv_conf;
+
+    ngx_set_connection_log(c, cscf->error_log);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_stream_ssl_preread_protocol_variable(ngx_stream_session_t *s,
     ngx_variable_value_t *v, uintptr_t data)
 {


More information about the nginx-devel mailing list