Commit 7296fb5b authored by somdoron's avatar somdoron

problem: unsecured websocket is rarely used in production

Solution: support websocket with tls (wss)
parent 8fe62090
...@@ -153,6 +153,7 @@ test_xpub_verbose ...@@ -153,6 +153,7 @@ test_xpub_verbose
test_mock_pub_sub test_mock_pub_sub
test_proxy_hwm test_proxy_hwm
test_ws_transport test_ws_transport
test_wss_transport
unittest_ip_resolver unittest_ip_resolver
unittest_mtrie unittest_mtrie
unittest_poller unittest_poller
......
...@@ -50,12 +50,14 @@ matrix: ...@@ -50,12 +50,14 @@ matrix:
apt: apt:
packages: packages:
- lcov - lcov
- env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled - env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled TLS=enabled
os: linux os: linux
dist: xenial
addons: addons:
apt: apt:
packages: packages:
- valgrind - valgrind
- libgnutls-dev
- env: BUILD_TYPE=default CURVE=libsodium GSSAPI=enabled PGM=enabled NORM=enabled - env: BUILD_TYPE=default CURVE=libsodium GSSAPI=enabled PGM=enabled NORM=enabled
os: linux os: linux
addons: addons:
...@@ -70,6 +72,13 @@ matrix: ...@@ -70,6 +72,13 @@ matrix:
- libsodium-dev - libsodium-dev
- asciidoc - asciidoc
- xmlto - xmlto
- env: BUILD_TYPE=default DRAFT=enabled TLS=enabled
os: linux
dist: xenial
addons:
apt:
packages:
- libgnutls-dev
- env: BUILD_TYPE=default CURVE=libsodium DRAFT=enabled GSSAPI=enabled PGM=enabled NORM=enabled TIPC=enabled USE_NSS=yes - env: BUILD_TYPE=default CURVE=libsodium DRAFT=enabled GSSAPI=enabled PGM=enabled NORM=enabled TIPC=enabled USE_NSS=yes
os: linux os: linux
addons: addons:
......
...@@ -305,6 +305,12 @@ src_libzmq_la_SOURCES += \ ...@@ -305,6 +305,12 @@ src_libzmq_la_SOURCES += \
external/sha1/sha1.h external/sha1/sha1.h
endif endif
if HAVE_WSS
src_libzmq_la_SOURCES += \
src/wss_engine.cpp \
src/wss_engine.hpp
endif
if ON_MINGW if ON_MINGW
src_libzmq_la_LDFLAGS = \ src_libzmq_la_LDFLAGS = \
-no-undefined \ -no-undefined \
...@@ -342,11 +348,16 @@ src_libzmq_la_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ $(CODE_COVERAGE_CXXFLAGS) \ ...@@ -342,11 +348,16 @@ src_libzmq_la_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ $(CODE_COVERAGE_CXXFLAGS) \
$(LIBUNWIND_CFLAGS) $(LIBUNWIND_CFLAGS)
src_libzmq_la_LIBADD = $(CODE_COVERAGE_LDFLAGS) $(LIBUNWIND_LIBS) src_libzmq_la_LIBADD = $(CODE_COVERAGE_LDFLAGS) $(LIBUNWIND_LIBS)
if HAVE_WS if USE_NSS
src_libzmq_la_CPPFLAGS += ${NSS3_CFLAGS} src_libzmq_la_CPPFLAGS += ${NSS3_CFLAGS}
src_libzmq_la_LIBADD += ${NSS3_LIBS} src_libzmq_la_LIBADD += ${NSS3_LIBS}
endif endif
if USE_GNUTLS
src_libzmq_la_CPPFLAGS += ${GNUTLS_CFLAGS}
src_libzmq_la_LIBADD += ${GNUTLS_LIBS}
endif
if USE_LIBSODIUM if USE_LIBSODIUM
src_libzmq_la_CPPFLAGS += ${sodium_CFLAGS} src_libzmq_la_CPPFLAGS += ${sodium_CFLAGS}
src_libzmq_la_LIBADD += ${sodium_LIBS} src_libzmq_la_LIBADD += ${sodium_LIBS}
...@@ -850,6 +861,14 @@ tests_test_ws_transport_LDADD = ${TESTUTIL_LIBS} src/libzmq.la ${NSS3_LIBS} ...@@ -850,6 +861,14 @@ tests_test_ws_transport_LDADD = ${TESTUTIL_LIBS} src/libzmq.la ${NSS3_LIBS}
tests_test_ws_transport_CPPFLAGS = ${TESTUTIL_CPPFLAGS} ${NSS3_CFLAGS} tests_test_ws_transport_CPPFLAGS = ${TESTUTIL_CPPFLAGS} ${NSS3_CFLAGS}
endif endif
if HAVE_WSS
test_apps += \
tests/test_wss_transport
tests_test_wss_transport_SOURCES = tests/test_wss_transport.cpp
tests_test_wss_transport_LDADD = ${TESTUTIL_LIBS} src/libzmq.la ${GNUTLS_LIBS}
tests_test_wss_transport_CPPFLAGS = ${TESTUTIL_CPPFLAGS} ${GNUTLS_CFLAGS}
endif
if !ON_MINGW if !ON_MINGW
if !ON_CYGWIN if !ON_CYGWIN
test_apps += \ test_apps += \
......
...@@ -14,6 +14,10 @@ CONFIG_OPTS+=("PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig") ...@@ -14,6 +14,10 @@ CONFIG_OPTS+=("PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig")
CONFIG_OPTS+=("--prefix=${BUILD_PREFIX}") CONFIG_OPTS+=("--prefix=${BUILD_PREFIX}")
CONFIG_OPTS+=("--enable-valgrind") CONFIG_OPTS+=("--enable-valgrind")
if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
CONFIG_OPTS+=("--with-tls=yes")
fi
if [ -z $CURVE ]; then if [ -z $CURVE ]; then
CONFIG_OPTS+=("--disable-curve") CONFIG_OPTS+=("--disable-curve")
elif [ $CURVE == "libsodium" ]; then elif [ $CURVE == "libsodium" ]; then
......
...@@ -60,6 +60,10 @@ if [ $BUILD_TYPE == "default" ]; then ...@@ -60,6 +60,10 @@ if [ $BUILD_TYPE == "default" ]; then
CONFIG_OPTS+=("--with-poller=${POLLER}") CONFIG_OPTS+=("--with-poller=${POLLER}")
fi fi
if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
CONFIG_OPTS+=("--with-tls=yes")
fi
if [ -z $DRAFT ] || [ $DRAFT == "disabled" ]; then if [ -z $DRAFT ] || [ $DRAFT == "disabled" ]; then
CONFIG_OPTS+=("--enable-drafts=no") CONFIG_OPTS+=("--enable-drafts=no")
elif [ $DRAFT == "enabled" ]; then elif [ $DRAFT == "enabled" ]; then
......
...@@ -553,7 +553,7 @@ AM_CONDITIONAL(HAVE_CURVE, test "x$curve_library" != "x") ...@@ -553,7 +553,7 @@ AM_CONDITIONAL(HAVE_CURVE, test "x$curve_library" != "x")
AM_CONDITIONAL(USE_WEPOLL, test "$poller" = "wepoll") AM_CONDITIONAL(USE_WEPOLL, test "$poller" = "wepoll")
# Check requiring packages for WebSocket # Check requiring packages for WebSocket
sha1_library="" ws_crypto_library=""
AC_ARG_ENABLE([ws], AC_ARG_ENABLE([ws],
[AS_HELP_STRING([--disable-ws], [Disable WebSocket transport [default=no]])]) [AS_HELP_STRING([--disable-ws], [Disable WebSocket transport [default=no]])])
...@@ -561,28 +561,43 @@ AC_ARG_ENABLE([ws], ...@@ -561,28 +561,43 @@ AC_ARG_ENABLE([ws],
AC_ARG_WITH([nss], AC_ARG_WITH([nss],
[AS_HELP_STRING([--with-nss], [use nss instead of built-in sha1 [default=no]])]) [AS_HELP_STRING([--with-nss], [use nss instead of built-in sha1 [default=no]])])
AC_ARG_WITH([tls],
[AS_HELP_STRING([--with-tls], [Enable TLS (WSS transport) [default=no]])])
if test "x$enable_ws" != "xno"; then if test "x$enable_ws" != "xno"; then
if test "x$with_nss" = "xyes"; then if test "x$with_tls" = "xyes"; then
PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.1.4], [
ws_crypto_library="gnutls"
AC_DEFINE(ZMQ_USE_GNUTLS, [1], [Use GNUTLS for TLS])
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
AC_DEFINE(ZMQ_HAVE_WSS, [1], [WSS enabled])
AC_MSG_NOTICE(Using gnutls)
],[
AC_MSG_ERROR([GnuTLS is not installed. Install it, then run configure again])
])
elif test "x$with_nss" = "xyes"; then
PKG_CHECK_MODULES([NSS3], [nss], [ PKG_CHECK_MODULES([NSS3], [nss], [
PKGCFG_NAMES_PRIVATE="$PKGCFG_NAMES_PRIVATE nss" PKGCFG_NAMES_PRIVATE="$PKGCFG_NAMES_PRIVATE nss"
AC_DEFINE(ZMQ_USE_NSS, [1], [Using NSS]) AC_DEFINE(ZMQ_USE_NSS, [1], [Using NSS])
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket]) AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
sha1_library="nss" ws_crypto_library="nss"
AC_MSG_NOTICE(Using NSS) AC_MSG_NOTICE(Using NSS)
], [ ], [
AC_MSG_ERROR(nss is not installed. Install it, then run configure again) AC_MSG_ERROR([nss is not installed. Install it, then run configure again])
]) ])
else else
AC_DEFINE(ZMQ_USE_BUILTIN_SHA1, [1], [Using built-in sha1])
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket]) AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
sha1_library="builtin" AC_DEFINE(ZMQ_USE_BUILTIN_SHA1, [1], [Using built-in sha1])
AC_MSG_NOTICE(Using builting SHA1) AC_MSG_NOTICE(Using builting SHA1)
ws_crypto_library="builtin"
fi fi
fi fi
AM_CONDITIONAL(HAVE_WS, test "x$sha1_library" != "x") AM_CONDITIONAL(HAVE_WS, test "x$ws_crypto_library" != "x")
AM_CONDITIONAL(USE_NSS, test "x$sha1_library" = "xnss") AM_CONDITIONAL(USE_NSS, test "x$ws_crypto_library" = "xnss")
AM_CONDITIONAL(USE_BUILTIN_SHA1, test "x$sha1_library" = "xbuiltin") AM_CONDITIONAL(USE_BUILTIN_SHA1, test "x$ws_crypto_library" = "xbuiltin")
AM_CONDITIONAL(USE_GNUTLS, test "x$ws_crypto_library" = "xgnutls")
AM_CONDITIONAL(HAVE_WSS, test "x$ws_crypto_library" = "xgnutls")
# build using pgm # build using pgm
have_pgm_library="no" have_pgm_library="no"
......
...@@ -670,6 +670,12 @@ ZMQ_EXPORT void zmq_threadclose (void *thread_); ...@@ -670,6 +670,12 @@ ZMQ_EXPORT void zmq_threadclose (void *thread_);
#define ZMQ_SOCKS_PASSWORD 100 #define ZMQ_SOCKS_PASSWORD 100
#define ZMQ_IN_BATCH_SIZE 101 #define ZMQ_IN_BATCH_SIZE 101
#define ZMQ_OUT_BATCH_SIZE 102 #define ZMQ_OUT_BATCH_SIZE 102
#define ZMQ_WSS_KEY_PEM 103
#define ZMQ_WSS_CERT_PEM 104
#define ZMQ_WSS_TRUST_PEM 105
#define ZMQ_WSS_HOSTNAME 106
#define ZMQ_WSS_TRUST_SYSTEM 107
/* DRAFT Context options */ /* DRAFT Context options */
#define ZMQ_ZERO_COPY_RECV 10 #define ZMQ_ZERO_COPY_RECV 10
......
...@@ -68,6 +68,12 @@ zmq::address_t::~address_t () ...@@ -68,6 +68,12 @@ zmq::address_t::~address_t ()
} }
#endif #endif
#ifdef ZMQ_HAVE_WSS
else if (protocol == protocol_name::wss) {
LIBZMQ_DELETE (resolved.ws_addr);
}
#endif
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \ #if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS && !defined ZMQ_HAVE_VXWORKS
else if (protocol == protocol_name::ipc) { else if (protocol == protocol_name::ipc) {
...@@ -96,6 +102,10 @@ int zmq::address_t::to_string (std::string &addr_) const ...@@ -96,6 +102,10 @@ int zmq::address_t::to_string (std::string &addr_) const
if (protocol == protocol_name::ws && resolved.ws_addr) if (protocol == protocol_name::ws && resolved.ws_addr)
return resolved.ws_addr->to_string (addr_); return resolved.ws_addr->to_string (addr_);
#endif #endif
#ifdef ZMQ_HAVE_WSS
if (protocol == protocol_name::wss && resolved.ws_addr)
return resolved.ws_addr->to_string (addr_);
#endif
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \ #if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS && !defined ZMQ_HAVE_VXWORKS
if (protocol == protocol_name::ipc && resolved.ipc_addr) if (protocol == protocol_name::ipc && resolved.ipc_addr)
......
...@@ -64,6 +64,9 @@ static const char udp[] = "udp"; ...@@ -64,6 +64,9 @@ static const char udp[] = "udp";
#ifdef ZMQ_HAVE_WS #ifdef ZMQ_HAVE_WS
static const char ws[] = "ws"; static const char ws[] = "ws";
#endif #endif
#ifdef ZMQ_HAVE_WSS
static const char wss[] = "wss";
#endif
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \ #if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS && !defined ZMQ_HAVE_VXWORKS
static const char ipc[] = "ipc"; static const char ipc[] = "ipc";
......
...@@ -56,6 +56,10 @@ ...@@ -56,6 +56,10 @@
#include <nss.h> #include <nss.h>
#endif #endif
#ifdef ZMQ_USE_GNUTLS
#include <gnutls/gnutls.h>
#endif
#define ZMQ_CTX_TAG_VALUE_GOOD 0xabadcafe #define ZMQ_CTX_TAG_VALUE_GOOD 0xabadcafe
#define ZMQ_CTX_TAG_VALUE_BAD 0xdeadbeef #define ZMQ_CTX_TAG_VALUE_BAD 0xdeadbeef
...@@ -95,6 +99,10 @@ zmq::ctx_t::ctx_t () : ...@@ -95,6 +99,10 @@ zmq::ctx_t::ctx_t () :
#ifdef ZMQ_USE_NSS #ifdef ZMQ_USE_NSS
NSS_NoDB_Init (NULL); NSS_NoDB_Init (NULL);
#endif #endif
#ifdef ZMQ_USE_GNUTLS
gnutls_global_init ();
#endif
} }
bool zmq::ctx_t::check_tag () bool zmq::ctx_t::check_tag ()
...@@ -131,6 +139,10 @@ zmq::ctx_t::~ctx_t () ...@@ -131,6 +139,10 @@ zmq::ctx_t::~ctx_t ()
NSS_Shutdown (); NSS_Shutdown ();
#endif #endif
#ifdef ZMQ_USE_GNUTLS
gnutls_global_deinit ();
#endif
// Remove the tag, so that the object is considered dead. // Remove the tag, so that the object is considered dead.
_tag = ZMQ_CTX_TAG_VALUE_BAD; _tag = ZMQ_CTX_TAG_VALUE_BAD;
} }
......
...@@ -247,7 +247,8 @@ zmq::options_t::options_t () : ...@@ -247,7 +247,8 @@ zmq::options_t::options_t () :
out_batch_size (8192), out_batch_size (8192),
zero_copy (true), zero_copy (true),
router_notify (0), router_notify (0),
monitor_event_version (1) monitor_event_version (1),
wss_trust_system (false)
{ {
memset (curve_public_key, 0, CURVE_KEYSIZE); memset (curve_public_key, 0, CURVE_KEYSIZE);
memset (curve_secret_key, 0, CURVE_KEYSIZE); memset (curve_secret_key, 0, CURVE_KEYSIZE);
...@@ -784,6 +785,26 @@ int zmq::options_t::setsockopt (int option_, ...@@ -784,6 +785,26 @@ int zmq::options_t::setsockopt (int option_,
return 0; return 0;
} }
break; break;
case ZMQ_WSS_KEY_PEM:
// TODO: check if valid certificate
wss_key_pem = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_CERT_PEM:
// TODO: check if valid certificate
wss_cert_pem = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_TRUST_PEM:
// TODO: check if valid certificate
wss_trust_pem = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_HOSTNAME:
wss_hostname = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_TRUST_SYSTEM:
return do_setsockopt_int_as_bool_strict (optval_, optvallen_,
&wss_trust_system);
#endif #endif
default: default:
......
...@@ -286,6 +286,13 @@ struct options_t ...@@ -286,6 +286,13 @@ struct options_t
// Version of monitor events to emit // Version of monitor events to emit
int monitor_event_version; int monitor_event_version;
// WSS Keys
std::string wss_key_pem;
std::string wss_cert_pem;
std::string wss_trust_pem;
std::string wss_hostname;
bool wss_trust_system;
}; };
inline bool get_effective_conflate_option (const options_t &options) inline bool get_effective_conflate_option (const options_t &options)
......
...@@ -114,8 +114,14 @@ zmq::session_base_t::session_base_t (class io_thread_t *io_thread_, ...@@ -114,8 +114,14 @@ zmq::session_base_t::session_base_t (class io_thread_t *io_thread_,
_socket (socket_), _socket (socket_),
_io_thread (io_thread_), _io_thread (io_thread_),
_has_linger_timer (false), _has_linger_timer (false),
_addr (addr_) _addr (addr_),
_wss_hostname (NULL)
{ {
if (options_.wss_hostname.length () > 0) {
_wss_hostname = (char *) malloc (options_.wss_hostname.length () + 1);
assert (_wss_hostname);
strcpy (_wss_hostname, options_.wss_hostname.c_str ());
}
} }
const zmq::endpoint_uri_pair_t &zmq::session_base_t::get_endpoint () const const zmq::endpoint_uri_pair_t &zmq::session_base_t::get_endpoint () const
...@@ -138,6 +144,9 @@ zmq::session_base_t::~session_base_t () ...@@ -138,6 +144,9 @@ zmq::session_base_t::~session_base_t ()
if (_engine) if (_engine)
_engine->terminate (); _engine->terminate ();
if (_wss_hostname)
free (_wss_hostname);
LIBZMQ_DELETE (_addr); LIBZMQ_DELETE (_addr);
} }
...@@ -563,6 +572,10 @@ zmq::session_base_t::connecter_factory_entry_t ...@@ -563,6 +572,10 @@ zmq::session_base_t::connecter_factory_entry_t
connecter_factory_entry_t (protocol_name::ws, connecter_factory_entry_t (protocol_name::ws,
&zmq::session_base_t::create_connecter_ws), &zmq::session_base_t::create_connecter_ws),
#endif #endif
#ifdef ZMQ_HAVE_WSS
connecter_factory_entry_t (protocol_name::wss,
&zmq::session_base_t::create_connecter_wss),
#endif
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \ #if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS && !defined ZMQ_HAVE_VXWORKS
connecter_factory_entry_t (protocol_name::ipc, connecter_factory_entry_t (protocol_name::ipc,
...@@ -690,7 +703,16 @@ zmq::own_t *zmq::session_base_t::create_connecter_ws (io_thread_t *io_thread_, ...@@ -690,7 +703,16 @@ zmq::own_t *zmq::session_base_t::create_connecter_ws (io_thread_t *io_thread_,
bool wait_) bool wait_)
{ {
return new (std::nothrow) return new (std::nothrow)
ws_connecter_t (io_thread_, this, options, _addr, wait_); ws_connecter_t (io_thread_, this, options, _addr, wait_, false, NULL);
}
#endif
#ifdef ZMQ_HAVE_WSS
zmq::own_t *zmq::session_base_t::create_connecter_wss (io_thread_t *io_thread_,
bool wait_)
{
return new (std::nothrow) ws_connecter_t (io_thread_, this, options, _addr,
wait_, true, _wss_hostname);
} }
#endif #endif
......
...@@ -120,6 +120,7 @@ class session_base_t : public own_t, public io_object_t, public i_pipe_events ...@@ -120,6 +120,7 @@ class session_base_t : public own_t, public io_object_t, public i_pipe_events
own_t *create_connecter_ipc (io_thread_t *io_thread_, bool wait_); own_t *create_connecter_ipc (io_thread_t *io_thread_, bool wait_);
own_t *create_connecter_tcp (io_thread_t *io_thread_, bool wait_); own_t *create_connecter_tcp (io_thread_t *io_thread_, bool wait_);
own_t *create_connecter_ws (io_thread_t *io_thread_, bool wait_); own_t *create_connecter_ws (io_thread_t *io_thread_, bool wait_);
own_t *create_connecter_wss (io_thread_t *io_thread_, bool wait_);
typedef void (session_base_t::*start_connecting_fun_t) ( typedef void (session_base_t::*start_connecting_fun_t) (
io_thread_t *io_thread); io_thread_t *io_thread);
...@@ -191,6 +192,10 @@ class session_base_t : public own_t, public io_object_t, public i_pipe_events ...@@ -191,6 +192,10 @@ class session_base_t : public own_t, public io_object_t, public i_pipe_events
// Protocol and address to use when connecting. // Protocol and address to use when connecting.
address_t *_addr; address_t *_addr;
// TLS handshake, we need to take a copy when the session is created,
// in order to maintain the value at the creation time
char *_wss_hostname;
session_base_t (const session_base_t &); session_base_t (const session_base_t &);
const session_base_t &operator= (const session_base_t &); const session_base_t &operator= (const session_base_t &);
}; };
......
...@@ -338,6 +338,9 @@ int zmq::socket_base_t::check_protocol (const std::string &protocol_) const ...@@ -338,6 +338,9 @@ int zmq::socket_base_t::check_protocol (const std::string &protocol_) const
#ifdef ZMQ_HAVE_WS #ifdef ZMQ_HAVE_WS
&& protocol_ != protocol_name::ws && protocol_ != protocol_name::ws
#endif #endif
#ifdef ZMQ_HAVE_WSS
&& protocol_ != protocol_name::wss
#endif
#if defined ZMQ_HAVE_OPENPGM #if defined ZMQ_HAVE_OPENPGM
// pgm/epgm transports only available if 0MQ is compiled with OpenPGM. // pgm/epgm transports only available if 0MQ is compiled with OpenPGM.
&& protocol_ != "pgm" && protocol_ != "pgm"
...@@ -635,9 +638,15 @@ int zmq::socket_base_t::bind (const char *endpoint_uri_) ...@@ -635,9 +638,15 @@ int zmq::socket_base_t::bind (const char *endpoint_uri_)
} }
#ifdef ZMQ_HAVE_WS #ifdef ZMQ_HAVE_WS
#ifdef ZMQ_HAVE_WSS
if (protocol == protocol_name::ws || protocol == protocol_name::wss) {
ws_listener_t *listener = new (std::nothrow) ws_listener_t (
io_thread, this, options, protocol == protocol_name::wss);
#else
if (protocol == protocol_name::ws) { if (protocol == protocol_name::ws) {
ws_listener_t *listener = ws_listener_t *listener =
new (std::nothrow) ws_listener_t (io_thread, this, options); new (std::nothrow) ws_listener_t (io_thread, this, options, false);
#endif
alloc_assert (listener); alloc_assert (listener);
rc = listener->set_local_address (address.c_str ()); rc = listener->set_local_address (address.c_str ());
if (rc != 0) { if (rc != 0) {
...@@ -895,7 +904,11 @@ int zmq::socket_base_t::connect (const char *endpoint_uri_) ...@@ -895,7 +904,11 @@ int zmq::socket_base_t::connect (const char *endpoint_uri_)
paddr->resolved.tcp_addr = NULL; paddr->resolved.tcp_addr = NULL;
} }
#ifdef ZMQ_HAVE_WS #ifdef ZMQ_HAVE_WS
#ifdef ZMQ_HAVE_WSS
else if (protocol == protocol_name::ws || protocol == protocol_name::wss) {
#else
else if (protocol == protocol_name::ws) { else if (protocol == protocol_name::ws) {
#endif
paddr->resolved.ws_addr = new (std::nothrow) ws_address_t (); paddr->resolved.ws_addr = new (std::nothrow) ws_address_t ();
alloc_assert (paddr->resolved.ws_addr); alloc_assert (paddr->resolved.ws_addr);
rc = paddr->resolved.ws_addr->resolve (address.c_str (), false, rc = paddr->resolved.ws_addr->resolve (address.c_str (), false,
......
...@@ -275,14 +275,8 @@ bool zmq::stream_engine_base_t::in_event_internal () ...@@ -275,14 +275,8 @@ bool zmq::stream_engine_base_t::in_event_internal ()
size_t bufsize = 0; size_t bufsize = 0;
_decoder->get_buffer (&_inpos, &bufsize); _decoder->get_buffer (&_inpos, &bufsize);
const int rc = tcp_read (_inpos, bufsize); int rc = read (_inpos, bufsize);
if (rc == 0) {
// connection closed by peer
errno = EPIPE;
error (connection_error);
return false;
}
if (rc == -1) { if (rc == -1) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
error (connection_error); error (connection_error);
...@@ -370,7 +364,7 @@ void zmq::stream_engine_base_t::out_event () ...@@ -370,7 +364,7 @@ void zmq::stream_engine_base_t::out_event ()
// arbitrarily large. However, we assume that underlying TCP layer has // arbitrarily large. However, we assume that underlying TCP layer has
// limited transmission buffer and thus the actual number of bytes // limited transmission buffer and thus the actual number of bytes
// written should be reasonably modest. // written should be reasonably modest.
const int nbytes = tcp_write (_s, _outpos, _outsize); const int nbytes = write (_outpos, _outsize);
// IO error has occurred. We stop waiting for output events. // IO error has occurred. We stop waiting for output events.
// The engine is not terminated until we detect input error; // The engine is not terminated until we detect input error;
...@@ -744,7 +738,20 @@ void zmq::stream_engine_base_t::timer_event (int id_) ...@@ -744,7 +738,20 @@ void zmq::stream_engine_base_t::timer_event (int id_)
assert (false); assert (false);
} }
int zmq::stream_engine_base_t::tcp_read (void *data_, size_t size_) int zmq::stream_engine_base_t::read (void *data_, size_t size_)
{
int rc = zmq::tcp_read (_s, data_, size_);
if (rc == 0) {
// connection closed by peer
errno = EPIPE;
return -1;
}
return rc;
}
int zmq::stream_engine_base_t::write (const void *data_, size_t size_)
{ {
return zmq::tcp_read (_s, data_, size_); return zmq::tcp_write (_s, data_, size_);
} }
...@@ -90,7 +90,6 @@ class stream_engine_base_t : public io_object_t, public i_engine ...@@ -90,7 +90,6 @@ class stream_engine_base_t : public io_object_t, public i_engine
int decode_and_push (msg_t *msg_); int decode_and_push (msg_t *msg_);
void set_handshake_timer (); void set_handshake_timer ();
int tcp_read (void *data_, size_t size_);
virtual bool handshake () { return true; }; virtual bool handshake () { return true; };
virtual void plug_internal (){}; virtual void plug_internal (){};
...@@ -100,6 +99,9 @@ class stream_engine_base_t : public io_object_t, public i_engine ...@@ -100,6 +99,9 @@ class stream_engine_base_t : public io_object_t, public i_engine
virtual int process_heartbeat_message (msg_t *msg_) { return -1; }; virtual int process_heartbeat_message (msg_t *msg_) { return -1; };
virtual int produce_pong_message (msg_t *msg_) { return -1; }; virtual int produce_pong_message (msg_t *msg_) { return -1; };
virtual int read (void *data, size_t size_);
virtual int write (const void *data_, size_t size_);
void set_pollout () { io_object_t::set_pollout (_handle); } void set_pollout () { io_object_t::set_pollout (_handle); }
void set_pollin () { io_object_t::set_pollin (_handle); } void set_pollin () { io_object_t::set_pollin (_handle); }
session_base_t *session () { return _session; } session_base_t *session () { return _session; }
......
...@@ -42,6 +42,10 @@ ...@@ -42,6 +42,10 @@
#include "session_base.hpp" #include "session_base.hpp"
#include "ws_engine.hpp" #include "ws_engine.hpp"
#ifdef ZMQ_HAVE_WSS
#include "wss_engine.hpp"
#endif
#if !defined ZMQ_HAVE_WINDOWS #if !defined ZMQ_HAVE_WINDOWS
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
...@@ -67,12 +71,15 @@ zmq::ws_connecter_t::ws_connecter_t (class io_thread_t *io_thread_, ...@@ -67,12 +71,15 @@ zmq::ws_connecter_t::ws_connecter_t (class io_thread_t *io_thread_,
class session_base_t *session_, class session_base_t *session_,
const options_t &options_, const options_t &options_,
address_t *addr_, address_t *addr_,
bool delayed_start_) : bool delayed_start_,
bool wss_,
const char *tls_hostname_) :
stream_connecter_base_t ( stream_connecter_base_t (
io_thread_, session_, options_, addr_, delayed_start_), io_thread_, session_, options_, addr_, delayed_start_),
_connect_timer_started (false) _connect_timer_started (false),
_wss (wss_),
_hostname (tls_hostname_)
{ {
zmq_assert (_addr->protocol == protocol_name::ws);
} }
zmq::ws_connecter_t::~ws_connecter_t () zmq::ws_connecter_t::~ws_connecter_t ()
...@@ -264,8 +271,18 @@ void zmq::ws_connecter_t::create_engine (fd_t fd, ...@@ -264,8 +271,18 @@ void zmq::ws_connecter_t::create_engine (fd_t fd,
endpoint_type_connect); endpoint_type_connect);
// Create the engine object for this connection. // Create the engine object for this connection.
ws_engine_t *engine = new (std::nothrow) i_engine *engine = NULL;
ws_engine_t (fd, options, endpoint_pair, *_addr->resolved.ws_addr, true); if (_wss)
#ifdef ZMQ_HAVE_WSS
engine = new (std::nothrow)
wss_engine_t (fd, options, endpoint_pair, *_addr->resolved.ws_addr,
true, NULL, _hostname);
#else
assert (false);
#endif
else
engine = new (std::nothrow) ws_engine_t (
fd, options, endpoint_pair, *_addr->resolved.ws_addr, true);
alloc_assert (engine); alloc_assert (engine);
// Attach the engine to the corresponding session object. // Attach the engine to the corresponding session object.
......
...@@ -45,7 +45,9 @@ class ws_connecter_t : public stream_connecter_base_t ...@@ -45,7 +45,9 @@ class ws_connecter_t : public stream_connecter_base_t
zmq::session_base_t *session_, zmq::session_base_t *session_,
const options_t &options_, const options_t &options_,
address_t *addr_, address_t *addr_,
bool delayed_start_); bool delayed_start_,
bool wss_,
const char *tls_hostname_);
~ws_connecter_t (); ~ws_connecter_t ();
protected: protected:
...@@ -88,6 +90,9 @@ class ws_connecter_t : public stream_connecter_base_t ...@@ -88,6 +90,9 @@ class ws_connecter_t : public stream_connecter_base_t
ws_connecter_t (const ws_connecter_t &); ws_connecter_t (const ws_connecter_t &);
const ws_connecter_t &operator= (const ws_connecter_t &); const ws_connecter_t &operator= (const ws_connecter_t &);
bool _wss;
const char *_hostname;
}; };
} }
......
...@@ -33,8 +33,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. ...@@ -33,8 +33,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <secoid.h> #include <secoid.h>
#include <sechash.h> #include <sechash.h>
#define SHA_DIGEST_LENGTH 20 #define SHA_DIGEST_LENGTH 20
#else #elif defined ZMQ_USE_BUILTIN_SHA1
#include "../external/sha1/sha1.h" #include "../external/sha1/sha1.h"
#elif defined ZMQ_USE_GNUTLS
#define SHA_DIGEST_LENGTH 20
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#endif #endif
#if !defined ZMQ_HAVE_WINDOWS #if !defined ZMQ_HAVE_WINDOWS
...@@ -105,7 +109,7 @@ zmq::ws_engine_t::~ws_engine_t () ...@@ -105,7 +109,7 @@ zmq::ws_engine_t::~ws_engine_t ()
{ {
} }
void zmq::ws_engine_t::plug_internal () void zmq::ws_engine_t::start_ws_handshake ()
{ {
if (_client) { if (_client) {
unsigned char nonce[16]; unsigned char nonce[16];
...@@ -135,7 +139,11 @@ void zmq::ws_engine_t::plug_internal () ...@@ -135,7 +139,11 @@ void zmq::ws_engine_t::plug_internal ()
_outsize = size; _outsize = size;
set_pollout (); set_pollout ();
} }
}
void zmq::ws_engine_t::plug_internal ()
{
start_ws_handshake ();
set_pollin (); set_pollin ();
in_event (); in_event ();
} }
...@@ -147,6 +155,7 @@ int zmq::ws_engine_t::routing_id_msg (msg_t *msg_) ...@@ -147,6 +155,7 @@ int zmq::ws_engine_t::routing_id_msg (msg_t *msg_)
if (_options.routing_id_size > 0) if (_options.routing_id_size > 0)
memcpy (msg_->data (), _options.routing_id, _options.routing_id_size); memcpy (msg_->data (), _options.routing_id, _options.routing_id_size);
_next_msg = &ws_engine_t::pull_msg_from_session; _next_msg = &ws_engine_t::pull_msg_from_session;
return 0; return 0;
} }
...@@ -197,12 +206,8 @@ bool zmq::ws_engine_t::handshake () ...@@ -197,12 +206,8 @@ bool zmq::ws_engine_t::handshake ()
bool zmq::ws_engine_t::server_handshake () bool zmq::ws_engine_t::server_handshake ()
{ {
int nbytes = tcp_read (_read_buffer, WS_BUFFER_SIZE); int nbytes = read (_read_buffer, WS_BUFFER_SIZE);
if (nbytes == 0) { if (nbytes == -1) {
errno = EPIPE;
error (zmq::i_engine::connection_error);
return false;
} else if (nbytes == -1) {
if (errno != EAGAIN) if (errno != EAGAIN)
error (zmq::i_engine::connection_error); error (zmq::i_engine::connection_error);
return false; return false;
...@@ -470,12 +475,8 @@ bool zmq::ws_engine_t::server_handshake () ...@@ -470,12 +475,8 @@ bool zmq::ws_engine_t::server_handshake ()
bool zmq::ws_engine_t::client_handshake () bool zmq::ws_engine_t::client_handshake ()
{ {
int nbytes = tcp_read (_read_buffer, WS_BUFFER_SIZE); int nbytes = read (_read_buffer, WS_BUFFER_SIZE);
if (nbytes == 0) { if (nbytes == -1) {
errno = EPIPE;
error (zmq::i_engine::connection_error);
return false;
} else if (nbytes == -1) {
if (errno != EAGAIN) if (errno != EAGAIN)
error (zmq::i_engine::connection_error); error (zmq::i_engine::connection_error);
return false; return false;
...@@ -868,12 +869,20 @@ static void compute_accept_key (char *key, unsigned char *hash) ...@@ -868,12 +869,20 @@ static void compute_accept_key (char *key, unsigned char *hash)
(unsigned int) strlen (magic_string)); (unsigned int) strlen (magic_string));
HASH_End (ctx, hash, &len, SHA_DIGEST_LENGTH); HASH_End (ctx, hash, &len, SHA_DIGEST_LENGTH);
HASH_Destroy (ctx); HASH_Destroy (ctx);
#else #elif defined ZMQ_USE_BUILTIN_SHA1
sha1_ctxt ctx; sha1_ctxt ctx;
SHA1_Init (&ctx); SHA1_Init (&ctx);
SHA1_Update (&ctx, (unsigned char *) key, strlen (key)); SHA1_Update (&ctx, (unsigned char *) key, strlen (key));
SHA1_Update (&ctx, (unsigned char *) magic_string, strlen (magic_string)); SHA1_Update (&ctx, (unsigned char *) magic_string, strlen (magic_string));
SHA1_Final (hash, &ctx); SHA1_Final (hash, &ctx);
#elif defined ZMQ_USE_GNUTLS
gnutls_hash_hd_t hd;
gnutls_hash_init (&hd, GNUTLS_DIG_SHA1);
gnutls_hash (hd, key, strlen (key));
gnutls_hash (hd, magic_string, strlen (magic_string));
gnutls_hash_deinit (hd, hash);
#else
#error "No sha1 implementation set"
#endif #endif
} }
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
#include "stream_engine_base.hpp" #include "stream_engine_base.hpp"
#include "ws_address.hpp" #include "ws_address.hpp"
#define WS_BUFFER_SIZE 8192 #define WS_BUFFER_SIZE 8192
#define MAX_HEADER_NAME_LENGTH 1024 #define MAX_HEADER_NAME_LENGTH 1024
#define MAX_HEADER_VALUE_LENGTH 2048 #define MAX_HEADER_VALUE_LENGTH 2048
...@@ -138,6 +137,7 @@ class ws_engine_t : public stream_engine_base_t ...@@ -138,6 +137,7 @@ class ws_engine_t : public stream_engine_base_t
protected: protected:
bool handshake (); bool handshake ();
void plug_internal (); void plug_internal ();
void start_ws_handshake ();
private: private:
int routing_id_msg (msg_t *msg_); int routing_id_msg (msg_t *msg_);
......
...@@ -44,6 +44,10 @@ ...@@ -44,6 +44,10 @@
#include "ws_engine.hpp" #include "ws_engine.hpp"
#include "session_base.hpp" #include "session_base.hpp"
#ifdef ZMQ_HAVE_WSS
#include "wss_engine.hpp"
#endif
#ifndef ZMQ_HAVE_WINDOWS #ifndef ZMQ_HAVE_WINDOWS
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
...@@ -63,9 +67,33 @@ ...@@ -63,9 +67,33 @@
zmq::ws_listener_t::ws_listener_t (io_thread_t *io_thread_, zmq::ws_listener_t::ws_listener_t (io_thread_t *io_thread_,
socket_base_t *socket_, socket_base_t *socket_,
const options_t &options_) : const options_t &options_,
stream_listener_base_t (io_thread_, socket_, options_) bool wss_) :
stream_listener_base_t (io_thread_, socket_, options_),
_wss (wss_)
{
#ifdef ZMQ_HAVE_WSS
if (_wss) {
int rc = gnutls_certificate_allocate_credentials (&_tls_cred);
assert (rc == GNUTLS_E_SUCCESS);
gnutls_datum_t cert = {(unsigned char *) options_.wss_cert_pem.c_str (),
(unsigned int) options_.wss_cert_pem.length ()};
gnutls_datum_t key = {(unsigned char *) options_.wss_key_pem.c_str (),
(unsigned int) options_.wss_key_pem.length ()};
rc = gnutls_certificate_set_x509_key_mem (_tls_cred, &cert, &key,
GNUTLS_X509_FMT_PEM);
assert (rc == GNUTLS_E_SUCCESS);
}
#endif
}
zmq::ws_listener_t::~ws_listener_t ()
{ {
#ifdef ZMQ_HAVE_WSS
if (_wss)
gnutls_certificate_free_credentials (_tls_cred);
#endif
} }
void zmq::ws_listener_t::in_event () void zmq::ws_listener_t::in_event ()
...@@ -256,8 +284,17 @@ void zmq::ws_listener_t::create_engine (fd_t fd) ...@@ -256,8 +284,17 @@ void zmq::ws_listener_t::create_engine (fd_t fd)
get_socket_name (fd, socket_end_local), get_socket_name (fd, socket_end_local),
get_socket_name (fd, socket_end_remote), endpoint_type_bind); get_socket_name (fd, socket_end_remote), endpoint_type_bind);
ws_engine_t *engine = new (std::nothrow) i_engine *engine = NULL;
ws_engine_t (fd, options, endpoint_pair, _address, false); if (_wss)
#ifdef ZMQ_HAVE_WSS
engine = new (std::nothrow) wss_engine_t (
fd, options, endpoint_pair, _address, false, _tls_cred, NULL);
#else
assert (false);
#endif
else
engine = new (std::nothrow)
ws_engine_t (fd, options, endpoint_pair, _address, false);
alloc_assert (engine); alloc_assert (engine);
// Choose I/O thread to run connecter in. Given that we are already // Choose I/O thread to run connecter in. Given that we are already
......
...@@ -34,6 +34,10 @@ ...@@ -34,6 +34,10 @@
#include "ws_address.hpp" #include "ws_address.hpp"
#include "stream_listener_base.hpp" #include "stream_listener_base.hpp"
#if ZMQ_USE_GNUTLS
#include <gnutls/gnutls.h>
#endif
namespace zmq namespace zmq
{ {
class ws_listener_t : public stream_listener_base_t class ws_listener_t : public stream_listener_base_t
...@@ -41,7 +45,10 @@ class ws_listener_t : public stream_listener_base_t ...@@ -41,7 +45,10 @@ class ws_listener_t : public stream_listener_base_t
public: public:
ws_listener_t (zmq::io_thread_t *io_thread_, ws_listener_t (zmq::io_thread_t *io_thread_,
zmq::socket_base_t *socket_, zmq::socket_base_t *socket_,
const options_t &options_); const options_t &options_,
bool wss_);
~ws_listener_t ();
// Set address to listen on. // Set address to listen on.
int set_local_address (const char *addr_); int set_local_address (const char *addr_);
...@@ -67,6 +74,11 @@ class ws_listener_t : public stream_listener_base_t ...@@ -67,6 +74,11 @@ class ws_listener_t : public stream_listener_base_t
ws_listener_t (const ws_listener_t &); ws_listener_t (const ws_listener_t &);
const ws_listener_t &operator= (const ws_listener_t &); const ws_listener_t &operator= (const ws_listener_t &);
bool _wss;
#if ZMQ_HAVE_WSS
gnutls_certificate_credentials_t _tls_cred;
#endif
}; };
} }
......
/*
Copyright (c) 2007-2019 Contributors as noted in the AUTHORS file
This file is part of libzmq, the ZeroMQ core engine in C++.
libzmq is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
As a special exception, the Contributors give you permission to link
this library with independent modules to produce an executable,
regardless of the license terms of these independent modules, and to
copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the
terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library.
If you modify this library, you must extend this exception to your
version of the library.
libzmq is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.hpp"
#include "wss_engine.hpp"
static int verify_certificate_callback (gnutls_session_t session)
{
unsigned int status;
const char *hostname;
// read hostname
hostname = (const char *) gnutls_session_get_ptr (session);
int rc = gnutls_certificate_verify_peers3 (session, hostname, &status);
assert (rc >= 0);
if (status != 0) {
// TODO: somehow log the error
// Certificate is not trusted
return GNUTLS_E_CERTIFICATE_ERROR;
}
// notify gnutls to continue handshake normally
return 0;
}
zmq::wss_engine_t::wss_engine_t (fd_t fd_,
const options_t &options_,
const endpoint_uri_pair_t &endpoint_uri_pair_,
ws_address_t &address_,
bool client_,
void *tls_server_cred_,
const char *hostname_) :
ws_engine_t (fd_, options_, endpoint_uri_pair_, address_, client_),
_established (false),
_tls_client_cred (NULL)
{
int rc = 0;
if (client_) {
// TODO: move to session_base, to allow changing the socket options between connect calls
rc = gnutls_certificate_allocate_credentials (&_tls_client_cred);
assert (rc == 0);
if (options_.wss_trust_system)
gnutls_certificate_set_x509_system_trust (_tls_client_cred);
if (options_.wss_trust_pem.length () > 0) {
gnutls_datum_t trust = {
(unsigned char *) options_.wss_trust_pem.c_str (),
(unsigned int) options_.wss_trust_pem.length ()};
rc = gnutls_certificate_set_x509_trust_mem (
_tls_client_cred, &trust, GNUTLS_X509_FMT_PEM);
assert (rc >= 0);
}
gnutls_certificate_set_verify_function (_tls_client_cred,
verify_certificate_callback);
rc = gnutls_init (&_tls_session, GNUTLS_CLIENT | GNUTLS_NONBLOCK);
assert (rc == GNUTLS_E_SUCCESS);
if (hostname_)
gnutls_server_name_set (_tls_session, GNUTLS_NAME_DNS, hostname_,
strlen (hostname_));
gnutls_session_set_ptr (_tls_session, (void *) hostname_);
rc = gnutls_credentials_set (_tls_session, GNUTLS_CRD_CERTIFICATE,
_tls_client_cred);
assert (rc == GNUTLS_E_SUCCESS);
} else {
assert (tls_server_cred_);
rc = gnutls_init (&_tls_session, GNUTLS_SERVER | GNUTLS_NONBLOCK);
assert (rc == GNUTLS_E_SUCCESS);
rc = gnutls_credentials_set (_tls_session, GNUTLS_CRD_CERTIFICATE,
tls_server_cred_);
assert (rc == GNUTLS_E_SUCCESS);
}
gnutls_set_default_priority (_tls_session);
gnutls_transport_set_int (_tls_session, fd_);
gnutls_handshake_set_timeout (_tls_session,
GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
}
zmq::wss_engine_t::~wss_engine_t ()
{
gnutls_deinit (_tls_session);
if (_tls_client_cred)
gnutls_certificate_free_credentials (_tls_client_cred);
}
void zmq::wss_engine_t::plug_internal ()
{
set_pollin ();
in_event ();
}
bool zmq::wss_engine_t::handshake ()
{
if (!_established) {
int rc = gnutls_handshake (_tls_session);
// TODO: when E_AGAIN is returned we might need to call gnutls_handshake for out_event as well, see gnutls_record_get_direction
if (rc == GNUTLS_E_SUCCESS) {
start_ws_handshake ();
_established = true;
return false;
} else if (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED
|| rc == GNUTLS_E_WARNING_ALERT_RECEIVED)
return false;
else {
error (zmq::i_engine::connection_error);
return false;
}
}
return ws_engine_t::handshake ();
}
int zmq::wss_engine_t::read (void *data_, size_t size_)
{
ssize_t rc = gnutls_record_recv (_tls_session, data_, size_);
if (rc == GNUTLS_E_REHANDSHAKE) {
gnutls_alert_send (_tls_session, GNUTLS_AL_WARNING,
GNUTLS_A_NO_RENEGOTIATION);
return 0;
}
if (rc == GNUTLS_E_INTERRUPTED) {
errno = EINTR;
return -1;
}
if (rc == GNUTLS_E_AGAIN) {
errno = EAGAIN;
return -1;
}
// TODO: change return type to ssize_t (signed)
return rc;
}
int zmq::wss_engine_t::write (const void *data_, size_t size_)
{
ssize_t rc = gnutls_record_send (_tls_session, data_, size_);
if (rc == GNUTLS_E_INTERRUPTED) {
errno = EINTR;
return -1;
}
if (rc == GNUTLS_E_AGAIN) {
errno = EAGAIN;
return -1;
}
// TODO: change return type to ssize_t (signed)
return rc;
}
/*
Copyright (c) 2007-2019 Contributors as noted in the AUTHORS file
This file is part of libzmq, the ZeroMQ core engine in C++.
libzmq is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
As a special exception, the Contributors give you permission to link
this library with independent modules to produce an executable,
regardless of the license terms of these independent modules, and to
copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the
terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library.
If you modify this library, you must extend this exception to your
version of the library.
libzmq is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_WSS_ENGINE_HPP_INCLUDED__
#define __ZMQ_WSS_ENGINE_HPP_INCLUDED__
#include <gnutls/gnutls.h>
#include "ws_engine.hpp"
#define WSS_BUFFER_SIZE 8192
namespace zmq
{
class wss_engine_t : public ws_engine_t
{
public:
wss_engine_t (fd_t fd_,
const options_t &options_,
const endpoint_uri_pair_t &endpoint_uri_pair_,
ws_address_t &address_,
bool client_,
void *tls_server_cred_,
const char *hostname_);
~wss_engine_t ();
protected:
bool handshake ();
void plug_internal ();
int read (void *data, size_t size_);
int write (const void *data_, size_t size_);
private:
bool _established;
gnutls_certificate_credentials_t _tls_client_cred;
gnutls_session_t _tls_session;
};
}
#endif
...@@ -148,13 +148,8 @@ int zmq::zmtp_engine_t::receive_greeting () ...@@ -148,13 +148,8 @@ int zmq::zmtp_engine_t::receive_greeting ()
{ {
bool unversioned = false; bool unversioned = false;
while (_greeting_bytes_read < _greeting_size) { while (_greeting_bytes_read < _greeting_size) {
const int n = tcp_read (_greeting_recv + _greeting_bytes_read, const int n = read (_greeting_recv + _greeting_bytes_read,
_greeting_size - _greeting_bytes_read); _greeting_size - _greeting_bytes_read);
if (n == 0) {
errno = EPIPE;
error (connection_error);
return -1;
}
if (n == -1) { if (n == -1) {
if (errno != EAGAIN) if (errno != EAGAIN)
error (connection_error); error (connection_error);
......
/*
Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
This file is part of libzmq, the ZeroMQ core engine in C++.
libzmq is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
As a special exception, the Contributors give you permission to link
this library with independent modules to produce an executable,
regardless of the license terms of these independent modules, and to
copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the
terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library.
If you modify this library, you must extend this exception to your
version of the library.
libzmq is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "testutil.hpp"
#include "testutil_unity.hpp"
SETUP_TEARDOWN_TESTCONTEXT
const char *key =
"-----BEGIN PRIVATE KEY-----\n"
"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\n"
"NX3nbNTMU7FmPoUgF8jm+hS9ecfWVf9c\n"
"-----END PRIVATE KEY-----";
const char *cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIFjzCCA3egAwIBAgIUMVYFTVoSsqvKRSfeaBZfD0WlDhkwDQYJKoZIhvcNAQEL\n"
"BQAwVzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE\n"
"CgwTRGVmYXVsdCBDb21wYW55IEx0ZDETMBEGA1UEAwwKemVyb21xLm9yZzAeFw0x\n"
"OTEwMDExMTEyMjVaFw0xOTEwMzExMTEyMjVaMFcxCzAJBgNVBAYTAlhYMRUwEwYD\n"
"VQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQx\n"
"EzARBgNVBAMMCnplcm9tcS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
"AoICAQCrXKFPWrRqbdNoltLhL8YYva5au+f3ntrOybMJmhWJdkXL1DxC5F6XDNNz\n"
"YET+WTrBpwfY1yX6OYZwBpnh9K/Kb00wJTkd6MxeqEV2eTp7iAt/BzmWNXIausyk\n"
"XuBuWq+M+oFXXlTbgFXLlqV8/B+1klxFuSjNxbDN+IBLgz7k86F2sAa6RoZ2jHWs\n"
"DmmnPHHUX7XZbs8IgBcwL112Z7QvX/0x/JQFn5ulxWlkvYsgunjebSaR2gQeKFZ8\n"
"f3E8J6bgUk2INEafKndzRX/hpZ0Q0g0R4DPTcSDSXdRb6do5Fgre/qhiKkRHqQPY\n"
"1aIZTG0byG60vtDacJ+NGM21hP0BtGxg+/ES1TdMSdmz9LaCGaV3ydoEC6qz+eXW\n"
"Id5jL82D7ywft679GQtmq8/iOwb6Z5sz76Xu8aiBtwYWrW3tlRfvPS/0nxHYLPhQ\n"
"1RJZKWW0EFUtar7aizde3fKDkUI1CvWUfLN5SrvA3TMHQCsbqXNRKMXRBW4M0jel\n"
"z1cy67l9IMa5vVzzjvxCdIyHjE1k5MzKhQgCihDYn0QdEuEbGjOE3sU/g22K2t33\n"
"QvDGGnwH3yoNvjgLQ/IEwcixrPm1cxQYmYhYrt5MPn4sNxVw3ne/LgRfRdIyCmGN\n"
"MN7QHG0s1ciTqxaKJPUyUDAoqOy9t17oKk0HVmp6YjzY2NEIkwIDAQABo1MwUTAd\n"
"BgNVHQ4EFgQUjU31StK8ffuKYL6IfYmCuHvgzr4wHwYDVR0jBBgwFoAUjU31StK8\n"
"ffuKYL6IfYmCuHvgzr4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n"
"AgEAPv4vOG5C3HXlPe+fRPqtR28cpjNddJWgFRkYxp5vntN6mVrswkUzD/a8sZ6t\n"
"ly4bGgQPGjshCOLvlRQera+XxBMB2kafWL+2YiRsLRl0eCeTY04Rn3MUVFwuet9/\n"
"gk1Xh3j5dMPh3oAo9ZVT+/rYc9376YDYm5IPxZXPEA/huXc8iK8NXCWoUvkYMimC\n"
"x3dzyyW2hp3mJEjOQS8jSayZfsS/UjhV0KYwDPKdjUbHYR7hGqLrEXjIBz5ee8On\n"
"9olSYvZ7/TGIzZTSZXYUx9mbq763OTMjRGLTVj+fD0rsa5Toz4TXsOjzppS8cqL9\n"
"kzNmUG6qVpO4Q/+wKgfeUy6HqxGSxFqH6W0QdQP1rTtBTayhSdHppH5Dupx+7S4p\n"
"pmaL6k535DlFnFQZjIXIqGnP/oXwIjn5la66EqdU0fPLprH4sqVXAM032swyHFop\n"
"RIM6NV8u0fRjQXqyJDktMXPUYNaV+rwXbtImjVaoelK8LvSwzKc6NLEEHgsa3HMO\n"
"6z93LtCk+ocCrAABQor1S/fAq5TpL6btaUzAi2qfj5yWgZZnt7LkxLp+tHXtfif+\n"
"E/XAbpLYzkzTYi50IgEBkS1sjT5IOK9Yr0al2tDcQFGpS25SOz7BhCfnj3+MBD//\n"
"m4Y13hEvpYRBDnfhXCvwD9/wd6Xq1wA+lueDpwWbrfriTJo=\n"
"-----END CERTIFICATE-----";
void test_roundtrip ()
{
void *sb = test_context_socket (ZMQ_REP);
zmq_setsockopt (sb, ZMQ_WSS_CERT_PEM, cert, strlen (cert));
zmq_setsockopt (sb, ZMQ_WSS_KEY_PEM, key, strlen (key));
TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (sb, "wss://*:5556/roundtrip"));
void *sc = test_context_socket (ZMQ_REQ);
zmq_setsockopt (sc, ZMQ_WSS_TRUST_PEM, cert, strlen (cert));
zmq_setsockopt (sc, ZMQ_WSS_HOSTNAME, "zeromq.org", strlen ("zeromq.org"));
TEST_ASSERT_SUCCESS_ERRNO (
zmq_connect (sc, "wss://127.0.0.1:5556/roundtrip"));
bounce (sb, sc);
test_context_socket_close (sc);
test_context_socket_close (sb);
}
int main ()
{
setup_test_environment ();
UNITY_BEGIN ();
RUN_TEST (test_roundtrip);
return UNITY_END ();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment