DTLS patches
Vladimir Homutov
vl at nginx.com
Wed Feb 21 14:12:33 UTC 2018
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.
-------------- next part --------------
# HG changeset patch
# User Vladimir Homutov <vl at 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;
}
More information about the nginx
mailing list