Welcome! Log In Create A New Profile

Advanced

Re: [PATCH 2 of 3] Stream: virtual servers

Sergey Kandaurov
December 13, 2023 08:42AM
> On 10 Nov 2023, at 14:07, Roman Arutyunyan <arut@nginx.com> wrote:
>
> # HG changeset patch
> # User Roman Arutyunyan <arut@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
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[PATCH 0 of 3] Stream connection handling updates

Roman Arutyunyan 638 November 10, 2023 05:08AM

[PATCH 1 of 3] Stream: socket peek in preread phase

Roman Arutyunyan 76 November 10, 2023 05:08AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Sergey Kandaurov 66 December 12, 2023 08:18AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Roman Arutyunyan 57 December 13, 2023 09:08AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Sergey Kandaurov 68 December 13, 2023 10:52AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Sergey Kandaurov 52 December 27, 2023 09:36AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Roman Arutyunyan 49 January 04, 2024 11:04AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Sergey Kandaurov 40 January 18, 2024 08:44AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Roman Arutyunyan 37 January 18, 2024 10:08AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Sergey Kandaurov 37 January 18, 2024 11:16AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Maxim Dounin 35 January 18, 2024 02:46PM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Sergey Kandaurov 37 January 19, 2024 06:44AM

Re: [PATCH 1 of 3] Stream: socket peek in preread phase

Maxim Dounin 49 January 19, 2024 03:28PM

[PATCH 2 of 3] Stream: virtual servers

Roman Arutyunyan 90 November 10, 2023 05:08AM

Re: [PATCH 2 of 3] Stream: virtual servers

Sergey Kandaurov 57 December 13, 2023 08:42AM

Re: [PATCH 2 of 3] Stream: virtual servers

Sergey Kandaurov 61 December 14, 2023 12:36PM

[PATCH 0 of 6] Re: [PATCH 2 of 3] Stream: virtual servers

Sergey Kandaurov 62 December 15, 2023 10:40AM

[PATCH 1 of 6] Stream: using ngx_stream_ssl_srv_conf_t *sscf naming convention

Sergey Kandaurov 66 December 15, 2023 10:40AM

Re: [PATCH 1 of 6] Stream: using ngx_stream_ssl_srv_conf_t *sscf naming convention

Roman Arutyunyan 46 January 08, 2024 08:32AM

[PATCH 2 of 6] Overhauled some diagnostic messages missed in 1b05b9bbcebf

Sergey Kandaurov 58 December 15, 2023 10:40AM

Re: [PATCH 2 of 6] Overhauled some diagnostic messages missed in 1b05b9bbcebf

Roman Arutyunyan 43 January 09, 2024 08:16AM

[PATCH 3 of 6] Stream: reshuffled ngx_stream_listen_opt_t fields

Sergey Kandaurov 70 December 15, 2023 10:40AM

Re: [PATCH 3 of 6] Stream: reshuffled ngx_stream_listen_opt_t fields

Roman Arutyunyan 42 January 09, 2024 08:18AM

[PATCH 4 of 6] Stream: the "deferred" parameter of the "listen" directive

Sergey Kandaurov 57 December 15, 2023 10:40AM

Re: [PATCH 4 of 6] Stream: the "deferred" parameter of the "listen" directive

Roman Arutyunyan 46 January 09, 2024 10:40AM

Re: [PATCH 4 of 6] Stream: the "deferred" parameter of the "listen" directive

Sergey Kandaurov 34 January 18, 2024 09:52AM

Re: [PATCH 4 of 6] Stream: the "deferred" parameter of the "listen" directive

Roman Arutyunyan 38 January 18, 2024 10:26AM

Re: [PATCH 4 of 6] Stream: the "deferred" parameter of the "listen" directive

Sergey Kandaurov 41 January 18, 2024 11:22AM

[PATCH 5 of 6] Stream: the "accept_filter" parameter of the "listen" directive

Sergey Kandaurov 70 December 15, 2023 10:40AM

Re: [PATCH 5 of 6] Stream: the "accept_filter" parameter of the "listen" directive

Roman Arutyunyan 38 January 11, 2024 07:42AM

[PATCH 6 of 6] Stream: the "setfib" parameter of the "listen" directive

Sergey Kandaurov 59 December 15, 2023 10:42AM

Re: [PATCH 6 of 6] Stream: the "setfib" parameter of the "listen" directive

Roman Arutyunyan 50 January 11, 2024 07:46AM

[PATCH 3 of 3] Stream: ngx_stream_pass_module

Roman Arutyunyan 82 November 10, 2023 05:08AM

Re: [PATCH 3 of 3] Stream: ngx_stream_pass_module

Sergey Kandaurov 56 December 13, 2023 10:46AM

Re: [PATCH 3 of 3] Stream: ngx_stream_pass_module

Sergey Kandaurov 76 December 15, 2023 10:48AM

Re: [PATCH 3 of 3] Stream: ngx_stream_pass_module

Sergey Kandaurov 37 February 13, 2024 05:48AM

Re: [PATCH 3 of 3] Stream: ngx_stream_pass_module

Roman Arutyunyan 40 February 21, 2024 08:38AM

Re: [PATCH 3 of 3] Stream: ngx_stream_pass_module

Sergey Kandaurov 30 February 28, 2024 05:18AM

Re: [PATCH 3 of 3] Stream: ngx_stream_pass_module

Roman Arutyunyan 32 February 28, 2024 09:24AM

Re: [PATCH 3 of 3] Stream: ngx_stream_pass_module

Sergey Kandaurov 45 February 29, 2024 06:40AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 119
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready