On Wed, Feb 21, 2018 at 08:47:37AM -0500, shankerwangmiao wrote: > > I have tested this patch in my environment. Before the patch is applied, > `tcp_nodelay off` needs to be placed in every `server` clause with DTLS > enabled to work the problem around. >
Hello, can you please elaborate about your environment? Do you proxy DTLS stream directly to backend, or you perform DTLS offload ? What protocol are you using and which server/client software before/behind nginx? I'm attaching refreshed patch against nginx-1.13.9 for those who are interested to test.
# HG changeset patch # User Vladimir Homutov <v...@nginx.com> # Date 1519222093 -10800 # Wed Feb 21 17:08:13 2018 +0300 # Node ID b4b14f20123598d6c4bdff01e3c421e4f180f526 # Parent 88aad69eccef0422719698b54c82e3a020c0fe93 Stream: experimental DTLS support. With the patch, the "listen" directive in the "stream" block now accepts both "udp" and "ssl" directives. The "ssl_protocols" and "proxy_ssl_protocols" directives now accepts "DTLSv1" and "DTLSv1.2" parameters that enable support of corresponding protocols. DTLS termination: stream { # please enable debug log error_log logs/error.log debug; server { # add 'udp' and 'ssl' simultaneously to the listen directive listen 127.0.0.1:4443 udp ssl; # enable DTLSv1 or DTLSv1.2 or both protocols ssl_protocols DTLSv1; # setup other SSL options as usually ssl_certificate ...; ssl_certificate_key ...; proxy_pass ...; } } DTLS to backends: stream { # please enable debug log error_log logs/error.log debug; server { listen 127.0.0.1:5555 udp; # enable SSL to proxy proxy_ssl on; # enable DTLSv1 or DTLSv1.2 or both protocols proxy_ssl_protocols DTLSv1; # setup other proxy SSL options as usually proxy_ssl_certificate ...; proxy_ssl_certificate_key ...; # the backend is a DTLS server proxy_pass 127.0.0.1:4433; } diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf --- a/auto/lib/openssl/conf +++ b/auto/lib/openssl/conf @@ -132,4 +132,16 @@ END exit 1 fi + ngx_feature="OpenSSL DTLS support" + ngx_feature_name="NGX_OPENSSL_DTLS" + ngx_feature_run=no + ngx_feature_incs="#include <openssl/ssl.h>" + ngx_feature_path= + ngx_feature_libs="-lssl -lcrypto $NGX_LIBDL" + ngx_feature_test="DTLSv1_listen(NULL, NULL)" + . auto/feature + + if [ $ngx_found = yes ]; then + have=NGX_SSL_DTLS . auto/have + fi fi diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -507,6 +507,7 @@ void ngx_event_accept(ngx_event_t *ev); #if !(NGX_WIN32) void ngx_event_recvmsg(ngx_event_t *ev); #endif +ngx_int_t ngx_event_udp_accept(ngx_connection_t *c); ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle); u_char *ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len); diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -644,6 +644,81 @@ ngx_event_recvmsg(ngx_event_t *ev) ngx_int_t +ngx_event_udp_accept(ngx_connection_t *c) +{ + int on, rc; + ngx_socket_t fd; + + fd = ngx_socket(c->listening->sockaddr->sa_family, SOCK_DGRAM, 0); + if (fd == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, + ngx_socket_n " failed"); + return NGX_ERROR; + } + + if (ngx_nonblocking(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + goto failed; + } + + on = 1; + rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(int)); + if (rc == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, + "setsockopt(SO_REUSEADDR, 1) failed"); + goto failed; + } + +#if (NGX_HAVE_REUSEPORT && NGX_FREEBSD) + on = 1; + rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &on, sizeof(int)); + if (rc == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, + "setsockopt(SO_REUSEPORT, 1) failed"); + goto failed; + } +#endif + + rc = bind(fd, c->listening->sockaddr, c->listening->socklen); + if (-1 == rc) { + ngx_log_error(NGX_LOG_EMERG, c->log, ngx_socket_errno, + "bind() to %V failed", &c->listening->addr_text); + goto failed; + } + + if (connect(fd, c->sockaddr, c->socklen) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, + "connect() failed"); + goto failed; + } + + c->fd = fd; + c->shared = 0; + c->recv = ngx_udp_recv; + + if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) { + if (ngx_add_conn(c) == NGX_ERROR) { + goto failed; + } + } + + return NGX_OK; + +failed: + + if (ngx_close_socket(fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, c->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + c->fd = (ngx_socket_t) -1; + + return NGX_ERROR; +} + + +ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle) { if (ngx_shmtx_trylock(&ngx_accept_mutex)) { diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -69,6 +69,22 @@ static void *ngx_openssl_create_conf(ngx static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); +#if defined(NGX_HAVE_DTLS) +static int ngx_dtls_client_hmac(SSL *ssl, u_char res[EVP_MAX_MD_SIZE], + unsigned int *rlen); +static int ngx_dtls_generate_cookie_cb(SSL *ssl, unsigned char *cookie, + unsigned int *cookie_len); +static int ngx_dtls_verify_cookie_cb(SSL *ssl, +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const +#endif +unsigned char *cookie, unsigned int cookie_len); +static ngx_int_t ngx_dtls_handshake(ngx_connection_t *c); + + +#define COOKIE_SECRET_LENGTH 32 +static u_char ngx_dtls_cookie_secret[COOKIE_SECRET_LENGTH]; +#endif static ngx_command_t ngx_openssl_commands[] = { @@ -232,13 +248,71 @@ ngx_ssl_init(ngx_log_t *log) ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) { - ssl->ctx = SSL_CTX_new(SSLv23_method()); + if (protocols & NGX_SSL_DTLSv1 || protocols & NGX_SSL_DTLSv1_2) { + +#if defined(NGX_HAVE_DTLS) + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + + if (protocols & NGX_SSL_DTLSv1_2) { + + /* DTLS 1.2 is only supported since 1.0.2 */ + + /* DTLSv1_x_method() functions are deprecated in 1.1.0 */ + +#if OPENSSL_VERSION_NUMBER < 0x10002000L + + /* ancient ... 1.0.2 */ + ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, + "DTLSv1.2 is not supported by " + "the used version of OpenSSL"); + return NGX_ERROR; + +#else + /* 1.0.2 ... 1.1 */ + ssl->ctx = SSL_CTX_new(DTLSv1_2_method()); +#endif + } + + /* note: either 1.2 or 1.1 methods may be initialized, not both, + * preferred is 1.2 if both specified in ssl_protocols + */ + + if (protocols & NGX_SSL_DTLSv1 && ssl->ctx == NULL) { + ssl->ctx = SSL_CTX_new(DTLSv1_method()); + } +#else + ssl->ctx = SSL_CTX_new(DTLS_method()); +#endif + +#else + ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, + "OpenSSL is built without DTLS support"); + return NGX_ERROR; +#endif + + } else { + ssl->ctx = SSL_CTX_new(SSLv23_method()); + } if (ssl->ctx == NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_new() failed"); return NGX_ERROR; } +#if defined(NGX_HAVE_DTLS) + if (protocols & NGX_SSL_DTLSv1 || protocols & NGX_SSL_DTLSv1_2) { + + SSL_CTX_set_cookie_generate_cb(ssl->ctx, ngx_dtls_generate_cookie_cb); + SSL_CTX_set_cookie_verify_cb(ssl->ctx, ngx_dtls_verify_cookie_cb); + + /* TODO: probably this should be rotated regularly */ + if (!RAND_bytes(ngx_dtls_cookie_secret, COOKIE_SECRET_LENGTH)) { + return NGX_ERROR; + } + } +#endif + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_set_ex_data() failed"); @@ -1191,6 +1265,7 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl if (flags & NGX_SSL_CLIENT) { SSL_set_connect_state(sc->connection); + sc->client = 1; } else { SSL_set_accept_state(sc->connection); @@ -1227,6 +1302,19 @@ ngx_ssl_handshake(ngx_connection_t *c) int n, sslerr; ngx_err_t err; +#if defined(NGX_HAVE_DTLS) + ngx_int_t rc; + + if (c->type == SOCK_DGRAM && !c->ssl->client + && !c->ssl->dtls_cookie_accepted) + { + rc = ngx_dtls_handshake(c); + if (rc != NGX_OK) { + return rc; + } + } +#endif + ngx_ssl_clear_error(c->log); n = SSL_do_handshake(c->ssl->connection); @@ -1328,6 +1416,17 @@ ngx_ssl_handshake(ngx_connection_t *c) return NGX_ERROR; } + if (c->ssl->bio_is_mem) { + SSL_set_rfd(c->ssl->connection, c->fd); + c->ssl->bio_is_mem = 0; + + /* buffer is consumed by openssl, we don't want to proxy it */ + c->buffer->pos = c->buffer->last; + + /* continue with handshake with socket */ + return ngx_ssl_handshake(c); + } + return NGX_AGAIN; } @@ -1391,6 +1490,215 @@ ngx_ssl_handshake_handler(ngx_event_t *e } +#if defined(NGX_HAVE_DTLS) + +/* + * RFC 6347, 4.2.1: + * + * When responding to a HelloVerifyRequest, the client MUST use the same + * parameter values (version, random, session_id, cipher_suites, + * compression_method) as it did in the original ClientHello. The + * server SHOULD use those values to generate its cookie and verify that + * they are correct upon cookie receipt. + */ + +static int +ngx_dtls_client_hmac(SSL *ssl, u_char res[EVP_MAX_MD_SIZE], unsigned int *rlen) +{ + u_char *p; + size_t len; + ngx_connection_t *c; + + u_char buffer[64]; + + c = ngx_ssl_get_connection(ssl); + + p = buffer; + + p = ngx_cpymem(p, c->addr_text.data, c->addr_text.len); + p = ngx_sprintf(p, "%d", ngx_inet_get_port(c->sockaddr)); + + len = p - buffer; + + HMAC(EVP_sha1(), (const void*) ngx_dtls_cookie_secret, + COOKIE_SECRET_LENGTH, (const u_char*) buffer, len, res, rlen); + + return NGX_OK; +} + + +static int +ngx_dtls_generate_cookie_cb(SSL *ssl, unsigned char *cookie, + unsigned int *cookie_len) +{ + unsigned int rlen; + u_char res[EVP_MAX_MD_SIZE]; + + if (ngx_dtls_client_hmac(ssl, res, &rlen) != NGX_OK) { + return 0; + } + + ngx_memcpy(cookie, res, rlen); + *cookie_len = rlen; + + return 1; +} + + +static int +ngx_dtls_verify_cookie_cb(SSL *ssl, +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const +#endif + unsigned char *cookie, unsigned int cookie_len) +{ + unsigned int rlen; + u_char res[EVP_MAX_MD_SIZE]; + + if (ngx_dtls_client_hmac(ssl, res, &rlen) != NGX_OK) { + return 0; + } + + if (cookie_len == rlen && ngx_memcmp(res, cookie, rlen) == 0) { + return 1; + } + + return 0; +} + + +static ngx_int_t +ngx_dtls_handshake(ngx_connection_t *c) +{ + int n, rd; + BIO *rbio, *wbio; + ngx_int_t rc; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + BIO_ADDR *peer; +#else + SSL *ssl; + struct sockaddr *peer; +#endif + + wbio = BIO_new(BIO_s_mem()); + if (wbio == NULL) { + ngx_log_error(NGX_LOG_EMERG, c->log, 0, "BIO_new"); + return NGX_ERROR; + } + + rbio = BIO_new_mem_buf(c->buffer->pos, c->buffer->last - c->buffer->pos); + if (rbio == NULL) { + ngx_log_error(NGX_LOG_EMERG, c->log, 0, "BIO_new_mem_buf"); + return NGX_ERROR; + } + + BIO_set_mem_eof_return(rbio, -1); + + SSL_set_bio(c->ssl->connection, rbio, wbio); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + + peer = BIO_ADDR_new(); + + if (peer == NULL) { + ngx_log_error(NGX_LOG_EMERG, c->log, 0, "BIO_ADDR_new"); + return NGX_ERROR; + } + +#else + + peer = ngx_palloc(c->pool, c->socklen); + if (peer == NULL) { + return NGX_ERROR; + } + + ssl = c->ssl->connection; + SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE); + +#endif + + rc = DTLSv1_listen(c->ssl->connection, peer); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + BIO_ADDR_free(peer); +#endif + + if (rc < 0) { + ngx_ssl_error(NGX_LOG_EMERG, c->log, 0, + "DTLSv1_listen error %d", rc); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + return NGX_ERROR; +#else + /* no way to distinguish SSL error from NBIO */ + if (ERR_peek_last_error() != 0) { + return NGX_ERROR; + } + + /* assume -1 comes from NBIO and act accordingly */ + rc = 0; +#endif + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "DTLSv1_listen: %i", rc); + + if (rc == 0) { + /* non-blocking IO: need to send hello-verify request */ + n = BIO_ctrl_pending(wbio); + if (n > 0) { + /* openssl provided some data to send */ + rd = BIO_read(wbio, c->buffer->start, n); + if (rd != n) { + ngx_log_error(NGX_LOG_EMERG, c->log, 0, "DTLS BIO_read failed"); + return NGX_ERROR; + } + + rc = ngx_udp_send(c, c->buffer->start, n); + if (rc != n) { + return NGX_ERROR; + } + + /* ok, we sent response, session is over, + * waiting for helllo with cookie + */ + + } else { + /* renegotiation or other unexpected result */ + return NGX_ERROR; + } + + /* this session is no longer required, new will be created */ + + return NGX_ABORT; /* drop this session*/ + } + + /* rc >= 1: client with a valid cookie */ + + /* DTLSv1_listen PEEK'ed the data, SSL_accept() needs to read from start */ + if (BIO_reset(rbio) != 1) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "BIO_reset"); + return NGX_ERROR; + } + + if (c->shared) { + if (ngx_event_udp_accept(c) != NGX_OK) { + return NGX_ERROR; + } + } + + /* to be reset by handshake when mem buf content is consumed */ + c->ssl->bio_is_mem = 1; + + /* write BIO is real socket, to start sending server hello */ + SSL_set_wfd(c->ssl->connection, c->fd); + + c->ssl->dtls_cookie_accepted = 1; + + return NGX_OK; +} + +#endif + ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -50,6 +50,9 @@ #endif +#if !defined(OPENSSL_NO_DTLS) && OPENSSL_VERSION_NUMBER >= 0x009080dfL +#define NGX_HAVE_DTLS +#endif #define ngx_ssl_session_t SSL_SESSION #define ngx_ssl_conn_t SSL @@ -86,6 +89,9 @@ struct ngx_ssl_connection_s { unsigned no_wait_shutdown:1; unsigned no_send_shutdown:1; unsigned handshake_buffer_set:1; + unsigned dtls_cookie_accepted:1; + unsigned bio_is_mem:1; + unsigned client:1; }; @@ -138,6 +144,8 @@ typedef struct { #define NGX_SSL_TLSv1_1 0x0010 #define NGX_SSL_TLSv1_2 0x0020 #define NGX_SSL_TLSv1_3 0x0040 +#define NGX_SSL_DTLSv1 0x0080 +#define NGX_SSL_DTLSv1_2 0x0200 #define NGX_SSL_BUFFER 1 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 @@ -849,12 +849,6 @@ ngx_stream_core_listen(ngx_conf_t *cf, n return "\"backlog\" parameter is incompatible with \"udp\""; } -#if (NGX_STREAM_SSL) - if (ls->ssl) { - return "\"ssl\" parameter is incompatible with \"udp\""; - } -#endif - if (ls->so_keepalive) { return "\"so_keepalive\" parameter is incompatible with \"udp\""; } diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -27,6 +27,7 @@ typedef struct { size_t upload_rate; size_t download_rate; ngx_uint_t responses; + ngx_uint_t requests; ngx_uint_t next_upstream_tries; ngx_flag_t next_upstream; ngx_flag_t proxy_protocol; @@ -95,6 +96,8 @@ static void ngx_stream_proxy_ssl_handsha static ngx_int_t ngx_stream_proxy_ssl_name(ngx_stream_session_t *s); static ngx_int_t ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf); +static char *ngx_stream_proxy_set_ssl_protocols(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); static ngx_conf_bitmask_t ngx_stream_proxy_ssl_protocols[] = { @@ -104,6 +107,8 @@ static ngx_conf_bitmask_t ngx_stream_pr { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, + { ngx_string("DTLSv1"), NGX_SSL_DTLSv1 }, + { ngx_string("DTLSv1.2"), NGX_SSL_DTLSv1_2 }, { ngx_null_string, 0 } }; @@ -191,6 +196,13 @@ static ngx_command_t ngx_stream_proxy_c offsetof(ngx_stream_proxy_srv_conf_t, responses), NULL }, + { ngx_string("proxy_requests"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, requests), + NULL }, + { ngx_string("proxy_next_upstream"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -237,7 +249,7 @@ static ngx_command_t ngx_stream_proxy_c { ngx_string("proxy_ssl_protocols"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, + ngx_stream_proxy_set_ssl_protocols, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_proxy_srv_conf_t, ssl_protocols), &ngx_stream_proxy_ssl_protocols }, @@ -398,7 +410,9 @@ ngx_stream_proxy_handler(ngx_stream_sess return; } - if (c->type == SOCK_STREAM) { + if (c->type == SOCK_STREAM || (c->type == SOCK_DGRAM && c->ssl) + || (c->shared && pscf->requests)) + { p = ngx_pnalloc(c->pool, pscf->buffer_size); if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); @@ -422,6 +436,13 @@ ngx_stream_proxy_handler(ngx_stream_sess } } + if (c->shared && pscf->requests) { + if (ngx_event_udp_accept(c) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + if (u->resolved == NULL) { uscf = pscf->upstream; @@ -754,14 +775,16 @@ ngx_stream_proxy_init_upstream(ngx_strea #if (NGX_STREAM_SSL) - if (pc->type == SOCK_STREAM && pscf->ssl) { - - if (u->proxy_protocol) { - if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) { - return; + if (pscf->ssl) { + + if (pc->type == SOCK_STREAM) { + if (u->proxy_protocol) { + if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) { + return; + } + + u->proxy_protocol = 0; } - - u->proxy_protocol = 0; } if (pc->ssl == NULL) { @@ -1044,6 +1067,8 @@ static void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc) { long rc; + u_char *p; + ngx_connection_t *c; ngx_stream_session_t *s; ngx_stream_upstream_t *u; ngx_stream_proxy_srv_conf_t *pscf; @@ -1083,6 +1108,29 @@ ngx_stream_proxy_ssl_handshake(ngx_conne ngx_del_timer(pc->write); } + c = s->connection; + + if (c->shared && pscf->requests) { + + if (ngx_event_udp_accept(c) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + p = ngx_pnalloc(c->pool, pscf->buffer_size); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u = s->upstream; + + u->downstream_buf.start = p; + u->downstream_buf.end = p + pscf->buffer_size; + u->downstream_buf.pos = p; + u->downstream_buf.last = p; + } + ngx_stream_proxy_init_upstream(s); return; @@ -1584,8 +1632,11 @@ ngx_stream_proxy_process(ngx_stream_sess } } - if (c->type == SOCK_DGRAM && ++u->responses == pscf->responses) - { + if (c->type == SOCK_DGRAM && from_upstream) { + u->responses++; + } + + if (c->type == SOCK_DGRAM && u->responses == pscf->responses) { src->read->ready = 0; src->read->eof = 1; } @@ -1848,6 +1899,7 @@ ngx_stream_proxy_create_srv_conf(ngx_con conf->upload_rate = NGX_CONF_UNSET_SIZE; conf->download_rate = NGX_CONF_UNSET_SIZE; conf->responses = NGX_CONF_UNSET_UINT; + conf->requests = NGX_CONF_UNSET_UINT; conf->next_upstream_tries = NGX_CONF_UNSET_UINT; conf->next_upstream = NGX_CONF_UNSET; conf->proxy_protocol = NGX_CONF_UNSET; @@ -1893,6 +1945,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf ngx_conf_merge_uint_value(conf->responses, prev->responses, NGX_MAX_INT32_VALUE); + ngx_conf_merge_uint_value(conf->requests, + prev->requests, NGX_MAX_INT32_VALUE); + ngx_conf_merge_uint_value(conf->next_upstream_tries, prev->next_upstream_tries, 0); @@ -2019,6 +2074,34 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, return NGX_OK; } + +static +char *ngx_stream_proxy_set_ssl_protocols(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_stream_proxy_srv_conf_t *pscf = conf; + + char *rv; + + rv = ngx_conf_set_bitmask_slot(cf, cmd, conf); + + if (rv != NGX_CONF_OK) { + return rv; + } + + /* DTLS protocol requires corresponding TLS version to be set */ + + if (pscf->ssl_protocols & NGX_SSL_DTLSv1) { + pscf->ssl_protocols |= NGX_SSL_TLSv1; + } + + if (pscf->ssl_protocols & NGX_SSL_DTLSv1_2) { + pscf->ssl_protocols |= NGX_SSL_TLSv1_2; + } + + return NGX_CONF_OK; +} + #endif 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 @@ -34,6 +34,8 @@ static char *ngx_stream_ssl_merge_conf(n static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_stream_set_ssl_protocols(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf); @@ -46,6 +48,9 @@ static ngx_conf_bitmask_t ngx_stream_ss { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, + { ngx_string("DTLSv1"), NGX_SSL_DTLSv1 }, + { ngx_string("DTLSv1.2"), NGX_SSL_DTLSv1_2 }, + { ngx_null_string, 0 } }; @@ -105,7 +110,7 @@ static ngx_command_t ngx_stream_ssl_com { ngx_string("ssl_protocols"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, - ngx_conf_set_bitmask_slot, + ngx_stream_set_ssl_protocols, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_ssl_conf_t, protocols), &ngx_stream_ssl_protocols }, @@ -365,7 +370,8 @@ ngx_stream_ssl_init_connection(ngx_ssl_t cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); - if (cscf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) { + if (c->type == SOCK_STREAM + && cscf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) { return NGX_ERROR; } @@ -389,6 +395,11 @@ ngx_stream_ssl_init_connection(ngx_ssl_t return NGX_AGAIN; } + if (rc == NGX_ABORT) { + /* DTLS handshake sent the cookie to client */ + return NGX_ERROR; + } + /* rc == NGX_OK */ return NGX_OK; @@ -721,6 +732,33 @@ ngx_stream_ssl_password_file(ngx_conf_t static char * +ngx_stream_set_ssl_protocols(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_ssl_conf_t *scf = conf; + + char *rv; + + rv = ngx_conf_set_bitmask_slot(cf, cmd, conf); + + if (rv != NGX_CONF_OK) { + return rv; + } + + /* DTLS protocol requires corresponding TLS version to be set */ + + if (scf->protocols & NGX_SSL_DTLSv1) { + scf->protocols |= NGX_SSL_TLSv1; + } + + if (scf->protocols & NGX_SSL_DTLSv1_2) { + scf->protocols |= NGX_SSL_TLSv1_2; + } + + return NGX_CONF_OK; +} + + +static char * ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_stream_ssl_conf_t *scf = conf; @@ -835,8 +873,13 @@ invalid: 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 i; + ngx_stream_listen_t *ls; + ngx_stream_handler_pt *h; + ngx_stream_conf_ctx_t *sctx; + ngx_stream_ssl_conf_t **sscfp, *sscf; + 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); @@ -847,5 +890,52 @@ ngx_stream_ssl_init(ngx_conf_t *cf) *h = ngx_stream_ssl_handler; + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + ls = cmcf->listen.elts; + + for (i = 0; i < cmcf->listen.nelts; i++) { + if (ls[i].ssl) { + sctx = ls[i].ctx; + + sscfp = (ngx_stream_ssl_conf_t **)sctx->srv_conf; + cscfp = (ngx_stream_core_srv_conf_t **)sctx->srv_conf; + + sscf = sscfp[ngx_stream_ssl_module.ctx_index]; + cscf = cscfp[ngx_stream_core_module.ctx_index]; + + if (sscf->certificates == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate\" is defined " + "in server listening on SSL port at %s:%ui", + cscf->file_name, cscf->line); + return NGX_ERROR; + } + + if (ls[i].type == SOCK_DGRAM) { + if (!(sscf->protocols & NGX_SSL_DTLSv1 + || sscf->protocols & NGX_SSL_DTLSv1_2)) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_protocols\" does not enable DTLS in a " + "server listening on UDP SSL port at %s:%ui", + cscf->file_name, cscf->line); + return NGX_ERROR; + } + + } else { + if (sscf->protocols & NGX_SSL_DTLSv1 + || sscf->protocols & NGX_SSL_DTLSv1_2 ) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_protocols\" includes DTLS in a server " + "listening on SSL port at %s:%ui", + cscf->file_name, cscf->line); + return NGX_ERROR; + } + } + } + } + return NGX_OK; }
_______________________________________________ nginx mailing list nginx@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx