[PATCH 2 of 3] Stream: virtual servers

Sergey Kandaurov pluknet at nginx.com
Wed Dec 13 13:40:09 UTC 2023


> 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.

> 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().

> +
> +#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.

>             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.

> +    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));
+        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);
+
+
 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);
 
-
     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 "
                                "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]);
         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;
     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;
+    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


More information about the nginx-devel mailing list