mirror of
https://github.com/zeromq/libzmq.git
synced 2024-12-13 10:52:56 +01:00
problem: unsecured websocket is rarely used in production
Solution: support websocket with tls (wss)
This commit is contained in:
parent
8fe620901f
commit
7296fb5b15
1
.gitignore
vendored
1
.gitignore
vendored
@ -153,6 +153,7 @@ test_xpub_verbose
|
||||
test_mock_pub_sub
|
||||
test_proxy_hwm
|
||||
test_ws_transport
|
||||
test_wss_transport
|
||||
unittest_ip_resolver
|
||||
unittest_mtrie
|
||||
unittest_poller
|
||||
|
11
.travis.yml
11
.travis.yml
@ -50,12 +50,14 @@ matrix:
|
||||
apt:
|
||||
packages:
|
||||
- lcov
|
||||
- env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled
|
||||
- env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled TLS=enabled
|
||||
os: linux
|
||||
dist: xenial
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- valgrind
|
||||
- libgnutls-dev
|
||||
- env: BUILD_TYPE=default CURVE=libsodium GSSAPI=enabled PGM=enabled NORM=enabled
|
||||
os: linux
|
||||
addons:
|
||||
@ -70,6 +72,13 @@ matrix:
|
||||
- libsodium-dev
|
||||
- asciidoc
|
||||
- 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
|
||||
os: linux
|
||||
addons:
|
||||
|
21
Makefile.am
21
Makefile.am
@ -305,6 +305,12 @@ src_libzmq_la_SOURCES += \
|
||||
external/sha1/sha1.h
|
||||
endif
|
||||
|
||||
if HAVE_WSS
|
||||
src_libzmq_la_SOURCES += \
|
||||
src/wss_engine.cpp \
|
||||
src/wss_engine.hpp
|
||||
endif
|
||||
|
||||
if ON_MINGW
|
||||
src_libzmq_la_LDFLAGS = \
|
||||
-no-undefined \
|
||||
@ -342,11 +348,16 @@ src_libzmq_la_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ $(CODE_COVERAGE_CXXFLAGS) \
|
||||
$(LIBUNWIND_CFLAGS)
|
||||
src_libzmq_la_LIBADD = $(CODE_COVERAGE_LDFLAGS) $(LIBUNWIND_LIBS)
|
||||
|
||||
if HAVE_WS
|
||||
if USE_NSS
|
||||
src_libzmq_la_CPPFLAGS += ${NSS3_CFLAGS}
|
||||
src_libzmq_la_LIBADD += ${NSS3_LIBS}
|
||||
endif
|
||||
|
||||
if USE_GNUTLS
|
||||
src_libzmq_la_CPPFLAGS += ${GNUTLS_CFLAGS}
|
||||
src_libzmq_la_LIBADD += ${GNUTLS_LIBS}
|
||||
endif
|
||||
|
||||
if USE_LIBSODIUM
|
||||
src_libzmq_la_CPPFLAGS += ${sodium_CFLAGS}
|
||||
src_libzmq_la_LIBADD += ${sodium_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}
|
||||
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_CYGWIN
|
||||
test_apps += \
|
||||
|
@ -14,6 +14,10 @@ CONFIG_OPTS+=("PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig")
|
||||
CONFIG_OPTS+=("--prefix=${BUILD_PREFIX}")
|
||||
CONFIG_OPTS+=("--enable-valgrind")
|
||||
|
||||
if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
|
||||
CONFIG_OPTS+=("--with-tls=yes")
|
||||
fi
|
||||
|
||||
if [ -z $CURVE ]; then
|
||||
CONFIG_OPTS+=("--disable-curve")
|
||||
elif [ $CURVE == "libsodium" ]; then
|
||||
|
@ -60,6 +60,10 @@ if [ $BUILD_TYPE == "default" ]; then
|
||||
CONFIG_OPTS+=("--with-poller=${POLLER}")
|
||||
fi
|
||||
|
||||
if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
|
||||
CONFIG_OPTS+=("--with-tls=yes")
|
||||
fi
|
||||
|
||||
if [ -z $DRAFT ] || [ $DRAFT == "disabled" ]; then
|
||||
CONFIG_OPTS+=("--enable-drafts=no")
|
||||
elif [ $DRAFT == "enabled" ]; then
|
||||
|
33
configure.ac
33
configure.ac
@ -553,7 +553,7 @@ AM_CONDITIONAL(HAVE_CURVE, test "x$curve_library" != "x")
|
||||
AM_CONDITIONAL(USE_WEPOLL, test "$poller" = "wepoll")
|
||||
|
||||
# Check requiring packages for WebSocket
|
||||
sha1_library=""
|
||||
ws_crypto_library=""
|
||||
|
||||
AC_ARG_ENABLE([ws],
|
||||
[AS_HELP_STRING([--disable-ws], [Disable WebSocket transport [default=no]])])
|
||||
@ -561,28 +561,43 @@ AC_ARG_ENABLE([ws],
|
||||
AC_ARG_WITH([nss],
|
||||
[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$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], [
|
||||
PKGCFG_NAMES_PRIVATE="$PKGCFG_NAMES_PRIVATE nss"
|
||||
AC_DEFINE(ZMQ_USE_NSS, [1], [Using NSS])
|
||||
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
|
||||
sha1_library="nss"
|
||||
ws_crypto_library="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
|
||||
AC_DEFINE(ZMQ_USE_BUILTIN_SHA1, [1], [Using built-in sha1])
|
||||
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)
|
||||
ws_crypto_library="builtin"
|
||||
fi
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_WS, test "x$sha1_library" != "x")
|
||||
AM_CONDITIONAL(USE_NSS, test "x$sha1_library" = "xnss")
|
||||
AM_CONDITIONAL(USE_BUILTIN_SHA1, test "x$sha1_library" = "xbuiltin")
|
||||
AM_CONDITIONAL(HAVE_WS, test "x$ws_crypto_library" != "x")
|
||||
AM_CONDITIONAL(USE_NSS, test "x$ws_crypto_library" = "xnss")
|
||||
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
|
||||
have_pgm_library="no"
|
||||
|
@ -670,6 +670,12 @@ ZMQ_EXPORT void zmq_threadclose (void *thread_);
|
||||
#define ZMQ_SOCKS_PASSWORD 100
|
||||
#define ZMQ_IN_BATCH_SIZE 101
|
||||
#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 */
|
||||
#define ZMQ_ZERO_COPY_RECV 10
|
||||
|
@ -68,6 +68,12 @@ zmq::address_t::~address_t ()
|
||||
}
|
||||
#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 \
|
||||
&& !defined ZMQ_HAVE_VXWORKS
|
||||
else if (protocol == protocol_name::ipc) {
|
||||
@ -96,6 +102,10 @@ int zmq::address_t::to_string (std::string &addr_) const
|
||||
if (protocol == protocol_name::ws && resolved.ws_addr)
|
||||
return resolved.ws_addr->to_string (addr_);
|
||||
#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 \
|
||||
&& !defined ZMQ_HAVE_VXWORKS
|
||||
if (protocol == protocol_name::ipc && resolved.ipc_addr)
|
||||
|
@ -64,6 +64,9 @@ static const char udp[] = "udp";
|
||||
#ifdef ZMQ_HAVE_WS
|
||||
static const char ws[] = "ws";
|
||||
#endif
|
||||
#ifdef ZMQ_HAVE_WSS
|
||||
static const char wss[] = "wss";
|
||||
#endif
|
||||
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
|
||||
&& !defined ZMQ_HAVE_VXWORKS
|
||||
static const char ipc[] = "ipc";
|
||||
|
12
src/ctx.cpp
12
src/ctx.cpp
@ -56,6 +56,10 @@
|
||||
#include <nss.h>
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_USE_GNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#endif
|
||||
|
||||
#define ZMQ_CTX_TAG_VALUE_GOOD 0xabadcafe
|
||||
#define ZMQ_CTX_TAG_VALUE_BAD 0xdeadbeef
|
||||
|
||||
@ -95,6 +99,10 @@ zmq::ctx_t::ctx_t () :
|
||||
#ifdef ZMQ_USE_NSS
|
||||
NSS_NoDB_Init (NULL);
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_USE_GNUTLS
|
||||
gnutls_global_init ();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool zmq::ctx_t::check_tag ()
|
||||
@ -131,6 +139,10 @@ zmq::ctx_t::~ctx_t ()
|
||||
NSS_Shutdown ();
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_USE_GNUTLS
|
||||
gnutls_global_deinit ();
|
||||
#endif
|
||||
|
||||
// Remove the tag, so that the object is considered dead.
|
||||
_tag = ZMQ_CTX_TAG_VALUE_BAD;
|
||||
}
|
||||
|
@ -247,7 +247,8 @@ zmq::options_t::options_t () :
|
||||
out_batch_size (8192),
|
||||
zero_copy (true),
|
||||
router_notify (0),
|
||||
monitor_event_version (1)
|
||||
monitor_event_version (1),
|
||||
wss_trust_system (false)
|
||||
{
|
||||
memset (curve_public_key, 0, CURVE_KEYSIZE);
|
||||
memset (curve_secret_key, 0, CURVE_KEYSIZE);
|
||||
@ -784,6 +785,26 @@ int zmq::options_t::setsockopt (int option_,
|
||||
return 0;
|
||||
}
|
||||
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
|
||||
|
||||
default:
|
||||
|
@ -286,6 +286,13 @@ struct options_t
|
||||
|
||||
// Version of monitor events to emit
|
||||
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)
|
||||
|
@ -114,8 +114,14 @@ zmq::session_base_t::session_base_t (class io_thread_t *io_thread_,
|
||||
_socket (socket_),
|
||||
_io_thread (io_thread_),
|
||||
_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
|
||||
@ -138,6 +144,9 @@ zmq::session_base_t::~session_base_t ()
|
||||
if (_engine)
|
||||
_engine->terminate ();
|
||||
|
||||
if (_wss_hostname)
|
||||
free (_wss_hostname);
|
||||
|
||||
LIBZMQ_DELETE (_addr);
|
||||
}
|
||||
|
||||
@ -563,6 +572,10 @@ zmq::session_base_t::connecter_factory_entry_t
|
||||
connecter_factory_entry_t (protocol_name::ws,
|
||||
&zmq::session_base_t::create_connecter_ws),
|
||||
#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 \
|
||||
&& !defined ZMQ_HAVE_VXWORKS
|
||||
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_,
|
||||
bool wait_)
|
||||
{
|
||||
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
|
||||
|
||||
|
@ -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_tcp (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) (
|
||||
io_thread_t *io_thread);
|
||||
@ -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.
|
||||
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 &);
|
||||
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
|
||||
#ifdef ZMQ_HAVE_WS
|
||||
&& protocol_ != protocol_name::ws
|
||||
#endif
|
||||
#ifdef ZMQ_HAVE_WSS
|
||||
&& protocol_ != protocol_name::wss
|
||||
#endif
|
||||
#if defined ZMQ_HAVE_OPENPGM
|
||||
// pgm/epgm transports only available if 0MQ is compiled with OpenPGM.
|
||||
&& protocol_ != "pgm"
|
||||
@ -635,9 +638,15 @@ int zmq::socket_base_t::bind (const char *endpoint_uri_)
|
||||
}
|
||||
|
||||
#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) {
|
||||
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);
|
||||
rc = listener->set_local_address (address.c_str ());
|
||||
if (rc != 0) {
|
||||
@ -895,7 +904,11 @@ int zmq::socket_base_t::connect (const char *endpoint_uri_)
|
||||
paddr->resolved.tcp_addr = NULL;
|
||||
}
|
||||
#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) {
|
||||
#endif
|
||||
paddr->resolved.ws_addr = new (std::nothrow) ws_address_t ();
|
||||
alloc_assert (paddr->resolved.ws_addr);
|
||||
rc = paddr->resolved.ws_addr->resolve (address.c_str (), false,
|
||||
|
@ -275,14 +275,8 @@ bool zmq::stream_engine_base_t::in_event_internal ()
|
||||
size_t bufsize = 0;
|
||||
_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 (errno != EAGAIN) {
|
||||
error (connection_error);
|
||||
@ -370,7 +364,7 @@ void zmq::stream_engine_base_t::out_event ()
|
||||
// arbitrarily large. However, we assume that underlying TCP layer has
|
||||
// limited transmission buffer and thus the actual number of bytes
|
||||
// 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.
|
||||
// The engine is not terminated until we detect input error;
|
||||
@ -744,7 +738,20 @@ void zmq::stream_engine_base_t::timer_event (int id_)
|
||||
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_)
|
||||
{
|
||||
return zmq::tcp_read (_s, data_, 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_write (_s, data_, size_);
|
||||
}
|
||||
|
@ -90,7 +90,6 @@ class stream_engine_base_t : public io_object_t, public i_engine
|
||||
int decode_and_push (msg_t *msg_);
|
||||
|
||||
void set_handshake_timer ();
|
||||
int tcp_read (void *data_, size_t size_);
|
||||
|
||||
virtual bool handshake () { return true; };
|
||||
virtual void plug_internal (){};
|
||||
@ -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 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_pollin () { io_object_t::set_pollin (_handle); }
|
||||
session_base_t *session () { return _session; }
|
||||
|
@ -42,6 +42,10 @@
|
||||
#include "session_base.hpp"
|
||||
#include "ws_engine.hpp"
|
||||
|
||||
#ifdef ZMQ_HAVE_WSS
|
||||
#include "wss_engine.hpp"
|
||||
#endif
|
||||
|
||||
#if !defined ZMQ_HAVE_WINDOWS
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
@ -67,12 +71,15 @@ zmq::ws_connecter_t::ws_connecter_t (class io_thread_t *io_thread_,
|
||||
class session_base_t *session_,
|
||||
const options_t &options_,
|
||||
address_t *addr_,
|
||||
bool delayed_start_) :
|
||||
bool delayed_start_,
|
||||
bool wss_,
|
||||
const char *tls_hostname_) :
|
||||
stream_connecter_base_t (
|
||||
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 ()
|
||||
@ -264,8 +271,18 @@ void zmq::ws_connecter_t::create_engine (fd_t fd,
|
||||
endpoint_type_connect);
|
||||
|
||||
// Create the engine object for this connection.
|
||||
ws_engine_t *engine = new (std::nothrow)
|
||||
ws_engine_t (fd, options, endpoint_pair, *_addr->resolved.ws_addr, true);
|
||||
i_engine *engine = NULL;
|
||||
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);
|
||||
|
||||
// Attach the engine to the corresponding session object.
|
||||
|
@ -45,7 +45,9 @@ class ws_connecter_t : public stream_connecter_base_t
|
||||
zmq::session_base_t *session_,
|
||||
const options_t &options_,
|
||||
address_t *addr_,
|
||||
bool delayed_start_);
|
||||
bool delayed_start_,
|
||||
bool wss_,
|
||||
const char *tls_hostname_);
|
||||
~ws_connecter_t ();
|
||||
|
||||
protected:
|
||||
@ -88,6 +90,9 @@ class ws_connecter_t : public stream_connecter_base_t
|
||||
|
||||
ws_connecter_t (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/>.
|
||||
#include <secoid.h>
|
||||
#include <sechash.h>
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#else
|
||||
#elif defined ZMQ_USE_BUILTIN_SHA1
|
||||
#include "../external/sha1/sha1.h"
|
||||
#elif defined ZMQ_USE_GNUTLS
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#endif
|
||||
|
||||
#if !defined ZMQ_HAVE_WINDOWS
|
||||
@ -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) {
|
||||
unsigned char nonce[16];
|
||||
@ -135,7 +139,11 @@ void zmq::ws_engine_t::plug_internal ()
|
||||
_outsize = size;
|
||||
set_pollout ();
|
||||
}
|
||||
}
|
||||
|
||||
void zmq::ws_engine_t::plug_internal ()
|
||||
{
|
||||
start_ws_handshake ();
|
||||
set_pollin ();
|
||||
in_event ();
|
||||
}
|
||||
@ -147,6 +155,7 @@ int zmq::ws_engine_t::routing_id_msg (msg_t *msg_)
|
||||
if (_options.routing_id_size > 0)
|
||||
memcpy (msg_->data (), _options.routing_id, _options.routing_id_size);
|
||||
_next_msg = &ws_engine_t::pull_msg_from_session;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -197,12 +206,8 @@ bool zmq::ws_engine_t::handshake ()
|
||||
|
||||
bool zmq::ws_engine_t::server_handshake ()
|
||||
{
|
||||
int nbytes = tcp_read (_read_buffer, WS_BUFFER_SIZE);
|
||||
if (nbytes == 0) {
|
||||
errno = EPIPE;
|
||||
error (zmq::i_engine::connection_error);
|
||||
return false;
|
||||
} else if (nbytes == -1) {
|
||||
int nbytes = read (_read_buffer, WS_BUFFER_SIZE);
|
||||
if (nbytes == -1) {
|
||||
if (errno != EAGAIN)
|
||||
error (zmq::i_engine::connection_error);
|
||||
return false;
|
||||
@ -470,12 +475,8 @@ bool zmq::ws_engine_t::server_handshake ()
|
||||
|
||||
bool zmq::ws_engine_t::client_handshake ()
|
||||
{
|
||||
int nbytes = tcp_read (_read_buffer, WS_BUFFER_SIZE);
|
||||
if (nbytes == 0) {
|
||||
errno = EPIPE;
|
||||
error (zmq::i_engine::connection_error);
|
||||
return false;
|
||||
} else if (nbytes == -1) {
|
||||
int nbytes = read (_read_buffer, WS_BUFFER_SIZE);
|
||||
if (nbytes == -1) {
|
||||
if (errno != EAGAIN)
|
||||
error (zmq::i_engine::connection_error);
|
||||
return false;
|
||||
@ -868,12 +869,20 @@ static void compute_accept_key (char *key, unsigned char *hash)
|
||||
(unsigned int) strlen (magic_string));
|
||||
HASH_End (ctx, hash, &len, SHA_DIGEST_LENGTH);
|
||||
HASH_Destroy (ctx);
|
||||
#else
|
||||
#elif defined ZMQ_USE_BUILTIN_SHA1
|
||||
sha1_ctxt ctx;
|
||||
SHA1_Init (&ctx);
|
||||
SHA1_Update (&ctx, (unsigned char *) key, strlen (key));
|
||||
SHA1_Update (&ctx, (unsigned char *) magic_string, strlen (magic_string));
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "stream_engine_base.hpp"
|
||||
#include "ws_address.hpp"
|
||||
|
||||
|
||||
#define WS_BUFFER_SIZE 8192
|
||||
#define MAX_HEADER_NAME_LENGTH 1024
|
||||
#define MAX_HEADER_VALUE_LENGTH 2048
|
||||
@ -138,6 +137,7 @@ class ws_engine_t : public stream_engine_base_t
|
||||
protected:
|
||||
bool handshake ();
|
||||
void plug_internal ();
|
||||
void start_ws_handshake ();
|
||||
|
||||
private:
|
||||
int routing_id_msg (msg_t *msg_);
|
||||
|
@ -44,6 +44,10 @@
|
||||
#include "ws_engine.hpp"
|
||||
#include "session_base.hpp"
|
||||
|
||||
#ifdef ZMQ_HAVE_WSS
|
||||
#include "wss_engine.hpp"
|
||||
#endif
|
||||
|
||||
#ifndef ZMQ_HAVE_WINDOWS
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
@ -63,9 +67,33 @@
|
||||
|
||||
zmq::ws_listener_t::ws_listener_t (io_thread_t *io_thread_,
|
||||
socket_base_t *socket_,
|
||||
const options_t &options_) :
|
||||
stream_listener_base_t (io_thread_, socket_, options_)
|
||||
const options_t &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 ()
|
||||
@ -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_remote), endpoint_type_bind);
|
||||
|
||||
ws_engine_t *engine = new (std::nothrow)
|
||||
ws_engine_t (fd, options, endpoint_pair, _address, false);
|
||||
i_engine *engine = NULL;
|
||||
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);
|
||||
|
||||
// Choose I/O thread to run connecter in. Given that we are already
|
||||
|
@ -34,6 +34,10 @@
|
||||
#include "ws_address.hpp"
|
||||
#include "stream_listener_base.hpp"
|
||||
|
||||
#if ZMQ_USE_GNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#endif
|
||||
|
||||
namespace zmq
|
||||
{
|
||||
class ws_listener_t : public stream_listener_base_t
|
||||
@ -41,7 +45,10 @@ class ws_listener_t : public stream_listener_base_t
|
||||
public:
|
||||
ws_listener_t (zmq::io_thread_t *io_thread_,
|
||||
zmq::socket_base_t *socket_,
|
||||
const options_t &options_);
|
||||
const options_t &options_,
|
||||
bool wss_);
|
||||
|
||||
~ws_listener_t ();
|
||||
|
||||
// Set address to listen on.
|
||||
int set_local_address (const char *addr_);
|
||||
@ -67,6 +74,11 @@ class ws_listener_t : public stream_listener_base_t
|
||||
|
||||
ws_listener_t (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
|
||||
};
|
||||
}
|
||||
|
||||
|
194
src/wss_engine.cpp
Normal file
194
src/wss_engine.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
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;
|
||||
}
|
66
src/wss_engine.hpp
Normal file
66
src/wss_engine.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
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 ()
|
||||
{
|
||||
bool unversioned = false;
|
||||
while (_greeting_bytes_read < _greeting_size) {
|
||||
const int n = tcp_read (_greeting_recv + _greeting_bytes_read,
|
||||
_greeting_size - _greeting_bytes_read);
|
||||
if (n == 0) {
|
||||
errno = EPIPE;
|
||||
error (connection_error);
|
||||
return -1;
|
||||
}
|
||||
const int n = read (_greeting_recv + _greeting_bytes_read,
|
||||
_greeting_size - _greeting_bytes_read);
|
||||
if (n == -1) {
|
||||
if (errno != EAGAIN)
|
||||
error (connection_error);
|
||||
|
150
tests/test_wss_transport.cpp
Normal file
150
tests/test_wss_transport.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
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 ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user