Stream: virtual servers.

Server name is taken either from ngx_stream_ssl_module or
ngx_stream_ssl_preread_module.

The change adds "default_server" parameter to the "listen" directive,
as well as the following directives: "server_names_hash_max_size",
"server_names_hash_bucket_size", "server_name" and "ssl_reject_handshake".
This commit is contained in:
Roman Arutyunyan 2023-12-14 21:58:39 +04:00
parent 72e5d6ac19
commit d21675228a
7 changed files with 1453 additions and 391 deletions

View File

@ -16,16 +16,34 @@ static ngx_int_t ngx_stream_init_phases(ngx_conf_t *cf,
ngx_stream_core_main_conf_t *cmcf);
static ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf,
ngx_stream_core_main_conf_t *cmcf);
static ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
ngx_stream_listen_t *listen);
static char *ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
static ngx_int_t ngx_stream_add_addresses(ngx_conf_t *cf,
ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port,
ngx_stream_listen_opt_t *lsopt);
static ngx_int_t ngx_stream_add_address(ngx_conf_t *cf,
ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port,
ngx_stream_listen_opt_t *lsopt);
static ngx_int_t ngx_stream_add_server(ngx_conf_t *cf,
ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_addr_t *addr);
static ngx_int_t ngx_stream_optimize_servers(ngx_conf_t *cf,
ngx_stream_core_main_conf_t *cmcf, ngx_array_t *ports);
static ngx_int_t ngx_stream_server_names(ngx_conf_t *cf,
ngx_stream_core_main_conf_t *cmcf, ngx_stream_conf_addr_t *addr);
static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
static int ngx_libc_cdecl ngx_stream_cmp_dns_wildcards(const void *one,
const void *two);
static ngx_int_t ngx_stream_init_listening(ngx_conf_t *cf,
ngx_stream_conf_port_t *port);
static ngx_listening_t *ngx_stream_add_listening(ngx_conf_t *cf,
ngx_stream_conf_addr_t *addr);
static ngx_int_t ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
ngx_stream_conf_addr_t *addr);
#if (NGX_HAVE_INET6)
static ngx_int_t ngx_stream_add_addrs6(ngx_conf_t *cf,
ngx_stream_port_t *stport, ngx_stream_conf_addr_t *addr);
#endif
static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
ngx_uint_t ngx_stream_max_module;
@ -74,10 +92,8 @@ static char *
ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
ngx_uint_t i, m, mi, s;
ngx_uint_t mi, m, s;
ngx_conf_t pcf;
ngx_array_t ports;
ngx_stream_listen_t *listen;
ngx_stream_module_t *module;
ngx_stream_conf_ctx_t *ctx;
ngx_stream_core_srv_conf_t **cscfp;
@ -251,21 +267,13 @@ ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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_t *cf,
}
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;
ngx_stream_core_main_conf_t *cmcf;
sa = listen->sockaddr;
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
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)
{
continue;
}
/* a port is already in the port list */
port = &port[i];
goto found;
}
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;
if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
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 (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;
}
/* 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;
}
found:
}
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_uint_t p, a;
ngx_stream_conf_port_t *port;
ngx_stream_conf_addr_t *addr;
ngx_stream_core_srv_conf_t *cscf;
if (ports == NULL) {
return NGX_OK;
}
port = ports->elts;
for (p = 0; p < ports->nelts; p++) {
@ -451,12 +681,254 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
sizeof(ngx_stream_conf_addr_t), ngx_stream_cmp_conf_addrs);
/*
* check whether all name-based servers have the same
* configuration as a default server for given address:port
*/
addr = port[p].addrs.elts;
last = port[p].addrs.nelts;
for (a = 0; a < port[p].addrs.nelts; a++) {
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;
}
}
}
if (ngx_stream_init_listening(cf, &port[p]) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
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)
{
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;
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;
}
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++) {
#if (NGX_PCRE)
if (name[n].regex) {
regex++;
continue;
}
#endif
rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,
NGX_HASH_WILDCARD_KEY);
if (rc == NGX_ERROR) {
goto failed;
}
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;
}
}
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)
{
ngx_stream_conf_addr_t *first, *second;
first = (ngx_stream_conf_addr_t *) one;
second = (ngx_stream_conf_addr_t *) two;
if (first->opt.wildcard) {
/* a wildcard address must be the last resort, shift it to the end */
return 1;
}
if (second->opt.wildcard) {
/* a wildcard address must be the last resort, shift it to the end */
return -1;
}
if (first->opt.bind && !second->opt.bind) {
/* shift explicit bind()ed addresses to the start */
return -1;
}
if (!first->opt.bind && second->opt.bind) {
/* shift explicit bind()ed addresses to the start */
return 1;
}
/* do not sort by default */
return 0;
}
static int ngx_libc_cdecl
ngx_stream_cmp_dns_wildcards(const void *one, const void *two)
{
ngx_hash_key_t *first, *second;
first = (ngx_hash_key_t *) one;
second = (ngx_hash_key_t *) two;
return ngx_dns_strcmp(first->key.data, second->key.data);
}
static ngx_int_t
ngx_stream_init_listening(ngx_conf_t *cf, ngx_stream_conf_port_t *port)
{
ngx_uint_t i, last, bind_wildcard;
ngx_listening_t *ls;
ngx_stream_port_t *stport;
ngx_stream_conf_addr_t *addr;
addr = port->addrs.elts;
last = port->addrs.nelts;
/*
* if there is the binding to the "*:port" then we need to bind()
* to the "*:port" only and ignore the other bindings
* 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) {
@ -476,51 +948,14 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
continue;
}
ls = ngx_create_listening(cf, addr[i].opt.sockaddr,
addr[i].opt.socklen);
ls = ngx_stream_add_listening(cf, &addr[i]);
if (ls == NULL) {
return NGX_CONF_ERROR;
return NGX_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));
stport = ngx_pcalloc(cf->pool, sizeof(ngx_stream_port_t));
if (stport == NULL) {
return NGX_CONF_ERROR;
return NGX_ERROR;
}
ls->servers = stport;
@ -528,16 +963,17 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
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;
return NGX_ERROR;
}
break;
#endif
default: /* AF_INET */
if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {
return NGX_CONF_ERROR;
return NGX_ERROR;
}
break;
}
@ -545,9 +981,61 @@ ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
addr++;
last--;
}
return NGX_OK;
}
return NGX_CONF_OK;
static ngx_listening_t *
ngx_stream_add_listening(ngx_conf_t *cf, ngx_stream_conf_addr_t *addr)
{
ngx_listening_t *ls;
ngx_stream_core_srv_conf_t *cscf;
ls = ngx_create_listening(cf, addr->opt.sockaddr, addr->opt.socklen);
if (ls == NULL) {
return NULL;
}
ls->addr_ntop = 1;
ls->handler = ngx_stream_init_connection;
ls->pool_size = 256;
cscf = addr->default_server;
ls->logp = cscf->error_log;
ls->log.data = &ls->addr_text;
ls->log.handler = ngx_accept_log_error;
ls->type = addr->opt.type;
ls->backlog = addr->opt.backlog;
ls->rcvbuf = addr->opt.rcvbuf;
ls->sndbuf = addr->opt.sndbuf;
ls->keepalive = addr->opt.so_keepalive;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
ls->keepidle = addr->opt.tcp_keepidle;
ls->keepintvl = addr->opt.tcp_keepintvl;
ls->keepcnt = addr->opt.tcp_keepcnt;
#endif
#if (NGX_HAVE_INET6)
ls->ipv6only = addr->opt.ipv6only;
#endif
#if (NGX_HAVE_TCP_FASTOPEN)
ls->fastopen = addr->opt.fastopen;
#endif
#if (NGX_HAVE_REUSEPORT)
ls->reuseport = addr->opt.reuseport;
#endif
ls->wildcard = addr->opt.wildcard;
return ls;
}
@ -558,6 +1046,7 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
ngx_uint_t i;
struct sockaddr_in *sin;
ngx_stream_in_addr_t *addrs;
ngx_stream_virtual_names_t *vn;
stport->addrs = ngx_pcalloc(cf->pool,
stport->naddrs * sizeof(ngx_stream_in_addr_t));
@ -571,13 +1060,39 @@ ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
addrs[i].addr = sin->sin_addr.s_addr;
addrs[i].conf.ctx = addr[i].opt.ctx;
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;
addrs[i].conf.addr_text = addr[i].opt.addr_text;
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;
@ -593,6 +1108,7 @@ ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
ngx_uint_t i;
struct sockaddr_in6 *sin6;
ngx_stream_in6_addr_t *addrs6;
ngx_stream_virtual_names_t *vn;
stport->addrs = ngx_pcalloc(cf->pool,
stport->naddrs * sizeof(ngx_stream_in6_addr_t));
@ -606,50 +1122,42 @@ ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
addrs6[i].addr6 = sin6->sin6_addr;
addrs6[i].conf.ctx = addr[i].opt.ctx;
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;
addrs6[i].conf.addr_text = addr[i].opt.addr_text;
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
static ngx_int_t
ngx_stream_cmp_conf_addrs(const void *one, const void *two)
{
ngx_stream_conf_addr_t *first, *second;
first = (ngx_stream_conf_addr_t *) one;
second = (ngx_stream_conf_addr_t *) two;
if (first->opt.wildcard) {
/* a wildcard 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 */
return -1;
}
if (first->opt.bind && !second->opt.bind) {
/* shift explicit bind()ed addresses to the start */
return -1;
}
if (!first->opt.bind && second->opt.bind) {
/* shift explicit bind()ed addresses to the start */
return 1;
}
/* do not sort by default */
return 0;
}

View File

@ -45,9 +45,8 @@ typedef struct {
socklen_t socklen;
ngx_str_t addr_text;
/* server ctx */
ngx_stream_conf_ctx_t *ctx;
unsigned set:1;
unsigned default_server:1;
unsigned bind:1;
unsigned wildcard:1;
unsigned ssl:1;
@ -69,50 +68,7 @@ typedef struct {
int fastopen;
#endif
int type;
} ngx_stream_listen_t;
typedef struct {
ngx_stream_conf_ctx_t *ctx;
ngx_str_t addr_text;
unsigned ssl:1;
unsigned proxy_protocol:1;
} ngx_stream_addr_conf_t;
typedef struct {
in_addr_t addr;
ngx_stream_addr_conf_t conf;
} ngx_stream_in_addr_t;
#if (NGX_HAVE_INET6)
typedef struct {
struct in6_addr addr6;
ngx_stream_addr_conf_t conf;
} ngx_stream_in6_addr_t;
#endif
typedef struct {
/* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
void *addrs;
ngx_uint_t naddrs;
} ngx_stream_port_t;
typedef struct {
int family;
int type;
in_port_t port;
ngx_array_t addrs; /* array of ngx_stream_conf_addr_t */
} ngx_stream_conf_port_t;
typedef struct {
ngx_stream_listen_t opt;
} ngx_stream_conf_addr_t;
} ngx_stream_listen_opt_t;
typedef enum {
@ -153,7 +109,6 @@ typedef struct {
typedef struct {
ngx_array_t servers; /* ngx_stream_core_srv_conf_t */
ngx_array_t listen; /* ngx_stream_listen_t */
ngx_stream_phase_engine_t phase_engine;
@ -163,16 +118,24 @@ typedef struct {
ngx_array_t prefix_variables; /* ngx_stream_variable_t */
ngx_uint_t ncaptures;
ngx_uint_t server_names_hash_max_size;
ngx_uint_t server_names_hash_bucket_size;
ngx_uint_t variables_hash_max_size;
ngx_uint_t variables_hash_bucket_size;
ngx_hash_keys_arrays_t *variables_keys;
ngx_array_t *ports;
ngx_stream_phase_t phases[NGX_STREAM_LOG_PHASE + 1];
} ngx_stream_core_main_conf_t;
typedef struct {
/* array of the ngx_stream_server_name_t, "server_name" directive */
ngx_array_t server_names;
ngx_stream_content_handler_pt handler;
ngx_stream_conf_ctx_t *ctx;
@ -180,6 +143,8 @@ typedef struct {
u_char *file_name;
ngx_uint_t line;
ngx_str_t server_name;
ngx_flag_t tcp_nodelay;
size_t preread_buffer_size;
ngx_msec_t preread_timeout;
@ -191,10 +156,98 @@ typedef struct {
ngx_msec_t proxy_protocol_timeout;
ngx_uint_t listen; /* unsigned listen:1; */
unsigned listen:1;
#if (NGX_PCRE)
unsigned captures:1;
#endif
} ngx_stream_core_srv_conf_t;
/* list of structures to find core_srv_conf quickly at run time */
typedef struct {
#if (NGX_PCRE)
ngx_stream_regex_t *regex;
#endif
ngx_stream_core_srv_conf_t *server; /* virtual name server conf */
ngx_str_t name;
} ngx_stream_server_name_t;
typedef struct {
ngx_hash_combined_t names;
ngx_uint_t nregex;
ngx_stream_server_name_t *regex;
} ngx_stream_virtual_names_t;
typedef struct {
/* the default server configuration for this address:port */
ngx_stream_core_srv_conf_t *default_server;
ngx_stream_virtual_names_t *virtual_names;
unsigned ssl:1;
unsigned proxy_protocol:1;
} ngx_stream_addr_conf_t;
typedef struct {
in_addr_t addr;
ngx_stream_addr_conf_t conf;
} ngx_stream_in_addr_t;
#if (NGX_HAVE_INET6)
typedef struct {
struct in6_addr addr6;
ngx_stream_addr_conf_t conf;
} ngx_stream_in6_addr_t;
#endif
typedef struct {
/* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
void *addrs;
ngx_uint_t naddrs;
} ngx_stream_port_t;
typedef struct {
int family;
int type;
in_port_t port;
ngx_array_t addrs; /* array of ngx_stream_conf_addr_t */
} ngx_stream_conf_port_t;
typedef struct {
ngx_stream_listen_opt_t opt;
unsigned protocols:3;
unsigned protocols_set:1;
unsigned protocols_changed:1;
ngx_hash_t hash;
ngx_hash_wildcard_t *wc_head;
ngx_hash_wildcard_t *wc_tail;
#if (NGX_PCRE)
ngx_uint_t nregex;
ngx_stream_server_name_t *regex;
#endif
/* the default server configuration for this address:port */
ngx_stream_core_srv_conf_t *default_server;
ngx_array_t servers;
/* array of ngx_stream_core_srv_conf_t */
} ngx_stream_conf_addr_t;
struct ngx_stream_session_s {
uint32_t signature; /* "STRM" */
@ -210,6 +263,8 @@ struct ngx_stream_session_s {
void **main_conf;
void **srv_conf;
ngx_stream_virtual_names_t *virtual_names;
ngx_stream_upstream_t *upstream;
ngx_array_t *upstream_states;
/* of ngx_stream_upstream_state_t */
@ -283,6 +338,9 @@ typedef struct {
#define NGX_STREAM_WRITE_BUFFERED 0x10
ngx_int_t ngx_stream_add_listen(ngx_conf_t *cf,
ngx_stream_core_srv_conf_t *cscf, ngx_stream_listen_opt_t *lsopt);
void ngx_stream_core_run_phases(ngx_stream_session_t *s);
ngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s,
ngx_stream_phase_handler_t *ph);
@ -291,6 +349,10 @@ ngx_int_t ngx_stream_core_preread_phase(ngx_stream_session_t *s,
ngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s,
ngx_stream_phase_handler_t *ph);
ngx_int_t ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool,
ngx_uint_t alloc);
ngx_int_t ngx_stream_find_virtual_server(ngx_stream_session_t *s,
ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp);
void ngx_stream_init_connection(ngx_connection_t *c);
void ngx_stream_session_handler(ngx_event_t *rev);

View File

@ -27,6 +27,8 @@ static char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
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);
@ -47,6 +49,20 @@ static ngx_command_t ngx_stream_core_commands[] = {
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,
@ -61,6 +77,13 @@ static ngx_command_t ngx_stream_core_commands[] = {
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,
@ -441,6 +464,149 @@ ngx_stream_core_content_phase(ngx_stream_session_t *s,
}
ngx_int_t
ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
{
u_char *h, ch;
size_t i, dot_pos, host_len;
enum {
sw_usual = 0,
sw_literal,
sw_rest
} state;
dot_pos = host->len;
host_len = host->len;
h = host->data;
state = sw_usual;
for (i = 0; i < host->len; i++) {
ch = h[i];
switch (ch) {
case '.':
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case ':':
if (state == sw_usual) {
host_len = i;
state = sw_rest;
}
break;
case '[':
if (i == 0) {
state = sw_literal;
}
break;
case ']':
if (state == sw_literal) {
host_len = i + 1;
state = sw_rest;
}
break;
default:
if (ngx_path_separator(ch)) {
return NGX_DECLINED;
}
if (ch <= 0x20 || ch == 0x7f) {
return NGX_DECLINED;
}
if (ch >= 'A' && ch <= 'Z') {
alloc = 1;
}
break;
}
}
if (dot_pos == host_len - 1) {
host_len--;
}
if (host_len == 0) {
return NGX_DECLINED;
}
if (alloc) {
host->data = ngx_pnalloc(pool, host_len);
if (host->data == NULL) {
return NGX_ERROR;
}
ngx_strlow(host->data, h, host_len);
}
host->len = host_len;
return NGX_OK;
}
ngx_int_t
ngx_stream_find_virtual_server(ngx_stream_session_t *s,
ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp)
{
ngx_stream_core_srv_conf_t *cscf;
if (s->virtual_names == NULL) {
return NGX_DECLINED;
}
cscf = ngx_hash_find_combined(&s->virtual_names->names,
ngx_hash_key(host->data, host->len),
host->data, host->len);
if (cscf) {
*cscfp = cscf;
return NGX_OK;
}
#if (NGX_PCRE)
if (host->len && s->virtual_names->nregex) {
ngx_int_t n;
ngx_uint_t i;
ngx_stream_server_name_t *sn;
sn = s->virtual_names->regex;
for (i = 0; i < s->virtual_names->nregex; i++) {
n = ngx_stream_regex_exec(s, sn[i].regex, host);
if (n == NGX_DECLINED) {
continue;
}
if (n == NGX_OK) {
*cscfp = sn[i].server;
return NGX_OK;
}
return NGX_ERROR;
}
}
#endif /* NGX_PCRE */
return NGX_DECLINED;
}
static ngx_int_t
ngx_stream_core_preconfiguration(ngx_conf_t *cf)
{
@ -465,11 +631,8 @@ ngx_stream_core_create_main_conf(ngx_conf_t *cf)
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;
@ -483,6 +646,14 @@ ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf)
{
ngx_stream_core_main_conf_t *cmcf = conf;
ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);
ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,
ngx_cacheline_size);
cmcf->server_names_hash_bucket_size =
ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);
ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);
ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);
@ -514,6 +685,13 @@ ngx_stream_core_create_srv_conf(ngx_conf_t *cf)
* 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;
@ -532,6 +710,9 @@ ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
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);
@ -579,6 +760,37 @@ ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
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;
}
@ -680,9 +892,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *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_uint_t n, i, backlog;
ngx_stream_listen_opt_t lsopt;
cscf->listen = 1;
@ -703,51 +914,48 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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));
ls->backlog = NGX_LISTEN_BACKLOG;
ls->rcvbuf = -1;
ls->sndbuf = -1;
ls->type = SOCK_STREAM;
ls->ctx = cf->ctx;
ngx_memzero(&lsopt, sizeof(ngx_stream_listen_opt_t));
lsopt.backlog = NGX_LISTEN_BACKLOG;
lsopt.type = SOCK_STREAM;
lsopt.rcvbuf = -1;
lsopt.sndbuf = -1;
#if (NGX_HAVE_TCP_FASTOPEN)
ls->fastopen = -1;
lsopt.fastopen = -1;
#endif
#if (NGX_HAVE_INET6)
ls->ipv6only = 1;
lsopt.ipv6only = 1;
#endif
backlog = 0;
for (i = 2; i < cf->args->nelts; i++) {
if (ngx_strcmp(value[i].data, "default_server") == 0) {
lsopt.default_server = 1;
continue;
}
#if !(NGX_WIN32)
if (ngx_strcmp(value[i].data, "udp") == 0) {
ls->type = SOCK_DGRAM;
lsopt.type = SOCK_DGRAM;
continue;
}
#endif
if (ngx_strcmp(value[i].data, "bind") == 0) {
ls->bind = 1;
lsopt.set = 1;
lsopt.bind = 1;
continue;
}
#if (NGX_HAVE_TCP_FASTOPEN)
if (ngx_strncmp(value[i].data, "fastopen=", 9) == 0) {
ls->fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);
ls->bind = 1;
lsopt.fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);
lsopt.set = 1;
lsopt.bind = 1;
if (ls->fastopen == NGX_ERROR) {
if (lsopt.fastopen == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid fastopen \"%V\"", &value[i]);
return NGX_CONF_ERROR;
@ -758,10 +966,11 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) {
ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
ls->bind = 1;
lsopt.backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
lsopt.set = 1;
lsopt.bind = 1;
if (ls->backlog == NGX_ERROR || ls->backlog == 0) {
if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid backlog \"%V\"", &value[i]);
return NGX_CONF_ERROR;
@ -776,10 +985,11 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
size.len = value[i].len - 7;
size.data = value[i].data + 7;
ls->rcvbuf = ngx_parse_size(&size);
ls->bind = 1;
lsopt.rcvbuf = ngx_parse_size(&size);
lsopt.set = 1;
lsopt.bind = 1;
if (ls->rcvbuf == NGX_ERROR) {
if (lsopt.rcvbuf == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid rcvbuf \"%V\"", &value[i]);
return NGX_CONF_ERROR;
@ -792,10 +1002,11 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
size.len = value[i].len - 7;
size.data = value[i].data + 7;
ls->sndbuf = ngx_parse_size(&size);
ls->bind = 1;
lsopt.sndbuf = ngx_parse_size(&size);
lsopt.set = 1;
lsopt.bind = 1;
if (ls->sndbuf == NGX_ERROR) {
if (lsopt.sndbuf == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid sndbuf \"%V\"", &value[i]);
return NGX_CONF_ERROR;
@ -807,10 +1018,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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,
@ -819,7 +1030,9 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
ls->bind = 1;
lsopt.set = 1;
lsopt.bind = 1;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@ -831,8 +1044,9 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strcmp(value[i].data, "reuseport") == 0) {
#if (NGX_HAVE_REUSEPORT)
ls->reuseport = 1;
ls->bind = 1;
lsopt.reuseport = 1;
lsopt.set = 1;
lsopt.bind = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"reuseport is not supported "
@ -843,17 +1057,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strcmp(value[i].data, "ssl") == 0) {
#if (NGX_STREAM_SSL)
ngx_stream_ssl_conf_t *sslcf;
sslcf = ngx_stream_conf_get_module_srv_conf(cf,
ngx_stream_ssl_module);
sslcf->listen = 1;
sslcf->file = cf->conf_file->file.name.data;
sslcf->line = cf->conf_file->line;
ls->ssl = 1;
lsopt.ssl = 1;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@ -866,10 +1070,10 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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 {
@ -888,8 +1092,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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;
}
}
@ -904,8 +1108,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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;
}
}
@ -915,19 +1119,19 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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
@ -939,7 +1143,8 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
}
ls->bind = 1;
lsopt.set = 1;
lsopt.bind = 1;
continue;
@ -954,7 +1159,7 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) {
ls->proxy_protocol = 1;
lsopt.proxy_protocol = 1;
continue;
}
@ -963,27 +1168,27 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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
@ -1000,40 +1205,12 @@ ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
}
if (n != 0) {
nls = ngx_array_push(&cmcf->listen);
if (nls == NULL) {
return NGX_CONF_ERROR;
}
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);
*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);
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;
}
@ -1045,6 +1222,107 @@ 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)
{
ngx_stream_core_srv_conf_t *cscf = conf;
u_char ch;
ngx_str_t *value;
ngx_uint_t i;
ngx_stream_server_name_t *sn;
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
ch = value[i].data[0];
if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))
|| (ch == '.' && value[i].len < 2))
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"server name \"%V\" is invalid", &value[i]);
return NGX_CONF_ERROR;
}
if (ngx_strchr(value[i].data, '/')) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"server name \"%V\" has suspicious symbols",
&value[i]);
}
sn = ngx_array_push(&cscf->server_names);
if (sn == NULL) {
return NGX_CONF_ERROR;
}
#if (NGX_PCRE)
sn->regex = NULL;
#endif
sn->server = cscf;
if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {
sn->name = cf->cycle->hostname;
} else {
sn->name = value[i];
}
if (value[i].data[0] != '~') {
ngx_strlow(sn->name.data, sn->name.data, sn->name.len);
continue;
}
#if (NGX_PCRE)
{
u_char *p;
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
if (value[i].len == 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"empty regex in server name \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
value[i].len--;
value[i].data++;
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = value[i];
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
for (p = value[i].data; p < value[i].data + value[i].len; p++) {
if (*p >= 'A' && *p <= 'Z') {
rc.options = NGX_REGEX_CASELESS;
break;
}
}
sn->regex = ngx_stream_regex_compile(cf, &rc);
if (sn->regex == NULL) {
return NGX_CONF_ERROR;
}
sn->name = value[i];
cscf->captures = (rc.captures > 0);
}
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"using regex \"%V\" "
"requires PCRE library", &value[i]);
return NGX_CONF_ERROR;
#endif
}
return NGX_CONF_OK;
}
static char *
ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{

View File

@ -30,6 +30,7 @@ ngx_stream_init_connection(ngx_connection_t *c)
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_connection_t *c)
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_connection_t *c)
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;

View File

@ -219,6 +219,13 @@ static ngx_command_t ngx_stream_ssl_commands[] = {
offsetof(ngx_stream_ssl_conf_t, conf_commands),
&ngx_stream_ssl_conf_command_post },
{ ngx_string("ssl_reject_handshake"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_stream_ssl_conf_t, reject_handshake),
NULL },
{ ngx_string("ssl_alpn"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
ngx_stream_ssl_alpn,
@ -458,7 +465,112 @@ ngx_stream_ssl_handshake_handler(ngx_connection_t *c)
static int
ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
{
ngx_int_t rc;
ngx_str_t host;
const char *servername;
ngx_connection_t *c;
ngx_stream_session_t *s;
ngx_stream_ssl_conf_t *sscf;
ngx_stream_core_srv_conf_t *cscf;
c = ngx_ssl_get_connection(ssl_conn);
if (c->ssl->handshaked) {
*ad = SSL_AD_NO_RENEGOTIATION;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
s = c->data;
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
if (servername == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: null");
goto done;
}
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL server name: \"%s\"", servername);
host.len = ngx_strlen(servername);
if (host.len == 0) {
goto done;
}
host.data = (u_char *) servername;
rc = ngx_stream_validate_host(&host, c->pool, 1);
if (rc == NGX_ERROR) {
goto error;
}
if (rc == NGX_DECLINED) {
goto done;
}
rc = ngx_stream_find_virtual_server(s, &host, &cscf);
if (rc == NGX_ERROR) {
goto error;
}
if (rc == NGX_DECLINED) {
goto done;
}
s->srv_conf = cscf->ctx->srv_conf;
ngx_set_connection_log(c, cscf->error_log);
sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
if (sscf->ssl.ctx) {
if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
goto error;
}
/*
* SSL_set_SSL_CTX() only changes certs as of 1.0.0d
* adjust other things we care about
*/
SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),
SSL_CTX_get_verify_callback(sscf->ssl.ctx));
SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));
#if OPENSSL_VERSION_NUMBER >= 0x009080dfL
/* only in 0.9.8m+ */
SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &
~SSL_CTX_get_options(sscf->ssl.ctx));
#endif
SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));
#ifdef SSL_OP_NO_RENEGOTIATION
SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION);
#endif
}
done:
sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
if (sscf->reject_handshake) {
c->ssl->handshake_rejected = 1;
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
return SSL_TLSEXT_ERR_OK;
error:
*ad = SSL_AD_INTERNAL_ERROR;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
#endif
@ -655,7 +767,6 @@ ngx_stream_ssl_create_conf(ngx_conf_t *cf)
/*
* set by ngx_pcalloc():
*
* scf->listen = 0;
* scf->protocols = 0;
* scf->certificate_values = NULL;
* scf->dhparam = { 0, NULL };
@ -674,6 +785,7 @@ ngx_stream_ssl_create_conf(ngx_conf_t *cf)
scf->passwords = NGX_CONF_UNSET_PTR;
scf->conf_commands = NGX_CONF_UNSET_PTR;
scf->prefer_server_ciphers = NGX_CONF_UNSET;
scf->reject_handshake = NGX_CONF_UNSET;
scf->verify = NGX_CONF_UNSET_UINT;
scf->verify_depth = NGX_CONF_UNSET_UINT;
scf->builtin_session_cache = NGX_CONF_UNSET;
@ -702,6 +814,8 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->prefer_server_ciphers,
prev->prefer_server_ciphers, 0);
ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
(NGX_CONF_BITMASK_SET
|NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
@ -735,37 +849,23 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
conf->ssl.log = cf->log;
if (!conf->listen) {
return NGX_CONF_OK;
}
if (conf->certificates) {
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->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->nelts < conf->certificates->nelts) {
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\" and "
"the \"listen ... ssl\" directive in %s:%ui",
"for certificate \"%V\"",
((ngx_str_t *) conf->certificates->elts)
+ conf->certificates->nelts - 1,
conf->file, conf->line);
+ conf->certificates->nelts - 1);
return NGX_CONF_ERROR;
}
} else if (!conf->reject_handshake) {
return NGX_CONF_OK;
}
if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
return NGX_CONF_ERROR;
}
@ -818,7 +918,7 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
#endif
} else {
} else if (conf->certificates) {
/* configure certificates */
@ -917,6 +1017,10 @@ ngx_stream_ssl_compile_certificates(ngx_conf_t *cf,
ngx_stream_complex_value_t *cv;
ngx_stream_compile_complex_value_t ccv;
if (conf->certificates == NULL) {
return NGX_OK;
}
cert = conf->certificates->elts;
key = conf->certificate_keys->elts;
nelts = conf->certificates->nelts;
@ -1195,7 +1299,12 @@ ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
static ngx_int_t
ngx_stream_ssl_init(ngx_conf_t *cf)
{
ngx_uint_t a, p, s;
ngx_stream_handler_pt *h;
ngx_stream_ssl_conf_t *sscf;
ngx_stream_conf_addr_t *addr;
ngx_stream_conf_port_t *port;
ngx_stream_core_srv_conf_t **cscfp, *cscf;
ngx_stream_core_main_conf_t *cmcf;
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
@ -1207,5 +1316,58 @@ ngx_stream_ssl_init(ngx_conf_t *cf)
*h = ngx_stream_ssl_handler;
if (cmcf->ports == NULL) {
return NGX_OK;
}
port = cmcf->ports->elts;
for (p = 0; p < cmcf->ports->nelts; p++) {
addr = port[p].addrs.elts;
for (a = 0; a < port[p].addrs.nelts; a++) {
if (!addr[a].opt.ssl) {
continue;
}
cscf = addr[a].default_server;
sscf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
if (sscf->certificates) {
continue;
}
if (!sscf->reject_handshake) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate\" is defined for "
"the \"listen ... ssl\" directive in %s:%ui",
cscf->file_name, cscf->line);
return NGX_ERROR;
}
/*
* if no certificates are defined in the default server,
* check all non-default server blocks
*/
cscfp = addr[a].servers.elts;
for (s = 0; s < addr[a].servers.nelts; s++) {
cscf = cscfp[s];
sscf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
if (sscf->certificates || sscf->reject_handshake) {
continue;
}
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate\" is defined for "
"the \"listen ... ssl\" directive in %s:%ui",
cscf->file_name, cscf->line);
return NGX_ERROR;
}
}
}
return NGX_OK;
}

View File

@ -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;

View File

@ -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_stream_session_t *s)
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_stream_ssl_preread_ctx_t *ctx,
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;
@ -496,6 +499,54 @@ ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx,
}
static ngx_int_t
ngx_stream_ssl_preread_servername(ngx_stream_session_t *s,
ngx_str_t *servername)
{
ngx_int_t rc;
ngx_str_t host;
ngx_connection_t *c;
ngx_stream_core_srv_conf_t *cscf;
c = s->connection;
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
"SSL preread server name: \"%V\"", servername);
if (servername->len == 0) {
return NGX_OK;
}
host = *servername;
rc = ngx_stream_validate_host(&host, c->pool, 1);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_DECLINED) {
return NGX_OK;
}
rc = ngx_stream_find_virtual_server(s, &host, &cscf);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_DECLINED) {
return NGX_OK;
}
s->srv_conf = cscf->ctx->srv_conf;
ngx_set_connection_log(c, cscf->error_log);
return NGX_OK;
}
static ngx_int_t
ngx_stream_ssl_preread_protocol_variable(ngx_stream_session_t *s,
ngx_variable_value_t *v, uintptr_t data)