Added SSL/TLS support on connection level.

This commit is contained in:
Igor Sysoev
2018-09-20 15:05:37 +03:00
parent e964e982fd
commit 96cd68b340
22 changed files with 1104 additions and 778 deletions

View File

@@ -149,7 +149,7 @@ $NXT_BUILD_DIR/utf8_file_name_test: $NXT_LIB_UTF8_FILE_NAME_TEST_SRCS \\
-o $NXT_BUILD_DIR/utf8_file_name_test \\ -o $NXT_BUILD_DIR/utf8_file_name_test \\
$NXT_LIB_UTF8_FILE_NAME_TEST_SRCS \\ $NXT_LIB_UTF8_FILE_NAME_TEST_SRCS \\
$NXT_BUILD_DIR/$NXT_LIB_STATIC \\ $NXT_BUILD_DIR/$NXT_LIB_STATIC \\
$NXT_LD_OPT $NXT_LIBM $NXT_LIBS $NXT_LD_OPT $NXT_LIBM $NXT_LIBS $NXT_LIB_AUX_LIBS
$NXT_BUILD_DIR/unit_app_test: $NXT_BUILD_DIR/src/test/nxt_unit_app_test.o \\ $NXT_BUILD_DIR/unit_app_test: $NXT_BUILD_DIR/src/test/nxt_unit_app_test.o \\
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC

View File

@@ -19,7 +19,7 @@ NXT_UNIX_DOMAIN=YES
NXT_REGEX=NO NXT_REGEX=NO
NXT_PCRE=NO NXT_PCRE=NO
NXT_SSLTLS=NO NXT_TLS=NO
NXT_OPENSSL=NO NXT_OPENSSL=NO
NXT_GNUTLS=NO NXT_GNUTLS=NO
NXT_CYASSL=NO NXT_CYASSL=NO
@@ -72,7 +72,6 @@ do
--pcre) NXT_PCRE=YES ;; --pcre) NXT_PCRE=YES ;;
--ssltls) NXT_SSLTLS=YES ;;
--openssl) NXT_OPENSSL=YES ;; --openssl) NXT_OPENSSL=YES ;;
--gnutls) NXT_GNUTLS=YES ;; --gnutls) NXT_GNUTLS=YES ;;
--cyassl) NXT_CYASSL=YES ;; --cyassl) NXT_CYASSL=YES ;;

View File

@@ -105,8 +105,8 @@ NXT_LIB_SRC0=" \
NXT_LIB_UNIT_SRCS="src/nxt_unit.c" NXT_LIB_UNIT_SRCS="src/nxt_unit.c"
NXT_LIB_SSLTLS_DEPS="src/nxt_ssltls.h" NXT_LIB_TLS_DEPS="src/nxt_tls.h"
NXT_LIB_SSLTLS_SRCS="src/nxt_ssltls.c" NXT_LIB_TLS_SRCS=
NXT_LIB_OPENSSL_SRCS="src/nxt_openssl.c" NXT_LIB_OPENSSL_SRCS="src/nxt_openssl.c"
NXT_LIB_GNUTLS_SRCS="src/nxt_gnutls.c" NXT_LIB_GNUTLS_SRCS="src/nxt_gnutls.c"
NXT_LIB_CYASSL_SRCS="src/nxt_cyassl.c" NXT_LIB_CYASSL_SRCS="src/nxt_cyassl.c"
@@ -157,9 +157,9 @@ NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \
" "
if [ $NXT_SSLTLS = YES ]; then if [ $NXT_TLS = YES ]; then
nxt_have=NXT_SSLTLS . auto/have nxt_have=NXT_TLS . auto/have
NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_SSLTLS_SRCS" NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_TLS_SRCS"
fi fi

View File

@@ -31,7 +31,7 @@ if [ $NXT_OPENSSL = YES ]; then
if [ $nxt_found = yes ]; then if [ $nxt_found = yes ]; then
NXT_SSLTLS=YES NXT_TLS=YES
NXT_OPENSSL_LIBS="$nxt_feature_libs" NXT_OPENSSL_LIBS="$nxt_feature_libs"
nxt_feature="OpenSSL version" nxt_feature="OpenSSL version"
@@ -78,7 +78,7 @@ if [ $NXT_GNUTLS = YES ]; then
if [ $nxt_found = yes ]; then if [ $nxt_found = yes ]; then
NXT_SSLTLS=YES NXT_TLS=YES
$echo " + GnuTLS version: `pkg-config gnutls --modversion`" $echo " + GnuTLS version: `pkg-config gnutls --modversion`"
@@ -138,7 +138,7 @@ if [ $NXT_CYASSL = YES ]; then
if [ $nxt_found = yes ]; then if [ $nxt_found = yes ]; then
NXT_SSLTLS=YES NXT_TLS=YES
NXT_CYASSL_CFLAGS="$nxt_feature_incs" NXT_CYASSL_CFLAGS="$nxt_feature_incs"
NXT_CYASSL_LIBS="$nxt_feature_libs" NXT_CYASSL_LIBS="$nxt_feature_libs"
@@ -171,7 +171,7 @@ if [ $NXT_POLARSSL = YES ]; then
if [ $nxt_found = yes ]; then if [ $nxt_found = yes ]; then
NXT_SSLTLS=YES NXT_TLS=YES
NXT_POLARSSL_CFLAGS="$nxt_feature_incs" NXT_POLARSSL_CFLAGS="$nxt_feature_incs"
NXT_POLARSSL_LIBS="$nxt_feature_libs" NXT_POLARSSL_LIBS="$nxt_feature_libs"

View File

@@ -8,36 +8,34 @@
nxt_conn_io_t nxt_unix_conn_io = { nxt_conn_io_t nxt_unix_conn_io = {
nxt_conn_io_connect, .connect = nxt_conn_io_connect,
nxt_conn_io_accept, .accept = nxt_conn_io_accept,
nxt_conn_io_read, .read = nxt_conn_io_read,
nxt_conn_io_recvbuf, .recvbuf = nxt_conn_io_recvbuf,
nxt_conn_io_recv, .recv = nxt_conn_io_recv,
nxt_conn_io_write, .write = nxt_conn_io_write,
nxt_event_conn_io_write_chunk, .sendbuf = nxt_conn_io_sendbuf,
#if (NXT_HAVE_LINUX_SENDFILE) #if (NXT_HAVE_LINUX_SENDFILE)
nxt_linux_event_conn_io_sendfile, .old_sendbuf = nxt_linux_event_conn_io_sendfile,
#elif (NXT_HAVE_FREEBSD_SENDFILE) #elif (NXT_HAVE_FREEBSD_SENDFILE)
nxt_freebsd_event_conn_io_sendfile, .old_sendbuf = nxt_freebsd_event_conn_io_sendfile,
#elif (NXT_HAVE_MACOSX_SENDFILE) #elif (NXT_HAVE_MACOSX_SENDFILE)
nxt_macosx_event_conn_io_sendfile, .old_sendbuf = nxt_macosx_event_conn_io_sendfile,
#elif (NXT_HAVE_SOLARIS_SENDFILEV) #elif (NXT_HAVE_SOLARIS_SENDFILEV)
nxt_solaris_event_conn_io_sendfilev, .old_sendbuf = nxt_solaris_event_conn_io_sendfilev,
#elif (NXT_HAVE_AIX_SEND_FILE) #elif (NXT_HAVE_AIX_SEND_FILE)
nxt_aix_event_conn_io_send_file, .old_sendbuf = nxt_aix_event_conn_io_send_file,
#elif (NXT_HAVE_HPUX_SENDFILE) #elif (NXT_HAVE_HPUX_SENDFILE)
nxt_hpux_event_conn_io_sendfile, .old_sendbuf = nxt_hpux_event_conn_io_sendfile,
#else #else
nxt_event_conn_io_sendbuf, .old_sendbuf = nxt_event_conn_io_sendbuf,
#endif #endif
nxt_event_conn_io_writev, .writev = nxt_event_conn_io_writev,
nxt_event_conn_io_send, .send = nxt_event_conn_io_send,
nxt_conn_io_shutdown,
}; };
@@ -103,40 +101,6 @@ nxt_conn_free(nxt_task_t *task, nxt_conn_t *c)
} }
void
nxt_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
{
int ret;
nxt_conn_t *c;
static const struct linger linger_off = {
.l_onoff = 1,
.l_linger = 0,
};
c = obj;
nxt_debug(task, "event conn shutdown");
if (c->socket.timedout) {
/*
* Resetting of timed out connection on close
* releases kernel memory associated with socket.
* This also causes sending TCP/IP RST to a peer.
*/
ret = setsockopt(c->socket.fd, SOL_SOCKET, SO_LINGER, &linger_off,
sizeof(struct linger));
if (nxt_slow_path(ret != 0)) {
nxt_alert(task, "setsockopt(%d, SO_LINGER) failed %E",
c->socket.fd, nxt_socket_errno);
}
}
c->write_state->close_handler(task, c, data);
}
void void
nxt_conn_timer(nxt_event_engine_t *engine, nxt_conn_t *c, nxt_conn_timer(nxt_event_engine_t *engine, nxt_conn_t *c,
const nxt_conn_state_t *state, nxt_timer_t *timer) const nxt_conn_state_t *state, nxt_timer_t *timer)

View File

@@ -56,26 +56,20 @@ typedef struct {
ssize_t (*recv)(nxt_conn_t *c, void *buf, ssize_t (*recv)(nxt_conn_t *c, void *buf,
size_t size, nxt_uint_t flags); size_t size, nxt_uint_t flags);
/* /* The write() is an interface to write a buffer chain. */
* The write() is an interface to write a buffer chain with a given rate
* limit. It calls write_chunk() in a loop and handles write event timer.
*/
nxt_work_handler_t write; nxt_work_handler_t write;
/*
* The write_chunk() interface writes a buffer chain with a given limit
* and toggles write event. SSL/TLS libraries' write_chunk() interface
* buffers data and calls the library specific send() interface to write
* the buffered data eventually.
*/
ssize_t (*write_chunk)(nxt_conn_t *c,
nxt_buf_t *b, size_t limit);
/* /*
* The sendbuf() is an interface for OS-specific sendfile * The sendbuf() is an interface for OS-specific sendfile
* implementations or simple writev(). * implementations or simple writev().
*/ */
ssize_t (*sendbuf)(nxt_conn_t *c, nxt_buf_t *b, ssize_t (*sendbuf)(nxt_task_t *task,
nxt_sendbuf_t *sb);
/*
* The sendbuf() is an interface for OS-specific sendfile
* implementations or simple writev().
*/
ssize_t (*old_sendbuf)(nxt_conn_t *c, nxt_buf_t *b,
size_t limit); size_t limit);
/* /*
* The writev() is an interface to write several nxt_iobuf_t buffers. * The writev() is an interface to write several nxt_iobuf_t buffers.
@@ -146,8 +140,8 @@ struct nxt_conn_s {
nxt_conn_io_t *io; nxt_conn_io_t *io;
union { union {
#if (NXT_SSLTLS) #if (NXT_TLS)
void *ssltls; void *tls;
#endif #endif
nxt_thread_pool_t *thread_pool; nxt_thread_pool_t *thread_pool;
} u; } u;
@@ -225,7 +219,6 @@ struct nxt_conn_s {
NXT_EXPORT nxt_conn_t *nxt_conn_create(nxt_mp_t *mp, nxt_task_t *task); NXT_EXPORT nxt_conn_t *nxt_conn_create(nxt_mp_t *mp, nxt_task_t *task);
NXT_EXPORT void nxt_conn_free(nxt_task_t *task, nxt_conn_t *c); NXT_EXPORT void nxt_conn_free(nxt_task_t *task, nxt_conn_t *c);
void nxt_conn_io_shutdown(nxt_task_t *task, void *obj, void *data);
NXT_EXPORT void nxt_conn_close(nxt_event_engine_t *engine, nxt_conn_t *c); NXT_EXPORT void nxt_conn_close(nxt_event_engine_t *engine, nxt_conn_t *c);
NXT_EXPORT void nxt_conn_timer(nxt_event_engine_t *engine, nxt_conn_t *c, NXT_EXPORT void nxt_conn_timer(nxt_event_engine_t *engine, nxt_conn_t *c,
@@ -265,8 +258,6 @@ ssize_t nxt_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb, void *buf,
size_t nxt_event_conn_write_limit(nxt_conn_t *c); size_t nxt_event_conn_write_limit(nxt_conn_t *c);
nxt_bool_t nxt_event_conn_write_delayed(nxt_event_engine_t *engine, nxt_bool_t nxt_event_conn_write_delayed(nxt_event_engine_t *engine,
nxt_conn_t *c, size_t sent); nxt_conn_t *c, size_t sent);
ssize_t nxt_event_conn_io_write_chunk(nxt_conn_t *c, nxt_buf_t *b,
size_t limit);
ssize_t nxt_event_conn_io_writev(nxt_conn_t *c, nxt_iobuf_t *iob, ssize_t nxt_event_conn_io_writev(nxt_conn_t *c, nxt_iobuf_t *iob,
nxt_uint_t niob); nxt_uint_t niob);
ssize_t nxt_event_conn_io_send(nxt_conn_t *c, void *buf, size_t size); ssize_t nxt_event_conn_io_send(nxt_conn_t *c, void *buf, size_t size);

View File

@@ -39,7 +39,6 @@ nxt_conn_io_read(nxt_task_t *task, void *obj, void *data)
{ {
ssize_t n; ssize_t n;
nxt_conn_t *c; nxt_conn_t *c;
nxt_work_queue_t *wq;
nxt_event_engine_t *engine; nxt_event_engine_t *engine;
nxt_work_handler_t handler; nxt_work_handler_t handler;
const nxt_conn_state_t *state; const nxt_conn_state_t *state;
@@ -51,7 +50,13 @@ nxt_conn_io_read(nxt_task_t *task, void *obj, void *data)
engine = task->thread->engine; engine = task->thread->engine;
/*
* Here c->io->read() is assigned instead of direct nxt_conn_io_read()
* because the function can be called by nxt_kqueue_conn_io_read().
*/
c->socket.read_handler = c->io->read;
state = c->read_state; state = c->read_state;
c->socket.error_handler = state->error_handler;
if (c->socket.read_ready) { if (c->socket.read_ready) {
@@ -75,35 +80,39 @@ nxt_conn_io_read(nxt_task_t *task, void *obj, void *data)
nxt_timer_disable(engine, &c->read_timer); nxt_timer_disable(engine, &c->read_timer);
} }
wq = c->read_work_queue; nxt_work_queue_add(c->read_work_queue,
handler = state->ready_handler; state->ready_handler, task, c, data);
nxt_work_queue_add(wq, handler, task, c, data);
return; return;
} }
if (n != NXT_AGAIN) { if (n != NXT_AGAIN) {
/* n == 0 or n == NXT_ERROR. */
handler = (n == 0) ? state->close_handler : state->error_handler;
nxt_fd_event_block_read(engine, &c->socket); nxt_fd_event_block_read(engine, &c->socket);
nxt_timer_disable(engine, &c->read_timer); nxt_timer_disable(engine, &c->read_timer);
wq = &engine->fast_work_queue; nxt_work_queue_add(&engine->fast_work_queue,
handler, task, c, data);
return;
}
handler = (n == 0) ? state->close_handler : state->error_handler; /* n == NXT_AGAIN. */
nxt_work_queue_add(wq, handler, task, c, data); if (c->socket.read_ready) {
/*
* SSL/TLS library can return NXT_AGAIN if renegotiation
* occured during read operation, it toggled write event
* internally so only read timer should be set.
*/
if (c->read_timer.state == NXT_TIMER_DISABLED) {
nxt_conn_timer(engine, c, state, &c->read_timer);
}
return; return;
} }
} }
/*
* Here c->io->read() is assigned instead of direct nxt_conn_io_read()
* because the function can be called by nxt_kqueue_conn_io_read().
*/
c->socket.read_handler = c->io->read;
c->socket.error_handler = state->error_handler;
if (nxt_fd_event_is_disabled(c->socket.read)) { if (nxt_fd_event_is_disabled(c->socket.read)) {
nxt_fd_event_enable_read(engine, &c->socket); nxt_fd_event_enable_read(engine, &c->socket);
} }
@@ -195,7 +204,7 @@ nxt_conn_io_recv(nxt_conn_t *c, void *buf, size_t size, nxt_uint_t flags)
c->socket.fd, buf, size, flags, n); c->socket.fd, buf, size, flags, n);
if (n > 0) { if (n > 0) {
if ((size_t) n < size) { if ((size_t) n < size && (flags & MSG_PEEK) == 0) {
c->socket.read_ready = 0; c->socket.read_ready = 0;
} }
@@ -204,7 +213,10 @@ nxt_conn_io_recv(nxt_conn_t *c, void *buf, size_t size, nxt_uint_t flags)
if (n == 0) { if (n == 0) {
c->socket.closed = 1; c->socket.closed = 1;
c->socket.read_ready = 0;
if ((flags & MSG_PEEK) == 0) {
c->socket.read_ready = 0;
}
return n; return n;
} }

View File

@@ -44,12 +44,15 @@ nxt_conn_io_write(nxt_task_t *task, void *obj, void *data)
sb.sent = 0; sb.sent = 0;
sb.size = 0; sb.size = 0;
sb.buf = b; sb.buf = b;
#if (NXT_TLS)
sb.tls = c->u.tls;
#endif
sb.limit = 10 * 1024 * 1024; sb.limit = 10 * 1024 * 1024;
sb.ready = 1; sb.ready = 1;
sb.sync = 0; sb.sync = 0;
do { do {
ret = nxt_conn_io_sendbuf(task, &sb); ret = c->io->sendbuf(task, &sb);
c->socket.write_ready = sb.ready; c->socket.write_ready = sb.ready;
c->socket.error = sb.error; c->socket.error = sb.error;
@@ -100,7 +103,7 @@ nxt_conn_io_write(nxt_task_t *task, void *obj, void *data)
/* /*
* SSL libraries can require to toggle either write or read * SSL libraries can require to toggle either write or read
* event if renegotiation occurs during SSL write operation. * event if renegotiation occurs during SSL write operation.
* This case is handled on the event_io->send() level. Timer * This case is handled on the c->io->send() level. Timer
* can be set here because it should be set only for write * can be set here because it should be set only for write
* direction. * direction.
*/ */
@@ -301,23 +304,6 @@ nxt_event_conn_write_delayed(nxt_event_engine_t *engine, nxt_conn_t *c,
} }
ssize_t
nxt_event_conn_io_write_chunk(nxt_conn_t *c, nxt_buf_t *b, size_t limit)
{
ssize_t ret;
ret = c->io->sendbuf(c, b, limit);
if ((ret == NXT_AGAIN || !c->socket.write_ready)
&& nxt_fd_event_is_disabled(c->socket.write))
{
nxt_fd_event_enable_write(c->socket.task->thread->engine, &c->socket);
}
return ret;
}
ssize_t ssize_t
nxt_event_conn_io_sendbuf(nxt_conn_t *c, nxt_buf_t *b, size_t limit) nxt_event_conn_io_sendbuf(nxt_conn_t *c, nxt_buf_t *b, size_t limit)
{ {

View File

@@ -97,26 +97,24 @@ static ssize_t nxt_epoll_edge_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
static nxt_conn_io_t nxt_epoll_edge_conn_io = { static nxt_conn_io_t nxt_epoll_edge_conn_io = {
nxt_epoll_edge_conn_io_connect, .connect = nxt_epoll_edge_conn_io_connect,
nxt_conn_io_accept, .accept = nxt_conn_io_accept,
nxt_conn_io_read, .read = nxt_conn_io_read,
nxt_epoll_edge_conn_io_recvbuf, .recvbuf = nxt_epoll_edge_conn_io_recvbuf,
nxt_conn_io_recv, .recv = nxt_conn_io_recv,
nxt_conn_io_write, .write = nxt_conn_io_write,
nxt_event_conn_io_write_chunk, .sendbuf = nxt_conn_io_sendbuf,
#if (NXT_HAVE_LINUX_SENDFILE) #if (NXT_HAVE_LINUX_SENDFILE)
nxt_linux_event_conn_io_sendfile, .old_sendbuf = nxt_linux_event_conn_io_sendfile,
#else #else
nxt_event_conn_io_sendbuf, .old_sendbuf = nxt_event_conn_io_sendbuf,
#endif #endif
nxt_event_conn_io_writev, .writev = nxt_event_conn_io_writev,
nxt_event_conn_io_send, .send = nxt_event_conn_io_send,
nxt_conn_io_shutdown,
}; };

View File

@@ -109,7 +109,7 @@ nxt_event_conn_job_sendfile_handler(nxt_task_t *task, void *obj, void *data)
b = jbs->out; b = jbs->out;
do { do {
ret = c->io->sendbuf(c, b, jbs->limit); ret = c->io->old_sendbuf(c, b, jbs->limit);
if (ret == NXT_AGAIN) { if (ret == NXT_AGAIN) {
break; break;

File diff suppressed because it is too large Load Diff

View File

@@ -111,28 +111,26 @@ static ssize_t nxt_kqueue_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
static nxt_conn_io_t nxt_kqueue_conn_io = { static nxt_conn_io_t nxt_kqueue_conn_io = {
nxt_kqueue_conn_io_connect, .connect = nxt_kqueue_conn_io_connect,
nxt_kqueue_conn_io_accept, .accept = nxt_kqueue_conn_io_accept,
nxt_kqueue_conn_io_read, .read = nxt_kqueue_conn_io_read,
nxt_kqueue_conn_io_recvbuf, .recvbuf = nxt_kqueue_conn_io_recvbuf,
nxt_conn_io_recv, .recv = nxt_conn_io_recv,
nxt_conn_io_write, .write = nxt_conn_io_write,
nxt_event_conn_io_write_chunk, .sendbuf = nxt_conn_io_sendbuf,
#if (NXT_HAVE_FREEBSD_SENDFILE) #if (NXT_HAVE_FREEBSD_SENDFILE)
nxt_freebsd_event_conn_io_sendfile, .old_sendbuf = nxt_freebsd_event_conn_io_sendfile,
#elif (NXT_HAVE_MACOSX_SENDFILE) #elif (NXT_HAVE_MACOSX_SENDFILE)
nxt_macosx_event_conn_io_sendfile, .old_sendbuf = nxt_macosx_event_conn_io_sendfile,
#else #else
nxt_event_conn_io_sendbuf, .old_sendbuf = nxt_event_conn_io_sendbuf,
#endif #endif
nxt_event_conn_io_writev, .writev = nxt_event_conn_io_writev,
nxt_event_conn_io_send, .send = nxt_event_conn_io_send,
nxt_conn_io_shutdown,
}; };

View File

@@ -280,12 +280,12 @@ nxt_listen_socket_pool_min_size(nxt_listen_socket_t *ls)
break; break;
} }
#if (NXT_SSLTLS) #if (NXT_TLS)
if (ls->ssltls) { if (ls->tls) {
size += 4 * sizeof(void *) /* SSL/TLS connection */ size += 4 * sizeof(void *) /* SSL/TLS connection */
+ sizeof(nxt_buf_mem_t) + sizeof(nxt_buf_mem_t)
+ sizeof(nxt_mem_pool_cleanup_t); + sizeof(nxt_work_t); /* nxt_mp_cleanup */
} }
#endif #endif

View File

@@ -23,8 +23,8 @@ typedef struct {
uint8_t flags; uint8_t flags;
uint8_t read_after_accept; /* 1 bit */ uint8_t read_after_accept; /* 1 bit */
#if (NXT_SSLTLS) #if (NXT_TLS)
uint8_t ssltls; /* 1 bit */ uint8_t tls; /* 1 bit */
#endif #endif
#if (NXT_INET6 && defined IPV6_V6ONLY) #if (NXT_INET6 && defined IPV6_V6ONLY)
uint8_t ipv6only; /* 2 bits */ uint8_t ipv6only; /* 2 bits */

View File

@@ -113,8 +113,8 @@ typedef struct nxt_conn_s nxt_conn_t;
#include <nxt_log_moderation.h> #include <nxt_log_moderation.h>
#if (NXT_SSLTLS) #if (NXT_TLS)
#include <nxt_ssltls.h> #include <nxt_tls.h>
#endif #endif

View File

@@ -12,57 +12,68 @@
typedef struct { typedef struct {
SSL *session; SSL *session;
nxt_conn_t *conn;
int ssl_error; int ssl_error;
uint8_t times; /* 2 bits */ uint8_t times; /* 2 bits */
uint8_t handshake; /* 1 bit */
nxt_buf_mem_t buffer; nxt_buf_mem_t buffer;
} nxt_openssl_conn_t; } nxt_openssl_conn_t;
static nxt_int_t nxt_openssl_server_init(nxt_ssltls_conf_t *conf); typedef enum {
NXT_OPENSSL_HANDSHAKE = 0,
NXT_OPENSSL_READ,
NXT_OPENSSL_WRITE,
NXT_OPENSSL_SHUTDOWN,
} nxt_openssl_io_t;
static void nxt_openssl_conn_init(nxt_task_t *task, nxt_ssltls_conf_t *conf,
static nxt_int_t nxt_openssl_library_init(nxt_task_t *task);
static void nxt_openssl_library_free(nxt_task_t *task);
#if OPENSSL_VERSION_NUMBER < 0x10100004L
static nxt_int_t nxt_openssl_locks_init(void);
static void nxt_openssl_lock(int mode, int type, const char *file, int line);
static unsigned long nxt_openssl_thread_id(void);
static void nxt_openssl_locks_free(void);
#endif
static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
nxt_tls_conf_t *conf);
static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf);
static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
nxt_conn_t *c); nxt_conn_t *c);
static void nxt_openssl_session_cleanup(nxt_task_t *task, void *data);
static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data); static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data);
static void nxt_openssl_conn_io_read(nxt_task_t *task, void *obj, void *data); static ssize_t nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
static ssize_t nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb);
static ssize_t nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb,
void *buf, size_t size);
static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
void *data); void *data);
static ssize_t nxt_openssl_conn_io_write_chunk(nxt_conn_t *c, nxt_buf_t *b, static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c,
size_t limit); int ret, nxt_err_t sys_err, nxt_openssl_io_t io);
static ssize_t nxt_openssl_conn_io_send(nxt_conn_t *c, void *buf, size_t size); static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_err_t err, const char *fmt, ...);
nxt_conn_t *c, int ret, nxt_err_t sys_err, nxt_work_handler_t handler); static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
static void nxt_cdecl nxt_openssl_conn_error(nxt_conn_t *c, nxt_err_t err,
const char *fmt, ...);
static nxt_uint_t nxt_openssl_log_error_level(nxt_conn_t *c, nxt_err_t err);
static void nxt_cdecl nxt_openssl_log_error(nxt_uint_t level, nxt_log_t *log,
const char *fmt, ...);
static u_char *nxt_openssl_copy_error(u_char *p, u_char *end);
const nxt_ssltls_lib_t nxt_openssl_lib = { const nxt_tls_lib_t nxt_openssl_lib = {
nxt_openssl_server_init, .library_init = nxt_openssl_library_init,
NULL, .library_free = nxt_openssl_library_free,
.server_init = nxt_openssl_server_init,
.server_free = nxt_openssl_server_free,
}; };
static nxt_conn_io_t nxt_openssl_conn_io = { static nxt_conn_io_t nxt_openssl_conn_io = {
NULL, .read = nxt_conn_io_read,
NULL, .recvbuf = nxt_openssl_conn_io_recvbuf,
nxt_openssl_conn_io_read, .write = nxt_conn_io_write,
NULL, .sendbuf = nxt_openssl_conn_io_sendbuf,
NULL,
nxt_conn_io_write, .shutdown = nxt_openssl_conn_io_shutdown,
nxt_openssl_conn_io_write_chunk,
NULL,
NULL,
nxt_openssl_conn_io_send,
nxt_openssl_conn_io_shutdown,
}; };
@@ -71,7 +82,7 @@ static int nxt_openssl_connection_index;
static nxt_int_t static nxt_int_t
nxt_openssl_start(nxt_thread_t *thr) nxt_openssl_library_init(nxt_task_t *task)
{ {
int index; int index;
@@ -79,22 +90,38 @@ nxt_openssl_start(nxt_thread_t *thr)
return NXT_OK; return NXT_OK;
} }
SSL_load_error_strings(); #if OPENSSL_VERSION_NUMBER >= 0x10100003L
OPENSSL_config(NULL); OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
/* #else
* SSL_library_init(3): {
* nxt_int_t ret;
* SSL_library_init() always returns "1",
* so it is safe to discard the return value. SSL_load_error_strings();
*/
(void) SSL_library_init(); OPENSSL_config(NULL);
/*
* SSL_library_init(3):
*
* SSL_library_init() always returns "1",
* so it is safe to discard the return value.
*/
(void) SSL_library_init();
ret = nxt_openssl_locks_init();
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
}
#endif
nxt_openssl_version = SSLeay(); nxt_openssl_version = SSLeay();
nxt_log_error(NXT_LOG_INFO, thr->log, "%s, %xl", nxt_log(task, NXT_LOG_INFO, "%s, %xl",
SSLeay_version(SSLEAY_VERSION), nxt_openssl_version); SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
#ifndef SSL_OP_NO_COMPRESSION #ifndef SSL_OP_NO_COMPRESSION
{ {
@@ -116,7 +143,7 @@ nxt_openssl_start(nxt_thread_t *thr)
index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
if (index == -1) { if (index == -1) {
nxt_openssl_log_error(NXT_LOG_ALERT, thr->log, nxt_openssl_log_error(task, NXT_LOG_ALERT,
"SSL_get_ex_new_index() failed"); "SSL_get_ex_new_index() failed");
return NXT_ERROR; return NXT_ERROR;
} }
@@ -127,29 +154,115 @@ nxt_openssl_start(nxt_thread_t *thr)
} }
static nxt_int_t #if OPENSSL_VERSION_NUMBER >= 0x10100003L
nxt_openssl_server_init(nxt_ssltls_conf_t *conf)
static void
nxt_openssl_library_free(nxt_task_t *task)
{ {
SSL_CTX *ctx; }
const char *certificate, *key, *ciphers, *ca_certificate;
nxt_thread_t *thr;
STACK_OF(X509_NAME) *list;
thr = nxt_thread(); #else
if (nxt_openssl_start(thr) != NXT_OK) { static nxt_thread_mutex_t *nxt_openssl_locks;
static nxt_int_t
nxt_openssl_locks_init(void)
{
int i, n;
nxt_int_t ret;
n = CRYPTO_num_locks();
nxt_openssl_locks = OPENSSL_malloc(n * sizeof(nxt_thread_mutex_t));
if (nxt_slow_path(nxt_openssl_locks == NULL)) {
return NXT_ERROR; return NXT_ERROR;
} }
for (i = 0; i < n; i++) {
ret = nxt_thread_mutex_create(&nxt_openssl_locks[i]);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
}
CRYPTO_set_locking_callback(nxt_openssl_lock);
CRYPTO_set_id_callback(nxt_openssl_thread_id);
return NXT_OK;
}
static void
nxt_openssl_lock(int mode, int type, const char *file, int line)
{
nxt_thread_mutex_t *lock;
lock = &nxt_openssl_locks[type];
if ((mode & CRYPTO_LOCK) != 0) {
(void) nxt_thread_mutex_lock(lock);
} else {
(void) nxt_thread_mutex_unlock(lock);
}
}
static u_long
nxt_openssl_thread_id(void)
{
return (u_long) nxt_thread_handle();
}
static void
nxt_openssl_library_free(nxt_task_t *task)
{
nxt_openssl_locks_free();
}
static void
nxt_openssl_locks_free(void)
{
int i, n;
n = CRYPTO_num_locks();
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < n; i++) {
nxt_thread_mutex_destroy(&nxt_openssl_locks[i]);
}
OPENSSL_free(nxt_openssl_locks);
}
#endif
static nxt_int_t
nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
{
SSL_CTX *ctx;
const char *certificate, *key, *ciphers, *ca_certificate;
STACK_OF(X509_NAME) *list;
ctx = SSL_CTX_new(SSLv23_server_method()); ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL) { if (ctx == NULL) {
nxt_openssl_log_error(NXT_LOG_ALERT, thr->log, "SSL_CTX_new() failed"); nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_new() failed");
return NXT_ERROR; return NXT_ERROR;
} }
conf->ctx = ctx; conf->ctx = ctx;
conf->conn_init = nxt_openssl_conn_init; conf->conn_init = nxt_openssl_conn_init;
#ifdef SSL_OP_NO_RENEGOTIATION
/* Renegration is not currently supported. */
SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
#endif
#ifdef SSL_OP_NO_COMPRESSION #ifdef SSL_OP_NO_COMPRESSION
/* /*
* Disable gzip compression in OpenSSL 1.0.0, * Disable gzip compression in OpenSSL 1.0.0,
@@ -174,7 +287,7 @@ nxt_openssl_server_init(nxt_ssltls_conf_t *conf)
certificate = conf->certificate; certificate = conf->certificate;
if (SSL_CTX_use_certificate_chain_file(ctx, certificate) == 0) { if (SSL_CTX_use_certificate_chain_file(ctx, certificate) == 0) {
nxt_openssl_log_error(NXT_LOG_ALERT, thr->log, nxt_openssl_log_error(task, NXT_LOG_ALERT,
"SSL_CTX_use_certificate_file(\"%s\") failed", "SSL_CTX_use_certificate_file(\"%s\") failed",
certificate); certificate);
goto fail; goto fail;
@@ -183,7 +296,7 @@ nxt_openssl_server_init(nxt_ssltls_conf_t *conf)
key = conf->certificate_key; key = conf->certificate_key;
if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) { if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
nxt_openssl_log_error(NXT_LOG_ALERT, thr->log, nxt_openssl_log_error(task, NXT_LOG_ALERT,
"SSL_CTX_use_PrivateKey_file(\"%s\") failed", "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
key); key);
goto fail; goto fail;
@@ -192,7 +305,7 @@ nxt_openssl_server_init(nxt_ssltls_conf_t *conf)
ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5"; ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) { if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
nxt_openssl_log_error(NXT_LOG_ALERT, thr->log, nxt_openssl_log_error(task, NXT_LOG_ALERT,
"SSL_CTX_set_cipher_list(\"%s\") failed", "SSL_CTX_set_cipher_list(\"%s\") failed",
ciphers); ciphers);
goto fail; goto fail;
@@ -211,7 +324,7 @@ nxt_openssl_server_init(nxt_ssltls_conf_t *conf)
ca_certificate = conf->ca_certificate; ca_certificate = conf->ca_certificate;
if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) { if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
nxt_openssl_log_error(NXT_LOG_ALERT, thr->log, nxt_openssl_log_error(task, NXT_LOG_ALERT,
"SSL_CTX_load_verify_locations(\"%s\") failed", "SSL_CTX_load_verify_locations(\"%s\") failed",
ca_certificate); ca_certificate);
goto fail; goto fail;
@@ -220,7 +333,7 @@ nxt_openssl_server_init(nxt_ssltls_conf_t *conf)
list = SSL_load_client_CA_file(ca_certificate); list = SSL_load_client_CA_file(ca_certificate);
if (list == NULL) { if (list == NULL) {
nxt_openssl_log_error(NXT_LOG_ALERT, thr->log, nxt_openssl_log_error(task, NXT_LOG_ALERT,
"SSL_load_client_CA_file(\"%s\") failed", "SSL_load_client_CA_file(\"%s\") failed",
ca_certificate); ca_certificate);
goto fail; goto fail;
@@ -246,55 +359,53 @@ fail:
static void static void
nxt_openssl_conn_init(nxt_task_t *task, nxt_ssltls_conf_t *conf, nxt_conn_t *c) nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
{ {
int ret; SSL_CTX_free(conf->ctx);
SSL *s; }
SSL_CTX *ctx;
nxt_openssl_conn_t *ssltls;
nxt_mem_pool_cleanup_t *mpcl; static void
nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
{
int ret;
SSL *s;
SSL_CTX *ctx;
nxt_openssl_conn_t *tls;
nxt_log_debug(c->socket.log, "openssl conn init"); nxt_log_debug(c->socket.log, "openssl conn init");
ssltls = nxt_mp_zget(c->mem_pool, sizeof(nxt_openssl_conn_t)); tls = nxt_mp_zget(c->mem_pool, sizeof(nxt_openssl_conn_t));
if (ssltls == NULL) { if (tls == NULL) {
goto fail; goto fail;
} }
c->u.ssltls = ssltls; c->u.tls = tls;
nxt_buf_mem_set_size(&ssltls->buffer, conf->buffer_size); nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size);
mpcl = nxt_mem_pool_cleanup(c->mem_pool, 0);
if (mpcl == NULL) {
goto fail;
}
ctx = conf->ctx; ctx = conf->ctx;
s = SSL_new(ctx); s = SSL_new(ctx);
if (s == NULL) { if (s == NULL) {
nxt_openssl_log_error(NXT_LOG_ALERT, c->socket.log, nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_new() failed");
"SSL_new() failed");
goto fail; goto fail;
} }
ssltls->session = s; tls->session = s;
mpcl->handler = nxt_openssl_session_cleanup; tls->conn = c;
mpcl->data = ssltls;
ret = SSL_set_fd(s, c->socket.fd); ret = SSL_set_fd(s, c->socket.fd);
if (ret == 0) { if (ret == 0) {
nxt_openssl_log_error(NXT_LOG_ALERT, c->socket.log, nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_fd(%d) failed",
"SSL_set_fd(%d) failed", c->socket.fd); c->socket.fd);
goto fail; goto fail;
} }
SSL_set_accept_state(s); SSL_set_accept_state(s);
if (SSL_set_ex_data(s, nxt_openssl_connection_index, c) == 0) { if (SSL_set_ex_data(s, nxt_openssl_connection_index, c) == 0) {
nxt_openssl_log_error(NXT_LOG_ALERT, c->socket.log, nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_ex_data() failed");
"SSL_set_ex_data() failed");
goto fail; goto fail;
} }
@@ -311,38 +422,37 @@ fail:
} }
static void nxt_inline void
nxt_openssl_session_cleanup(nxt_task_t *task, void *data) nxt_openssl_conn_free(nxt_task_t *task, nxt_openssl_conn_t *tls)
{ {
nxt_openssl_conn_t *ssltls; nxt_debug(task, "openssl conn free");
ssltls = data; nxt_free(tls->buffer.start);
nxt_debug(task, "openssl session cleanup"); SSL_free(tls->session);
nxt_free(ssltls->buffer.start);
SSL_free(ssltls->session);
} }
static void static void
nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data) nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data)
{ {
int ret; int ret;
nxt_int_t n; nxt_int_t n;
nxt_err_t err; nxt_err_t err;
nxt_conn_t *c; nxt_conn_t *c;
nxt_openssl_conn_t *ssltls; nxt_work_queue_t *wq;
nxt_work_handler_t handler;
nxt_openssl_conn_t *tls;
const nxt_conn_state_t *state;
c = obj; c = obj;
ssltls = c->u.ssltls; tls = c->u.tls;
nxt_debug(task, "openssl conn handshake: %d", ssltls->times); nxt_debug(task, "openssl conn handshake: %d", tls->times);
/* "ssltls->times == 1" is suitable to run SSL_do_handshake() in job. */ /* "tls->times == 1" is suitable to run SSL_do_handshake() in job. */
ret = SSL_do_handshake(ssltls->session); ret = SSL_do_handshake(tls->session);
err = (ret <= 0) ? nxt_socket_errno : 0; err = (ret <= 0) ? nxt_socket_errno : 0;
@@ -350,130 +460,161 @@ nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data)
nxt_debug(task, "SSL_do_handshake(%d): %d err:%d", c->socket.fd, ret, err); nxt_debug(task, "SSL_do_handshake(%d): %d err:%d", c->socket.fd, ret, err);
state = (c->read_state != NULL) ? c->read_state : c->write_state;
if (ret > 0) { if (ret > 0) {
/* ret == 1, the handshake was successfully completed. */ /* ret == 1, the handshake was successfully completed. */
nxt_openssl_conn_io_read(task, c, data); tls->handshake = 1;
return;
}
n = nxt_openssl_conn_test_error(task, c, ret, err, if (c->read_state != NULL) {
nxt_openssl_conn_handshake); if (state->io_read_handler != NULL || c->read != NULL) {
nxt_conn_read(task->thread->engine, c);
if (n == NXT_ERROR) {
nxt_openssl_conn_error(c, err, "SSL_do_handshake(%d) failed",
c->socket.fd);
nxt_work_queue_add(c->read_work_queue, c->read_state->error_handler,
task, c, data);
} else if (ssltls->ssl_error == SSL_ERROR_WANT_READ && ssltls->times < 2) {
ssltls->times++;
}
}
static void
nxt_openssl_conn_io_read(nxt_task_t *task, void *obj, void *data)
{
int ret;
nxt_buf_t *b;
nxt_int_t n;
nxt_err_t err;
nxt_conn_t *c;
nxt_work_handler_t handler;
nxt_openssl_conn_t *ssltls;
c = obj;
nxt_debug(task, "openssl conn read");
handler = c->read_state->ready_handler;
b = c->read;
/* b == NULL is used to test descriptor readiness. */
if (b != NULL) {
ssltls = c->u.ssltls;
ret = SSL_read(ssltls->session, b->mem.free, b->mem.end - b->mem.free);
err = (ret <= 0) ? nxt_socket_errno : 0;
nxt_debug(task, "SSL_read(%d, %p, %uz): %d err:%d",
c->socket.fd, b->mem.free, b->mem.end - b->mem.free,
ret, err);
if (ret > 0) {
/* c->socket.read_ready is kept. */
b->mem.free += ret;
handler = c->read_state->ready_handler;
} else {
n = nxt_openssl_conn_test_error(task, c, ret, err,
nxt_openssl_conn_io_read);
if (nxt_fast_path(n != NXT_ERROR)) {
return; return;
} }
nxt_openssl_conn_error(c, err, "SSL_read(%d, %p, %uz) failed", } else {
c->socket.fd, b->mem.free, if (c->write != NULL) {
b->mem.end - b->mem.free); nxt_conn_write(task->thread->engine, c);
return;
}
}
handler = c->read_state->error_handler; handler = state->ready_handler;
} else {
c->socket.read_handler = nxt_openssl_conn_handshake;
c->socket.write_handler = nxt_openssl_conn_handshake;
n = nxt_openssl_conn_test_error(task, c, ret, err,
NXT_OPENSSL_HANDSHAKE);
switch (n) {
case NXT_AGAIN:
if (tls->ssl_error == SSL_ERROR_WANT_READ && tls->times < 2) {
tls->times++;
}
return;
case 0:
handler = state->close_handler;
break;
default:
case NXT_ERROR:
c->socket.error = err;
nxt_openssl_conn_error(task, err, "SSL_do_handshake(%d) failed",
c->socket.fd);
handler = state->error_handler;
break;
} }
} }
nxt_work_queue_add(c->read_work_queue, handler, task, c, data); wq = (c->read_state != NULL) ? c->read_work_queue : c->write_work_queue;
nxt_work_queue_add(wq, handler, task, c, data);
} }
static ssize_t static ssize_t
nxt_openssl_conn_io_write_chunk(nxt_conn_t *c, nxt_buf_t *b, size_t limit) nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b)
{ {
nxt_openssl_conn_t *ssltls; int ret;
size_t size;
nxt_int_t n;
nxt_err_t err;
nxt_openssl_conn_t *tls;
nxt_debug(c->socket.task, "openssl conn write chunk"); tls = c->u.tls;
size = b->mem.end - b->mem.free;
ssltls = c->u.ssltls; ret = SSL_read(tls->session, b->mem.free, size);
return nxt_sendbuf_copy_coalesce(c, &ssltls->buffer, b, limit); err = (ret <= 0) ? nxt_socket_errno : 0;
nxt_debug(c->socket.task, "SSL_read(%d, %p, %uz): %d err:%d",
c->socket.fd, b->mem.free, size, ret, err);
if (ret > 0) {
if ((size_t) ret < size) {
c->socket.read_ready = 0;
}
return ret;
}
n = nxt_openssl_conn_test_error(c->socket.task, c, ret, err,
NXT_OPENSSL_READ);
if (n == NXT_ERROR) {
c->socket.error = err;
nxt_openssl_conn_error(c->socket.task, err,
"SSL_read(%d, %p, %uz) failed",
c->socket.fd, b->mem.free, size);
}
return n;
} }
static ssize_t static ssize_t
nxt_openssl_conn_io_send(nxt_conn_t *c, void *buf, size_t size) nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb)
{
nxt_uint_t niov;
struct iovec iov;
niov = nxt_sendbuf_mem_coalesce0(task, sb, &iov, 1);
if (niov == 0 && sb->sync) {
return 0;
}
return nxt_openssl_conn_io_send(task, sb, iov.iov_base, iov.iov_len);
}
static ssize_t
nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb, void *buf,
size_t size)
{ {
int ret; int ret;
nxt_err_t err; nxt_err_t err;
nxt_int_t n; nxt_int_t n;
nxt_openssl_conn_t *ssltls; nxt_conn_t *c;
nxt_openssl_conn_t *tls;
ssltls = c->u.ssltls; tls = sb->tls;
ret = SSL_write(ssltls->session, buf, size); ret = SSL_write(tls->session, buf, size);
if (ret <= 0) { if (ret <= 0) {
err = nxt_socket_errno; err = nxt_socket_errno;
c->socket.error = err; sb->error = err;
} else { } else {
err = 0; err = 0;
} }
nxt_log_debug(c->socket.log, "SSL_write(%d, %p, %uz): %d err:%d", nxt_debug(task, "SSL_write(%d, %p, %uz): %d err:%d",
c->socket.fd, buf, size, ret, err); sb->socket, buf, size, ret, err);
if (ret > 0) { if (ret > 0) {
return ret; return ret;
} }
n = nxt_openssl_conn_test_error(c->socket.task, c, ret, err, c = tls->conn;
nxt_conn_io_write); c->socket.write_ready = sb->ready;
c->socket.error = sb->error;
n = nxt_openssl_conn_test_error(task, c, ret, err, NXT_OPENSSL_WRITE);
sb->ready = c->socket.write_ready;
sb->error = c->socket.error;
if (n == NXT_ERROR) { if (n == NXT_ERROR) {
nxt_openssl_conn_error(c, err, "SSL_write(%d, %p, %uz) failed", sb->error = err;
c->socket.fd, buf, size); nxt_openssl_conn_error(task, err, "SSL_write(%d, %p, %uz) failed",
sb->socket, buf, size);
} }
return n; return n;
@@ -489,18 +630,19 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
nxt_int_t n; nxt_int_t n;
nxt_bool_t quiet, once; nxt_bool_t quiet, once;
nxt_conn_t *c; nxt_conn_t *c;
nxt_openssl_conn_t *tls;
nxt_work_handler_t handler; nxt_work_handler_t handler;
nxt_openssl_conn_t *ssltls;
c = obj; c = obj;
nxt_debug(task, "openssl conn shutdown"); nxt_debug(task, "openssl conn shutdown");
ssltls = c->u.ssltls; c->read_state = NULL;
s = ssltls->session; tls = c->u.tls;
s = tls->session;
if (s == NULL) { if (s == NULL || !tls->handshake) {
handler = c->write_state->close_handler; handler = c->write_state->ready_handler;
goto done; goto done;
} }
@@ -532,22 +674,22 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
if (ret > 0) { if (ret > 0) {
/* ret == 1, the shutdown was successfully completed. */ /* ret == 1, the shutdown was successfully completed. */
handler = c->write_state->close_handler; handler = c->write_state->ready_handler;
goto done; goto done;
} }
if (ret == 0) { if (ret == 0) {
/* /*
* If SSL_shutdown() returns 0 then it should be called * If SSL_shutdown() returns 0 then it should be called
* again. The second SSL_shutdown() call should returns * again. The second SSL_shutdown() call should return
* -1/SSL_ERROR_WANT_READ or -1/SSL_ERROR_WANT_WRITE. * -1/SSL_ERROR_WANT_READ or -1/SSL_ERROR_WANT_WRITE.
* OpenSSL prior to 0.9.8m version however never returns * OpenSSL prior to 0.9.8m version however never returns
* -1 at all. Fortunately, OpenSSL internals preserve * -1 at all. Fortunately, OpenSSL preserves internally
* correct status available via SSL_get_error(-1). * correct status available via SSL_get_error(-1).
*/ */
if (once) { if (once) {
mode = SSL_get_shutdown(s);
once = 0; once = 0;
mode = SSL_get_shutdown(s);
continue; continue;
} }
@@ -559,71 +701,82 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
break; break;
} }
n = nxt_openssl_conn_test_error(task, c, ret, err, c->socket.read_handler = nxt_openssl_conn_io_shutdown;
nxt_openssl_conn_io_shutdown); c->socket.write_handler = nxt_openssl_conn_io_shutdown;
c->socket.error_handler = c->write_state->error_handler;
if (nxt_fast_path(n == 0)) { n = nxt_openssl_conn_test_error(task, c, ret, err, NXT_OPENSSL_SHUTDOWN);
return;
}
if (n != NXT_ERROR) { /* n == NXT_AGAIN */ switch (n) {
c->socket.error_handler = c->read_state->error_handler;
case 0:
handler = c->write_state->close_handler;
break;
case NXT_AGAIN:
nxt_timer_add(task->thread->engine, &c->read_timer, 5000); nxt_timer_add(task->thread->engine, &c->read_timer, 5000);
return; return;
default:
case NXT_ERROR:
c->socket.error = err;
nxt_openssl_conn_error(task, err, "SSL_shutdown(%d) failed",
c->socket.fd);
handler = c->write_state->error_handler;
} }
nxt_openssl_conn_error(c, err, "SSL_shutdown(%d) failed", c->socket.fd);
handler = c->write_state->error_handler;
done: done:
nxt_openssl_conn_free(task, tls);
nxt_work_queue_add(c->write_work_queue, handler, task, c, data); nxt_work_queue_add(c->write_work_queue, handler, task, c, data);
} }
static nxt_int_t static nxt_int_t
nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret, nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret,
nxt_err_t sys_err, nxt_work_handler_t handler) nxt_err_t sys_err, nxt_openssl_io_t io)
{ {
u_long lib_err; u_long lib_err;
nxt_work_queue_t *wq; nxt_openssl_conn_t *tls;
nxt_openssl_conn_t *ssltls;
ssltls = c->u.ssltls; tls = c->u.tls;
ssltls->ssl_error = SSL_get_error(ssltls->session, ret); tls->ssl_error = SSL_get_error(tls->session, ret);
nxt_log_debug(c->socket.log, "SSL_get_error(): %d", ssltls->ssl_error); nxt_debug(task, "SSL_get_error(): %d", tls->ssl_error);
switch (ssltls->ssl_error) { switch (tls->ssl_error) {
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
nxt_fd_event_block_write(task->thread->engine, &c->socket);
c->socket.read_ready = 0; if (io != NXT_OPENSSL_READ) {
c->socket.read_handler = handler; nxt_fd_event_block_write(task->thread->engine, &c->socket);
if (nxt_fd_event_is_disabled(c->socket.read)) { c->socket.read_ready = 0;
nxt_fd_event_enable_read(task->thread->engine, &c->socket);
if (nxt_fd_event_is_disabled(c->socket.read)) {
nxt_fd_event_enable_read(task->thread->engine, &c->socket);
}
} }
return NXT_AGAIN; return NXT_AGAIN;
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
nxt_fd_event_block_read(task->thread->engine, &c->socket);
c->socket.write_ready = 0; if (io != NXT_OPENSSL_WRITE) {
c->socket.write_handler = handler; nxt_fd_event_block_read(task->thread->engine, &c->socket);
if (nxt_fd_event_is_disabled(c->socket.write)) { c->socket.write_ready = 0;
nxt_fd_event_enable_write(task->thread->engine, &c->socket);
if (nxt_fd_event_is_disabled(c->socket.write)) {
nxt_fd_event_enable_write(task->thread->engine, &c->socket);
}
} }
return NXT_AGAIN; return NXT_AGAIN;
case SSL_ERROR_SYSCALL: case SSL_ERROR_SYSCALL:
lib_err = ERR_peek_error(); lib_err = ERR_peek_error();
nxt_debug(task, "ERR_peek_error(): %l", lib_err); nxt_debug(task, "ERR_peek_error(): %l", lib_err);
@@ -634,23 +787,10 @@ nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret,
/* A connection was just closed. */ /* A connection was just closed. */
c->socket.closed = 1; c->socket.closed = 1;
return 0;
/* Fall through. */
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
/* A "close notify" alert. */ /* A "close notify" alert. */
if (c->read_state != NULL) {
wq = c->read_work_queue;
handler = c->read_state->close_handler;
} else {
wq = c->write_work_queue;
handler = c->write_state->close_handler;
}
nxt_work_queue_add(wq, handler, task, c, c->socket.data);
return 0; return 0;
default: /* SSL_ERROR_SSL, etc. */ default: /* SSL_ERROR_SSL, etc. */
@@ -661,17 +801,16 @@ nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret,
static void nxt_cdecl static void nxt_cdecl
nxt_openssl_conn_error(nxt_conn_t *c, nxt_err_t err, const char *fmt, ...) nxt_openssl_conn_error(nxt_task_t *task, nxt_err_t err, const char *fmt, ...)
{ {
u_char *p, *end; u_char *p, *end;
va_list args; va_list args;
nxt_uint_t level; nxt_uint_t level;
u_char msg[NXT_MAX_ERROR_STR]; u_char msg[NXT_MAX_ERROR_STR];
c->socket.error = err; level = nxt_openssl_log_error_level(err);
level = nxt_openssl_log_error_level(c, err);
if (nxt_log_level_enough(c->socket.log, level)) { if (nxt_log_level_enough(task->log, level)) {
end = msg + sizeof(msg); end = msg + sizeof(msg);
@@ -685,7 +824,7 @@ nxt_openssl_conn_error(nxt_conn_t *c, nxt_err_t err, const char *fmt, ...)
p = nxt_openssl_copy_error(p, end); p = nxt_openssl_copy_error(p, end);
nxt_log_error(level, c->socket.log, "%*s", p - msg, msg); nxt_log(task, level, "%*s", p - msg, msg);
} else { } else {
ERR_clear_error(); ERR_clear_error();
@@ -694,7 +833,7 @@ nxt_openssl_conn_error(nxt_conn_t *c, nxt_err_t err, const char *fmt, ...)
static nxt_uint_t static nxt_uint_t
nxt_openssl_log_error_level(nxt_conn_t *c, nxt_err_t err) nxt_openssl_log_error_level(nxt_err_t err)
{ {
switch (ERR_GET_REASON(ERR_peek_error())) { switch (ERR_GET_REASON(ERR_peek_error())) {
@@ -707,7 +846,9 @@ nxt_openssl_log_error_level(nxt_conn_t *c, nxt_err_t err)
case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST: /* 151 */ case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST: /* 151 */
case SSL_R_EXCESSIVE_MESSAGE_SIZE: /* 152 */ case SSL_R_EXCESSIVE_MESSAGE_SIZE: /* 152 */
case SSL_R_LENGTH_MISMATCH: /* 159 */ case SSL_R_LENGTH_MISMATCH: /* 159 */
#ifdef SSL_R_NO_CIPHERS_PASSED
case SSL_R_NO_CIPHERS_PASSED: /* 182 */ case SSL_R_NO_CIPHERS_PASSED: /* 182 */
#endif
case SSL_R_NO_CIPHERS_SPECIFIED: /* 183 */ case SSL_R_NO_CIPHERS_SPECIFIED: /* 183 */
case SSL_R_NO_COMPRESSION_SPECIFIED: /* 187 */ case SSL_R_NO_COMPRESSION_SPECIFIED: /* 187 */
case SSL_R_NO_SHARED_CIPHER: /* 193 */ case SSL_R_NO_SHARED_CIPHER: /* 193 */
@@ -768,8 +909,8 @@ nxt_openssl_log_error_level(nxt_conn_t *c, nxt_err_t err)
} }
static void nxt_cdecl void nxt_cdecl
nxt_openssl_log_error(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level, const char *fmt, ...)
{ {
u_char *p, *end; u_char *p, *end;
va_list args; va_list args;
@@ -783,11 +924,11 @@ nxt_openssl_log_error(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...)
p = nxt_openssl_copy_error(p, end); p = nxt_openssl_copy_error(p, end);
nxt_log_error(level, log, "%*s", p - msg, msg); nxt_log(task, level, "%*s", p - msg, msg);
} }
static u_char * u_char *
nxt_openssl_copy_error(u_char *p, u_char *end) nxt_openssl_copy_error(u_char *p, u_char *end)
{ {
int flags; int flags;
@@ -806,8 +947,8 @@ nxt_openssl_copy_error(u_char *p, u_char *end)
p = nxt_sprintf(p, end, " (%d: %s) (OpenSSL: ", ERR_GET_REASON(err), data); p = nxt_sprintf(p, end, " (%d: %s) (OpenSSL: ", ERR_GET_REASON(err), data);
/* /*
* ... followed by all queued cumbersome OpenSSL * ... followed by all queued cumbersome OpenSSL error messages
* error messages and drain the error queue. * and drain the error queue.
*/ */
delimiter = ""; delimiter = "";
clear = 0; clear = 0;

View File

@@ -299,6 +299,18 @@ nxt_router_start(nxt_task_t *task, void *data)
rt = task->thread->runtime; rt = task->thread->runtime;
#if (NXT_TLS)
rt->tls = nxt_service_get(rt->services, "SSL/TLS", "OpenSSL");
if (nxt_slow_path(rt->tls == NULL)) {
return NXT_ERROR;
}
ret = rt->tls->library_init(task);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
#endif
ret = nxt_http_init(task, rt); ret = nxt_http_init(task, rt);
if (nxt_slow_path(ret != NXT_OK)) { if (nxt_slow_path(ret != NXT_OK)) {
return ret; return ret;
@@ -2773,6 +2785,12 @@ nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint)
if (rtcf != NULL) { if (rtcf != NULL) {
nxt_debug(task, "old router conf is destroyed"); nxt_debug(task, "old router conf is destroyed");
#if (NXT_TLS)
if (skcf->tls != NULL) {
task->thread->runtime->tls->server_free(task, skcf->tls);
}
#endif
nxt_router_access_log_release(task, lock, rtcf->access_log); nxt_router_access_log_release(task, lock, rtcf->access_log);
nxt_mp_thread_adopt(rtcf->mem_pool); nxt_mp_thread_adopt(rtcf->mem_pool);

View File

@@ -153,6 +153,10 @@ typedef struct {
nxt_msec_t header_read_timeout; nxt_msec_t header_read_timeout;
nxt_msec_t body_read_timeout; nxt_msec_t body_read_timeout;
nxt_msec_t send_timeout; nxt_msec_t send_timeout;
#if (NXT_TLS)
nxt_tls_conf_t *tls;
#endif
} nxt_socket_conf_t; } nxt_socket_conf_t;

View File

@@ -28,6 +28,10 @@ struct nxt_runtime_s {
nxt_file_name_t *pid_file; nxt_file_name_t *pid_file;
#if (NXT_TLS)
const nxt_tls_lib_t *tls;
#endif
nxt_array_t *thread_pools; /* of nxt_thread_pool_t */ nxt_array_t *thread_pools; /* of nxt_thread_pool_t */
nxt_runtime_cont_t continuation; nxt_runtime_cont_t continuation;

View File

@@ -37,6 +37,7 @@
typedef struct { typedef struct {
nxt_buf_t *buf; nxt_buf_t *buf;
void *tls;
nxt_socket_t socket; nxt_socket_t socket;
nxt_err_t error; nxt_err_t error;
nxt_off_t sent; nxt_off_t sent;

View File

@@ -1,7 +0,0 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) NGINX, Inc.
*/
#include <nxt_main.h>

View File

@@ -4,8 +4,8 @@
* Copyright (C) NGINX, Inc. * Copyright (C) NGINX, Inc.
*/ */
#ifndef _NXT_SSLTLS_H_INCLUDED_ #ifndef _NXT_TLS_H_INCLUDED_
#define _NXT_SSLTLS_H_INCLUDED_ #define _NXT_TLS_H_INCLUDED_
/* /*
@@ -20,24 +20,29 @@
* and compatible with tunnels. * and compatible with tunnels.
*/ */
#define NXT_SSLTLS_BUFFER_SIZE 4096 #define NXT_TLS_BUFFER_SIZE 4096
typedef struct nxt_ssltls_conf_s nxt_ssltls_conf_t; typedef struct nxt_tls_conf_s nxt_tls_conf_t;
typedef struct { typedef struct {
nxt_int_t (*server_init)(nxt_ssltls_conf_t *conf); nxt_int_t (*library_init)(nxt_task_t *task);
nxt_int_t (*set_versions)(nxt_ssltls_conf_t *conf); void (*library_free)(nxt_task_t *task);
} nxt_ssltls_lib_t;
nxt_int_t (*server_init)(nxt_task_t *task,
nxt_tls_conf_t *conf);
void (*server_free)(nxt_task_t *task,
nxt_tls_conf_t *conf);
} nxt_tls_lib_t;
struct nxt_ssltls_conf_s { struct nxt_tls_conf_s {
void *ctx; void *ctx;
void (*conn_init)(nxt_task_t *task, void (*conn_init)(nxt_task_t *task,
nxt_ssltls_conf_t *conf, nxt_conn_t *c); nxt_tls_conf_t *conf, nxt_conn_t *c);
const nxt_ssltls_lib_t *lib; const nxt_tls_lib_t *lib;
char *certificate; char *certificate;
char *certificate_key; char *certificate_key;
@@ -50,20 +55,24 @@ struct nxt_ssltls_conf_s {
#if (NXT_HAVE_OPENSSL) #if (NXT_HAVE_OPENSSL)
extern const nxt_ssltls_lib_t nxt_openssl_lib; extern const nxt_tls_lib_t nxt_openssl_lib;
void nxt_cdecl nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level,
const char *fmt, ...);
u_char *nxt_openssl_copy_error(u_char *p, u_char *end);
#endif #endif
#if (NXT_HAVE_GNUTLS) #if (NXT_HAVE_GNUTLS)
extern const nxt_ssltls_lib_t nxt_gnutls_lib; extern const nxt_tls_lib_t nxt_gnutls_lib;
#endif #endif
#if (NXT_HAVE_CYASSL) #if (NXT_HAVE_CYASSL)
extern const nxt_ssltls_lib_t nxt_cyassl_lib; extern const nxt_tls_lib_t nxt_cyassl_lib;
#endif #endif
#if (NXT_HAVE_POLARSSL) #if (NXT_HAVE_POLARSSL)
extern const nxt_ssltls_lib_t nxt_polar_lib; extern const nxt_tls_lib_t nxt_polar_lib;
#endif #endif
#endif /* _NXT_SSLTLS_H_INCLUDED_ */ #endif /* _NXT_TLS_H_INCLUDED_ */