Problem: CLIENT and SERVER don't check SNDMORE

These sockets don't handle multipart data, so if callers send it,
they drop frames, and things break silently.

Solution: if the caller tries to use ZMQ_SNDMORE, return -1 and
set errno to EINVAL.
This commit is contained in:
Pieter Hintjens
2015-12-29 17:00:06 +01:00
parent ae3b2734df
commit 2566c02a9e
8 changed files with 131 additions and 111 deletions

View File

@@ -96,7 +96,7 @@
if (thread_safe) \
sync.lock();
#define EXIT_MUTEX() \
#define EXIT_MUTEX(); \
if (thread_safe) \
sync.unlock();
@@ -329,24 +329,24 @@ void zmq::socket_base_t::attach_pipe (pipe_t *pipe_, bool subscribe_to_all_)
int zmq::socket_base_t::setsockopt (int option_, const void *optval_,
size_t optvallen_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
if (!options.is_valid(option_)) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
if (unlikely (ctx_terminated)) {
errno = ETERM;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// First, check whether specific socket type overloads the option.
int rc = xsetsockopt (option_, optval_, optvallen_);
if (rc == 0 || errno != EINVAL) {
EXIT_MUTEX();
EXIT_MUTEX ();
return rc;
}
@@ -355,64 +355,64 @@ int zmq::socket_base_t::setsockopt (int option_, const void *optval_,
rc = options.setsockopt (option_, optval_, optvallen_);
update_pipe_options(option_);
EXIT_MUTEX();
EXIT_MUTEX ();
return rc;
}
int zmq::socket_base_t::getsockopt (int option_, void *optval_,
size_t *optvallen_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
if (unlikely (ctx_terminated)) {
errno = ETERM;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
if (option_ == ZMQ_RCVMORE) {
if (*optvallen_ < sizeof (int)) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
memset(optval_, 0, *optvallen_);
*((int*) optval_) = rcvmore ? 1 : 0;
*optvallen_ = sizeof (int);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
if (option_ == ZMQ_FD) {
if (*optvallen_ < sizeof (fd_t)) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
if (thread_safe) {
// thread safe socket doesn't provide file descriptor
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
*((fd_t*)optval_) = ((mailbox_t*)mailbox)->get_fd();
*optvallen_ = sizeof(fd_t);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
if (option_ == ZMQ_EVENTS) {
if (*optvallen_ < sizeof (int)) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
int rc = process_commands (0, false);
if (rc != 0 && (errno == EINTR || errno == ETERM)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
errno_assert (rc == 0);
@@ -422,86 +422,86 @@ int zmq::socket_base_t::getsockopt (int option_, void *optval_,
if (has_in ())
*((int*) optval_) |= ZMQ_POLLIN;
*optvallen_ = sizeof (int);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
if (option_ == ZMQ_LAST_ENDPOINT) {
if (*optvallen_ < last_endpoint.size () + 1) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
strcpy (static_cast <char *> (optval_), last_endpoint.c_str ());
*optvallen_ = last_endpoint.size () + 1;
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
if (option_ == ZMQ_THREAD_SAFE) {
if (*optvallen_ < sizeof (int)) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
memset(optval_, 0, *optvallen_);
*((int*) optval_) = thread_safe ? 1 : 0;
*optvallen_ = sizeof (int);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
int rc = options.getsockopt (option_, optval_, optvallen_);
EXIT_MUTEX();
EXIT_MUTEX ();
return rc;
}
int zmq::socket_base_t::add_signaler(signaler_t *s_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
if (!thread_safe) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
((mailbox_safe_t*)mailbox)->add_signaler(s_);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
int zmq::socket_base_t::remove_signaler(signaler_t *s_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
if (!thread_safe) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
((mailbox_safe_t*)mailbox)->remove_signaler(s_);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
int zmq::socket_base_t::bind (const char *addr_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
if (unlikely (ctx_terminated)) {
errno = ETERM;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Process pending commands, if any.
int rc = process_commands (0, false);
if (unlikely (rc != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -509,7 +509,7 @@ int zmq::socket_base_t::bind (const char *addr_)
std::string protocol;
std::string address;
if (parse_uri (addr_, protocol, address) || check_protocol (protocol)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -521,14 +521,14 @@ int zmq::socket_base_t::bind (const char *addr_)
last_endpoint.assign (addr_);
options.connected = true;
}
EXIT_MUTEX();
EXIT_MUTEX ();
return rc;
}
if (protocol == "pgm" || protocol == "epgm" || protocol == "norm") {
// For convenience's sake, bind can be used interchangeable with
// connect for PGM, EPGM and NORM transports.
EXIT_MUTEX();
EXIT_MUTEX ();
rc = connect (addr_);
if (rc != -1)
options.connected = true;
@@ -540,7 +540,7 @@ int zmq::socket_base_t::bind (const char *addr_)
io_thread_t *io_thread = choose_io_thread (options.affinity);
if (!io_thread) {
errno = EMTHREAD;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -552,7 +552,7 @@ int zmq::socket_base_t::bind (const char *addr_)
if (rc != 0) {
LIBZMQ_DELETE(listener);
event_bind_failed (address, zmq_errno());
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -561,7 +561,7 @@ int zmq::socket_base_t::bind (const char *addr_)
add_endpoint (last_endpoint.c_str (), (own_t *) listener, NULL);
options.connected = true;
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
@@ -574,7 +574,7 @@ int zmq::socket_base_t::bind (const char *addr_)
if (rc != 0) {
LIBZMQ_DELETE(listener);
event_bind_failed (address, zmq_errno());
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -583,7 +583,7 @@ int zmq::socket_base_t::bind (const char *addr_)
add_endpoint (last_endpoint.c_str (), (own_t *) listener, NULL);
options.connected = true;
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
#endif
@@ -596,7 +596,7 @@ int zmq::socket_base_t::bind (const char *addr_)
if (rc != 0) {
LIBZMQ_DELETE(listener);
event_bind_failed (address, zmq_errno());
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -605,7 +605,7 @@ int zmq::socket_base_t::bind (const char *addr_)
add_endpoint (addr_, (own_t *) listener, NULL);
options.connected = true;
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
#endif
@@ -618,7 +618,7 @@ int zmq::socket_base_t::bind (const char *addr_)
if (rc != 0) {
LIBZMQ_DELETE(listener);
event_bind_failed (address, zmq_errno ());
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -626,30 +626,30 @@ int zmq::socket_base_t::bind (const char *addr_)
add_endpoint (last_endpoint.c_str(), (own_t *) listener, NULL);
options.connected = true;
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
#endif
EXIT_MUTEX();
EXIT_MUTEX ();
zmq_assert (false);
return -1;
}
int zmq::socket_base_t::connect (const char *addr_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
if (unlikely (ctx_terminated)) {
errno = ETERM;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Process pending commands, if any.
int rc = process_commands (0, false);
if (unlikely (rc != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -657,7 +657,7 @@ int zmq::socket_base_t::connect (const char *addr_)
std::string protocol;
std::string address;
if (parse_uri (addr_, protocol, address) || check_protocol (protocol)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -763,7 +763,7 @@ int zmq::socket_base_t::connect (const char *addr_)
inprocs.insert (inprocs_t::value_type (std::string (addr_), new_pipes [0]));
options.connected = true;
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
bool is_single_connect = (options.type == ZMQ_DEALER ||
@@ -775,7 +775,7 @@ int zmq::socket_base_t::connect (const char *addr_)
// There is no valid use for multiple connects for SUB-PUB nor
// DEALER-ROUTER nor REQ-REP. Multiple connects produces
// nonsensical results.
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
}
@@ -784,7 +784,7 @@ int zmq::socket_base_t::connect (const char *addr_)
io_thread_t *io_thread = choose_io_thread (options.affinity);
if (!io_thread) {
errno = EMTHREAD;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -827,7 +827,7 @@ int zmq::socket_base_t::connect (const char *addr_)
if (rc == -1) {
errno = EINVAL;
LIBZMQ_DELETE(paddr);
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Defer resolution until a socket is opened
@@ -841,7 +841,7 @@ int zmq::socket_base_t::connect (const char *addr_)
int rc = paddr->resolved.ipc_addr->resolve (address.c_str ());
if (rc != 0) {
LIBZMQ_DELETE(paddr);
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
}
@@ -857,7 +857,7 @@ int zmq::socket_base_t::connect (const char *addr_)
if (res != NULL)
pgm_freeaddrinfo (res);
if (rc != 0 || port_number == 0) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
}
@@ -870,7 +870,7 @@ int zmq::socket_base_t::connect (const char *addr_)
int rc = paddr->resolved.tipc_addr->resolve (address.c_str());
if (rc != 0) {
LIBZMQ_DELETE(paddr);
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
}
@@ -883,7 +883,7 @@ int zmq::socket_base_t::connect (const char *addr_)
int rc = paddr->resolved.vmci_addr->resolve (address.c_str ());
if (rc != 0) {
LIBZMQ_DELETE(paddr);
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
}
@@ -929,7 +929,7 @@ int zmq::socket_base_t::connect (const char *addr_)
paddr->to_string (last_endpoint);
add_endpoint (addr_, (own_t *) session, newpipe);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
@@ -942,19 +942,19 @@ void zmq::socket_base_t::add_endpoint (const char *addr_, own_t *endpoint_, pipe
int zmq::socket_base_t::term_endpoint (const char *addr_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
// Check whether the library haven't been shut down yet.
if (unlikely (ctx_terminated)) {
errno = ETERM;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Check whether endpoint address passed to the function is valid.
if (unlikely (!addr_)) {
errno = EINVAL;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -962,7 +962,7 @@ int zmq::socket_base_t::term_endpoint (const char *addr_)
// (from launch_child() for example) we're asked to terminate now.
int rc = process_commands (0, false);
if (unlikely(rc != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -970,27 +970,27 @@ int zmq::socket_base_t::term_endpoint (const char *addr_)
std::string protocol;
std::string address;
if (parse_uri(addr_, protocol, address) || check_protocol(protocol)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Disconnect an inproc socket
if (protocol == "inproc") {
if (unregister_endpoint (std::string(addr_), this) == 0) {
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
std::pair <inprocs_t::iterator, inprocs_t::iterator> range = inprocs.equal_range (std::string (addr_));
if (range.first == range.second) {
errno = ENOENT;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
for (inprocs_t::iterator it = range.first; it != range.second; ++it)
it->second->terminate (true);
inprocs.erase (range.first, range.second);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
@@ -998,7 +998,7 @@ int zmq::socket_base_t::term_endpoint (const char *addr_)
std::pair <endpoints_t::iterator, endpoints_t::iterator> range = endpoints.equal_range (std::string (addr_));
if (range.first == range.second) {
errno = ENOENT;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -1009,32 +1009,32 @@ int zmq::socket_base_t::term_endpoint (const char *addr_)
term_child (it->second.first);
}
endpoints.erase (range.first, range.second);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
int zmq::socket_base_t::send (msg_t *msg_, int flags_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
// Check whether the library haven't been shut down yet.
if (unlikely (ctx_terminated)) {
errno = ETERM;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Check whether message passed to the function is valid.
if (unlikely (!msg_ || !msg_->check ())) {
errno = EFAULT;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Process pending commands, if any.
int rc = process_commands (0, true);
if (unlikely (rc != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -1047,21 +1047,21 @@ int zmq::socket_base_t::send (msg_t *msg_, int flags_)
msg_->reset_metadata ();
// Try to send the message.
// Try to send the message using method in each socket class
rc = xsend (msg_);
if (rc == 0) {
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
if (unlikely (errno != EAGAIN)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// In case of non-blocking send we'll simply propagate
// the error - including EAGAIN - up the stack.
if (flags_ & ZMQ_DONTWAIT || options.sndtimeo == 0) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -1075,45 +1075,45 @@ int zmq::socket_base_t::send (msg_t *msg_, int flags_)
// If timeout is reached in the meantime, return EAGAIN.
while (true) {
if (unlikely (process_commands (timeout, false) != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
rc = xsend (msg_);
if (rc == 0)
break;
if (unlikely (errno != EAGAIN)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
if (timeout > 0) {
timeout = (int) (end - clock.now_ms ());
if (timeout <= 0) {
errno = EAGAIN;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
}
}
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
{
ENTER_MUTEX();
ENTER_MUTEX ();
// Check whether the library haven't been shut down yet.
if (unlikely (ctx_terminated)) {
errno = ETERM;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
// Check whether message passed to the function is valid.
if (unlikely (!msg_ || !msg_->check ())) {
errno = EFAULT;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -1127,7 +1127,7 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
// ticks is more efficient than doing RDTSC all the time.
if (++ticks == inbound_poll_rate) {
if (unlikely (process_commands (0, false) != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
ticks = 0;
@@ -1136,7 +1136,7 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
// Get the message.
int rc = xrecv (msg_);
if (unlikely (rc != 0 && errno != EAGAIN)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
@@ -1145,7 +1145,7 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
if (file_desc != retired_fd)
msg_->set_fd(file_desc);
extract_flags (msg_);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
@@ -1155,21 +1155,21 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
// If it's not, return EAGAIN.
if (flags_ & ZMQ_DONTWAIT || options.rcvtimeo == 0) {
if (unlikely (process_commands (0, false) != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
ticks = 0;
rc = xrecv (msg_);
if (rc < 0) {
EXIT_MUTEX();
EXIT_MUTEX ();
return rc;
}
if (file_desc != retired_fd)
msg_->set_fd(file_desc);
extract_flags (msg_);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
@@ -1183,7 +1183,7 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
bool block = (ticks != 0);
while (true) {
if (unlikely (process_commands (block ? timeout : 0, false) != 0)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
rc = xrecv (msg_);
@@ -1192,7 +1192,7 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
break;
}
if (unlikely (errno != EAGAIN)) {
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
block = true;
@@ -1200,7 +1200,7 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
timeout = (int) (end - clock.now_ms ());
if (timeout <= 0) {
errno = EAGAIN;
EXIT_MUTEX();
EXIT_MUTEX ();
return -1;
}
}
@@ -1209,7 +1209,7 @@ int zmq::socket_base_t::recv (msg_t *msg_, int flags_)
if (file_desc != retired_fd)
msg_->set_fd(file_desc);
extract_flags (msg_);
EXIT_MUTEX();
EXIT_MUTEX ();
return 0;
}
@@ -1246,7 +1246,7 @@ void zmq::socket_base_t::start_reaping (poller_t *poller_)
if (!thread_safe)
fd = ((mailbox_t*)mailbox)->get_fd();
else {
ENTER_MUTEX();
ENTER_MUTEX ();
reaper_signaler = new signaler_t();
@@ -1257,7 +1257,7 @@ void zmq::socket_base_t::start_reaping (poller_t *poller_)
// Send a signal to make sure reaper handle existing commands
reaper_signaler->send();
EXIT_MUTEX();
EXIT_MUTEX ();
}
handle = poller->add_fd (fd, this);
@@ -1426,15 +1426,14 @@ void zmq::socket_base_t::in_event ()
// of the reaper thread. Process any commands from other threads/sockets
// that may be available at the moment. Ultimately, the socket will
// be destroyed.
ENTER_MUTEX();
ENTER_MUTEX ();
// If the socket is thread safe we need to unsignal the reaper signaler
if (thread_safe)
reaper_signaler->recv();
process_commands (0, false);
EXIT_MUTEX();
EXIT_MUTEX ();
check_destroy ();
}