diff --git a/3rd/libzmq/AUTHORS b/3rd/libzmq/AUTHORS new file mode 100644 index 00000000..42b865fa --- /dev/null +++ b/3rd/libzmq/AUTHORS @@ -0,0 +1,152 @@ +Corporate Contributors +====================== + +Copyright (c) 2007-2014 iMatix Corporation +Copyright (c) 2009-2011 250bpm s.r.o. +Copyright (c) 2010-2011 Miru Limited +Copyright (c) 2011 VMware, Inc. +Copyright (c) 2012 Spotify AB +Copyright (c) 2013 Ericsson AB +Copyright (c) 2014 AppDynamics Inc. +Copyright (c) 2015 Google, Inc. +Copyright (c) 2015-2016 Brocade Communications Systems Inc. + +Individual Contributors +======================= + +AJ Lewis +Alexej Lotz +Andrew Thompson +André Caron +Asko Kauppi +Attila Mark +Barak Amar +Ben Gray +Bernd Melchers +Bernd Prager +Bob Beaty +Brandon Carpenter +Brett Cameron +Brian Buchanan +Burak Arslan +Carl Clemens +Chia-liang Kao +Chris Busbey +Chris Rempel +Chris Wong +Christian Gudrian +Christian Kamm +Chuck Remes +Conrad D. Steenberg +Constantin Rack +Daniel J. Bernstein +Dhammika Pathirana +Dhruva Krishnamurthy +Dirk O. Kaar +Doron Somech +Douglas Creager +Drew Crawford +Erich Heine +Erik Hugne +Erik Rigtorp +Fabien Ninoles +Frank Denis +George Neill +Gerard Toonstra +Ghislain Putois +Gonzalo Diethelm +Guido Goldstein +Harald Achitz +Hardeep Singh +Hiten Pandya +Ian Barber +Ilja Golshtein +Ilya Kulakov +Ivo Danihelka +Jacob Rideout +Joe Thornber +Jon Dyte +Kamil Shakirov +Ken Steele +Kouhei Sutou +Laurent Alebarde +Leonardo J. Consoni +Lionel Flandrin +Lourens Naudé +Luca Boccassi +Marc Rossi +Mark Barbisan +Martin Hurton +Martin Lucina +Martin Pales +Martin Sustrik +Matus Hamorsky +Max Wolf +McClain Looney +Michael Compton +Mika Fischer +Mikael Helbo Kjaer +Mike Gatny +Mikko Koppanen +Min Ragan-Kelley +Neale Ferguson +Nir Soffer +Osiris Pedroso +Paul Betts +Paul Colomiets +Pavel Gushcha +Pavol Malosek +Perry Kundert +Peter Bourgon +Philip Kovacs +Pieter Hintjens +Piotr Trojanek +Reza Ebrahimi +Richard Newton +Rik van der Heijden +Robert G. Jakabosky +Sebastian Otaegui +Stefan Radomski +Steven McCoy +Stuart Webster +Tamara Kustarova +Taras Shpot +Tero Marttila +Terry Wilson +Thijs Terlouw +Thomas Rodgers +Tim Mossbarger +Toralf Wittner +Tore Halvorsen +Trevor Bernard +Vitaly Mayatskikh + +Credits +======= + +Aamir Mohammad +Adrian von Bidder +Aleksey Yeschenko +Alessio Spadaro +Alexander Majorov +Anh Vu +Bernd Schumacher +Brian Granger +Carsten Dinkelmann +David Bahi +Dirk Eddelbuettel +Evgueny Khartchenko +Frank Vanden Berghen +Ian Barber +John Apps +Markus Fischer +Matt Muggeridge +Michael Santy +Oleg Sevostyanov +Paulo Henrique Silva +Peter Busser +Peter Lemenkov +Robert Zhang +Toralf Wittner +Zed Shaw + diff --git a/3rd/libzmq/CMakeLists.txt b/3rd/libzmq/CMakeLists.txt new file mode 100644 index 00000000..2e3320a2 --- /dev/null +++ b/3rd/libzmq/CMakeLists.txt @@ -0,0 +1,1813 @@ +# CMake build script for ZeroMQ +project(ZeroMQ) + +if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) + cmake_minimum_required(VERSION 3.0.2) +else() + cmake_minimum_required(VERSION 2.8.12) +endif() + +include(CheckIncludeFiles) +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) +include(CheckLibraryExists) +include(CheckCSourceCompiles) +include(CheckCSourceRuns) +include(CMakeDependentOption) +include(CheckCXXSymbolExists) +include(CheckTypeSize) +include(FindThreads) +include(GNUInstallDirs) +include(CheckTypeSize) +include(CMakePackageConfigHelpers) + +list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}") +set(ZMQ_CMAKE_MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/builds/cmake/Modules) +list(APPEND CMAKE_MODULE_PATH ${ZMQ_CMAKE_MODULES_DIR}) + +include(TestZMQVersion) +include(ZMQSourceRunChecks) +include(ZMQSupportMacros) + +find_package(PkgConfig) + +# Set lists to empty beforehand as to not accidentally take values from parent +set(sources) +set(cxx-sources) +set(html-docs) +set(target_outputs) + +option(ENABLE_ASAN "Build with address sanitizer" OFF) +if(ENABLE_ASAN) + message(STATUS "Instrumenting with Address Sanitizer") + set(CMAKE_BUILD_TYPE "RelWithDebInfo") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address -fsanitize-address-use-after-scope") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address -fsanitize-address-use-after-scope") +endif() + +# NOTE: Running libzmq under TSAN doesn't make much sense -- synchronization in libzmq is to some extent +# handled by the code "knowing" what threads are allowed to do, rather than by enforcing those +# restrictions, so TSAN generates a lot of (presumably) false positives from libzmq. +# The settings below are intended to enable libzmq to be built with minimal support for TSAN +# such that it can be used along with other code that is also built with TSAN. +option(ENABLE_TSAN "Build with thread sanitizer" OFF) +if(ENABLE_TSAN) + message(STATUS "Instrumenting with Thread Sanitizer") + set(CMAKE_BUILD_TYPE "RelWithDebInfo") + set(TSAN_FLAGS "-fno-omit-frame-pointer -fsanitize=thread") + set(TSAN_CCFLAGS "${TSAN_CCFLAGS} -mllvm -tsan-instrument-memory-accesses=0") + set(TSAN_CCFLAGS "${TSAN_CCFLAGS} -mllvm -tsan-instrument-atomics=0") + set(TSAN_CCFLAGS "${TSAN_CCFLAGS} -mllvm -tsan-instrument-func-entry-exit=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TSAN_FLAGS} ${TSAN_CCFLAGS} -fPIE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TSAN_FLAGS} ${TSAN_CCFLAGS} -fPIE") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TSAN_FLAGS} -pie -Qunused-arguments") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TSAN_FLAGS} -pie -Qunused-arguments") +endif() + +option(ENABLE_UBSAN "Build with undefined behavior sanitizer" OFF) +if(ENABLE_UBSAN) + message(STATUS "Instrumenting with Undefined Behavior Sanitizer") + set(CMAKE_BUILD_TYPE "RelWithDebInfo") + set(UBSAN_FLAGS "${UBSAN_FLAGS} -fno-omit-frame-pointer") + set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=undefined") + set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=implicit-conversion") + set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=implicit-integer-truncation") + set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=integer") + set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=nullability") + set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=vptr") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${UBSAN_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${UBSAN_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${UBSAN_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${UBSAN_FLAGS}") +endif() + +option(ENABLE_INTRINSICS "Build using compiler intrinsics for atomic ops" OFF) +if(ENABLE_INTRINSICS) + message(STATUS "Using compiler intrinsics for atomic ops") + add_definitions(-DZMQ_HAVE_ATOMIC_INTRINSICS) +endif() + +set(ZMQ_OUTPUT_BASENAME + "zmq" + CACHE STRING "Output zmq library base name") + +if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) + # Find more information: https://cmake.org/Wiki/CMake_RPATH_handling + + # Apply CMP0042: MACOSX_RPATH is enabled by default + cmake_policy(SET CMP0042 NEW) + + # Add an install rpath if it is not a system directory + list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir) + if("${isSystemDir}" STREQUAL "-1") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + endif() + + # Add linker search paths pointing to external dependencies + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +endif() + +if (NOT MSVC) + if(NOT CMAKE_CXX_FLAGS MATCHES "-std=") + # use C++11 by default if supported + check_cxx_compiler_flag("-std=gnu++11" COMPILER_SUPPORTS_CXX11) + if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") + endif() + endif() + if(NOT CMAKE_C_FLAGS MATCHES "-std=") + check_c_compiler_flag("-std=gnu11" COMPILER_SUPPORTS_C11) + if(COMPILER_SUPPORTS_C11) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") + else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") + endif() + endif() + + # clang 6 has a warning that does not make sense on multi-platform code + check_cxx_compiler_flag("-Wno-tautological-constant-compare" CXX_HAS_TAUT_WARNING) + if(CXX_HAS_TAUT_WARNING) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-tautological-constant-compare") + endif() + check_c_compiler_flag("-Wno-tautological-constant-compare" CC_HAS_TAUT_WARNING) + if(CC_HAS_TAUT_WARNING) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-tautological-constant-compare") + endif() +endif() + +# Will be used to add flags to pkg-config useful when apps want to statically link +set(pkg_config_libs_private "") +set(pkg_config_names_private "") + +option(WITH_OPENPGM "Build with support for OpenPGM" OFF) +option(WITH_NORM "Build with support for NORM" OFF) +option(WITH_VMCI "Build with support for VMware VMCI socket" OFF) + +if(APPLE) + option(ZMQ_BUILD_FRAMEWORK "Build as OS X framework" OFF) +endif() + +if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + message(STATUS "Build and install draft classes and methods") + option(ENABLE_DRAFTS "Build and install draft classes and methods" ON) +else() + message(STATUS "Not building draft classes and methods") + option(ENABLE_DRAFTS "Build and install draft classes and methods" OFF) +endif() + +# Enable WebSocket transport and RadixTree +if(ENABLE_DRAFTS) + set(ZMQ_BUILD_DRAFT_API 1) + option(ENABLE_WS "Enable WebSocket transport" ON) + option(ENABLE_RADIX_TREE "Use radix tree implementation to manage subscriptions" ON) +else() + option(ENABLE_WS "Enable WebSocket transport" OFF) + option(ENABLE_RADIX_TREE "Use radix tree implementation to manage subscriptions" OFF) +endif() + +if(ENABLE_RADIX_TREE) + message(STATUS "Using radix tree implementation to manage subscriptions") + set(ZMQ_USE_RADIX_TREE 1) +endif() + +if(ENABLE_WS) + list( + APPEND + sources + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_address.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_connecter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_decoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_encoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_engine.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_listener.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_address.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_connecter.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_decoder.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_encoder.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_engine.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_listener.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ws_protocol.hpp) + set(ZMQ_HAVE_WS 1) + + message(STATUS "Enable WebSocket transport") + + option(WITH_TLS "Use TLS for WSS support" ON) + option(WITH_NSS "Use NSS instead of builtin sha1" OFF) + + if(WITH_TLS) + find_package("GnuTLS" 3.6.7) + if(GNUTLS_FOUND) + set(pkg_config_names_private "${pkg_config_names_private} gnutls") + list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/src/wss_address.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/wss_address.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/wss_engine.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/wss_engine.cpp) + + message(STATUS "Enable WSS transport") + set(ZMQ_USE_GNUTLS 1) + set(ZMQ_HAVE_WSS 1) + else() + message(WARNING "No WSS support, you may want to install GnuTLS and run cmake again") + endif() + endif() +endif() + +if(NOT ZMQ_USE_GNUTLS) + if(WITH_NSS) + pkg_check_modules(NSS3 "nss") + if(NSS3_FOUND) + set(pkg_config_names_private "${pkg_config_names_private} nss") + message(STATUS "Using NSS") + set(ZMQ_USE_NSS 1) + else() + find_package("NSS3") + if(NSS3_FOUND) + set(pkg_config_libs_private "${pkg_config_libs_private} -lnss3") + message(STATUS "Using NSS") + set(ZMQ_USE_NSS 1) + else() + message(WARNING "No nss installed, if you don't want builtin SHA1, install NSS or GnuTLS") + endif() + endif() + endif() + if(NOT ZMQ_USE_NSS) + list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/external/sha1/sha1.c + ${CMAKE_CURRENT_SOURCE_DIR}/external/sha1/sha1.h) + message(STATUS "Using builtin sha1") + set(ZMQ_USE_BUILTIN_SHA1 1) + endif() +endif() + +if(NOT MSVC) + option(WITH_LIBBSD "Use libbsd instead of builtin strlcpy" ON) + if(WITH_LIBBSD) + pkg_check_modules(LIBBSD "libbsd") + if(LIBBSD_FOUND) + message(STATUS "Using libbsd") + set(pkg_config_names_private "${pkg_config_names_private} libbsd") + set(ZMQ_HAVE_LIBBSD 1) + endif() + endif() + check_cxx_symbol_exists(strlcpy string.h ZMQ_HAVE_STRLCPY) +endif() + +# Select curve encryption library, defaults to tweetnacl To use libsodium instead, use --with-libsodium(must be +# installed) To disable curve, use --disable-curve + +option(WITH_LIBSODIUM "Use libsodium instead of built-in tweetnacl" ON) +option(WITH_LIBSODIUM_STATIC "Use static libsodium library" OFF) +option(ENABLE_CURVE "Enable CURVE security" ON) + +if(ENABLE_CURVE) + if(WITH_LIBSODIUM) + if(sodium_FOUND) + message(STATUS "Using libsodium for CURVE security") + set(ZMQ_USE_LIBSODIUM 1) + set(ZMQ_HAVE_CURVE 1) + else() + message( + WARNING + "libsodium not installed, instead using builtin tweetnacl, you may want to install libsodium and run cmake again" + ) + endif() + endif() + if(NOT ZMQ_HAVE_CURVE) + message(STATUS "Using tweetnacl for CURVE security") + list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/src/tweetnacl.c) + set(ZMQ_USE_TWEETNACL 1) + set(ZMQ_HAVE_CURVE 1) + endif() +else() + message(STATUS "CURVE security is disabled") +endif() + +set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + +option(WITH_MILITANT "Enable militant assertions" OFF) +if(WITH_MILITANT) + add_definitions(-DZMQ_ACT_MILITANT) +endif() + +set(API_POLLER + "" + CACHE STRING "Choose polling system for zmq_poll(er)_*. valid values are + poll or select [default=poll unless POLLER=select]") + +set(POLLER + "" + CACHE STRING "Choose polling system for I/O threads. valid values are + kqueue, epoll, devpoll, pollset, poll or select [default=autodetect]") + +if(WIN32) + if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" AND CMAKE_SYSTEM_VERSION MATCHES "^10.0") + set(ZMQ_HAVE_WINDOWS_UWP ON) + set(ZMQ_HAVE_IPC OFF) + # to remove compile warninging "D9002 ignoring unknown option" + string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + set(CMAKE_CXX_FLAGS_DEBUG + ${CMAKE_CXX_FLAGS_DEBUG} + CACHE STRING "" FORCE) + string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO + ${CMAKE_CXX_FLAGS_RELWITHDEBINFO} + CACHE STRING "" FORCE) + string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + endif() + # from https://stackoverflow.com/a/40217291/2019765 + macro(get_WIN32_WINNT version) + if(CMAKE_SYSTEM_VERSION) + set(ver ${CMAKE_SYSTEM_VERSION}) + string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver}) + string(REGEX MATCH "^([0-9]+)" verMajor ${ver}) + # Check for Windows 10, b/c we'll need to convert to hex 'A'. + if("${verMajor}" MATCHES "10") + set(verMajor "A") + string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver}) + endif("${verMajor}" MATCHES "10") + # Remove all remaining '.' characters. + string(REPLACE "." "" ver ${ver}) + # Prepend each digit with a zero. + string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver}) + set(${version} "0x${ver}") + endif(CMAKE_SYSTEM_VERSION) + endmacro(get_WIN32_WINNT) + + get_win32_winnt(ZMQ_WIN32_WINNT_DEFAULT) + message(STATUS "Detected _WIN32_WINNT from CMAKE_SYSTEM_VERSION: ${ZMQ_WIN32_WINNT_DEFAULT}") + + # TODO limit _WIN32_WINNT to the actual Windows SDK version, which might be different from the default version + # installed with Visual Studio + if(MSVC_VERSION STREQUAL "1500" AND CMAKE_SYSTEM_VERSION VERSION_GREATER "6.0") + set(ZMQ_WIN32_WINNT_LIMIT "0x0600") + elseif(MSVC_VERSION STREQUAL "1600" AND CMAKE_SYSTEM_VERSION VERSION_GREATER "6.1") + set(ZMQ_WIN32_WINNT_LIMIT "0x0601") + elseif(MSVC_VERSION STREQUAL "1700" AND CMAKE_SYSTEM_VERSION VERSION_GREATER "6.1") + set(ZMQ_WIN32_WINNT_LIMIT "0x0601") + elseif(MSVC_VERSION STREQUAL "1800" AND CMAKE_SYSTEM_VERSION VERSION_GREATER "6.2") + set(ZMQ_WIN32_WINNT_LIMIT "0x0602") + endif() + if(ZMQ_WIN32_WINNT_LIMIT) + message( + STATUS + "Mismatch of Visual Studio Version (${MSVC_VERSION}) and CMAKE_SYSTEM_VERSION (${CMAKE_SYSTEM_VERSION}), limiting _WIN32_WINNT to ${ZMQ_WIN32_WINNT_LIMIT}, you may override this by setting ZMQ_WIN32_WINNT" + ) + set(ZMQ_WIN32_WINNT_DEFAULT "${ZMQ_WIN32_WINNT_LIMIT}") + endif() + + set(ZMQ_WIN32_WINNT + "${ZMQ_WIN32_WINNT_DEFAULT}" + CACHE STRING "Value to set _WIN32_WINNT to for building [default=autodetect from build environment]") + + # On Windows Vista or greater, with MSVC 2013 or greater, default to epoll (which is required on Win 10 for ipc + # support) + if(ZMQ_WIN32_WINNT GREATER "0x05FF" + AND MSVC_VERSION GREATER 1799 + AND POLLER STREQUAL "" + AND NOT ZMQ_HAVE_WINDOWS_UWP) + set(POLLER "epoll") + endif() + + add_definitions(-D_WIN32_WINNT=${ZMQ_WIN32_WINNT}) +endif(WIN32) + +if(NOT MSVC) + if(POLLER STREQUAL "") + check_cxx_symbol_exists(kqueue sys/event.h HAVE_KQUEUE) + if(HAVE_KQUEUE) + set(POLLER "kqueue") + endif() + endif() + + if(POLLER STREQUAL "") + check_cxx_symbol_exists(epoll_create sys/epoll.h HAVE_EPOLL) + if(HAVE_EPOLL) + set(POLLER "epoll") + check_cxx_symbol_exists(epoll_create1 sys/epoll.h HAVE_EPOLL_CLOEXEC) + if(HAVE_EPOLL_CLOEXEC) + set(ZMQ_IOTHREAD_POLLER_USE_EPOLL_CLOEXEC 1) + endif() + endif() + endif() + + if(POLLER STREQUAL "") + set(CMAKE_EXTRA_INCLUDE_FILES sys/devpoll.h) + check_type_size("struct pollfd" DEVPOLL) + set(CMAKE_EXTRA_INCLUDE_FILES) + if(HAVE_DEVPOLL) + set(POLLER "devpoll") + endif() + endif() + + if(POLLER STREQUAL "") + check_cxx_symbol_exists(pollset_create sys/pollset.h HAVE_POLLSET) + if(HAVE_POLLSET) + set(POLLER "pollset") + endif() + endif() + + if(POLLER STREQUAL "") + check_cxx_symbol_exists(poll poll.h HAVE_POLL) + if(HAVE_POLL) + set(POLLER "poll") + endif() + endif() +endif() + +if(POLLER STREQUAL "") + if(WIN32) + set(HAVE_SELECT 1) + else() + check_cxx_symbol_exists(select sys/select.h HAVE_SELECT) + endif() + if(HAVE_SELECT) + set(POLLER "select") + else() + message(FATAL_ERROR "Could not autodetect polling method") + endif() +endif() + +if(POLLER STREQUAL "kqueue" + OR POLLER STREQUAL "epoll" + OR POLLER STREQUAL "devpoll" + OR POLLER STREQUAL "pollset" + OR POLLER STREQUAL "poll" + OR POLLER STREQUAL "select") + message(STATUS "Using polling method in I/O threads: ${POLLER}") + string(TOUPPER ${POLLER} UPPER_POLLER) + set(ZMQ_IOTHREAD_POLLER_USE_${UPPER_POLLER} 1) +else() + message(FATAL_ERROR "Invalid polling method") +endif() + +if(POLLER STREQUAL "epoll" AND WIN32) + message(STATUS "Including wepoll") + list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/external/wepoll/wepoll.c + ${CMAKE_CURRENT_SOURCE_DIR}/external/wepoll/wepoll.h) +endif() + +if(API_POLLER STREQUAL "") + if(POLLER STREQUAL "select") + set(API_POLLER "select") + else() + set(API_POLLER "poll") + endif() +endif() + +message(STATUS "Using polling method in zmq_poll(er)_* API: ${API_POLLER}") +string(TOUPPER ${API_POLLER} UPPER_API_POLLER) +set(ZMQ_POLL_BASED_ON_${UPPER_API_POLLER} 1) + +execute_process( + COMMAND getconf LEVEL1_DCACHE_LINESIZE + OUTPUT_VARIABLE CACHELINE_SIZE + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +if(CACHELINE_SIZE STREQUAL "" + OR CACHELINE_SIZE EQUAL 0 + OR CACHELINE_SIZE EQUAL -1) + set(ZMQ_CACHELINE_SIZE 64) +else() + set(ZMQ_CACHELINE_SIZE ${CACHELINE_SIZE}) +endif() +message(STATUS "Using ${ZMQ_CACHELINE_SIZE} bytes alignment for lock-free data structures") + +if(NOT CYGWIN) + # TODO cannot we simply do 'if(WIN32) set(ZMQ_HAVE_WINDOWS ON)' or similar? + check_include_files(windows.h ZMQ_HAVE_WINDOWS) +endif() + +if(NOT WIN32) + set(ZMQ_HAVE_IPC 1) +else() + check_include_files("winsock2.h;afunix.h" ZMQ_HAVE_IPC) +endif() + +# ##################### BEGIN condition_variable_t selection +if(NOT ZMQ_CV_IMPL) + # prefer C++11 STL std::condition_variable implementation, if available + check_include_files(condition_variable ZMQ_HAVE_STL_CONDITION_VARIABLE LANGUAGE CXX) + + if(ZMQ_HAVE_STL_CONDITION_VARIABLE) + set(ZMQ_CV_IMPL_DEFAULT "stl11") + else() + if(WIN32 AND NOT CMAKE_SYSTEM_VERSION VERSION_LESS "6.0") + # Win32API CONDITION_VARIABLE is supported from Windows Vista only + set(ZMQ_CV_IMPL_DEFAULT "win32api") + elseif(CMAKE_USE_PTHREADS_INIT) + set(ZMQ_CV_IMPL_DEFAULT "pthreads") + else() + set(ZMQ_CV_IMPL_DEFAULT "none") + endif() + endif() + + # TODO a vxworks implementation also exists, but vxworks is not currently supported with cmake at all + set(ZMQ_CV_IMPL + "${ZMQ_CV_IMPL_DEFAULT}" + CACHE STRING "Choose condition_variable_t implementation. Valid values are + stl11, win32api, pthreads, none [default=autodetect]") +endif() + +message(STATUS "Using condition_variable_t implementation: ${ZMQ_CV_IMPL}") +if(ZMQ_CV_IMPL STREQUAL "stl11") + set(ZMQ_USE_CV_IMPL_STL11 1) +elseif(ZMQ_CV_IMPL STREQUAL "win32api") + set(ZMQ_USE_CV_IMPL_WIN32API 1) +elseif(ZMQ_CV_IMPL STREQUAL "pthreads") + set(ZMQ_USE_CV_IMPL_PTHREADS 1) +elseif(ZMQ_CV_IMPL STREQUAL "none") + set(ZMQ_USE_CV_IMPL_NONE 1) +else() + message(ERROR "Unknown value for ZMQ_CV_IMPL: ${ZMQ_CV_IMPL}") +endif() +# ##################### END condition_variable_t selection + +if(NOT MSVC) + check_include_files(ifaddrs.h ZMQ_HAVE_IFADDRS) + check_include_files(sys/uio.h ZMQ_HAVE_UIO) + check_include_files(sys/eventfd.h ZMQ_HAVE_EVENTFD) + if(ZMQ_HAVE_EVENTFD AND NOT CMAKE_CROSSCOMPILING) + zmq_check_efd_cloexec() + endif() +endif() + +if(ZMQ_HAVE_WINDOWS) + # Cannot use check_library_exists because the symbol is always declared as char(*)(void) + set(CMAKE_REQUIRED_LIBRARIES "ws2_32.lib") + check_cxx_symbol_exists(WSAStartup "winsock2.h" HAVE_WS2_32) + + set(CMAKE_REQUIRED_LIBRARIES "rpcrt4.lib") + check_cxx_symbol_exists(UuidCreateSequential "rpc.h" HAVE_RPCRT4) + + set(CMAKE_REQUIRED_LIBRARIES "iphlpapi.lib") + check_cxx_symbol_exists(GetAdaptersAddresses "winsock2.h;iphlpapi.h" HAVE_IPHLAPI) + check_cxx_symbol_exists(if_nametoindex "iphlpapi.h" HAVE_IF_NAMETOINDEX) + + set(CMAKE_REQUIRED_LIBRARIES "") + # TODO: This not the symbol we're looking for. What is the symbol? + check_library_exists(ws2 fopen "" HAVE_WS2) +else() + check_cxx_symbol_exists(if_nametoindex net/if.h HAVE_IF_NAMETOINDEX) + check_cxx_symbol_exists(SO_PEERCRED sys/socket.h ZMQ_HAVE_SO_PEERCRED) + check_cxx_symbol_exists(LOCAL_PEERCRED sys/socket.h ZMQ_HAVE_LOCAL_PEERCRED) +endif() + +if(NOT MINGW) + find_library(RT_LIBRARY rt) + if(RT_LIBRARY) + set(pkg_config_libs_private "${pkg_config_libs_private} -lrt") + endif() +endif() + +find_package(Threads) + +if(WIN32 AND NOT CYGWIN) + if(NOT HAVE_WS2_32 AND NOT HAVE_WS2) + message(FATAL_ERROR "Cannot link to ws2_32 or ws2") + endif() + + if(NOT HAVE_RPCRT4) + message(FATAL_ERROR "Cannot link to rpcrt4") + endif() + + if(NOT HAVE_IPHLAPI) + message(FATAL_ERROR "Cannot link to iphlapi") + endif() +endif() + +if(NOT MSVC) + set(CMAKE_REQUIRED_LIBRARIES rt) + check_cxx_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME) + set(CMAKE_REQUIRED_LIBRARIES) + + check_cxx_symbol_exists(fork unistd.h HAVE_FORK) + check_cxx_symbol_exists(gethrtime sys/time.h HAVE_GETHRTIME) + check_cxx_symbol_exists(mkdtemp stdlib.h HAVE_MKDTEMP) + check_cxx_symbol_exists(accept4 sys/socket.h HAVE_ACCEPT4) + check_cxx_symbol_exists(strnlen string.h HAVE_STRNLEN) +else() + set(HAVE_STRNLEN 1) +endif() + +add_definitions(-D_REENTRANT -D_THREAD_SAFE) +add_definitions(-DZMQ_CUSTOM_PLATFORM_HPP) + +option(ENABLE_EVENTFD "Enable/disable eventfd" ZMQ_HAVE_EVENTFD) + +macro(zmq_check_cxx_flag_prepend flag) + check_cxx_compiler_flag("${flag}" HAVE_FLAG_${flag}) + + if(HAVE_FLAG_${flag}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") + endif() +endmacro() + +option(ENABLE_ANALYSIS "Build with static analysis(make take very long)" OFF) + +if(MSVC) + if(ENABLE_ANALYSIS) + zmq_check_cxx_flag_prepend("/W4") + + zmq_check_cxx_flag_prepend("/analyze") + + # C++11/14/17-specific, but maybe possible via conditional defines + zmq_check_cxx_flag_prepend("/wd26440") # Function '...' can be declared 'noexcept' + zmq_check_cxx_flag_prepend("/wd26432") # If you define or delete any default operation in the type '...', define or + # delete them all + zmq_check_cxx_flag_prepend("/wd26439") # This kind of function may not throw. Declare it 'noexcept' + zmq_check_cxx_flag_prepend("/wd26447") # The function is declared 'noexcept' but calls function '...' which may + # throw exceptions + zmq_check_cxx_flag_prepend("/wd26433") # Function '...' should be marked with 'override' + zmq_check_cxx_flag_prepend("/wd26409") # Avoid calling new and delete explicitly, use std::make_unique instead + # Requires GSL + zmq_check_cxx_flag_prepend("/wd26429") # Symbol '...' is never tested for nullness, it can be marked as not_null + zmq_check_cxx_flag_prepend("/wd26446") # Prefer to use gsl::at() + zmq_check_cxx_flag_prepend("/wd26481") # Don't use pointer arithmetic. Use span instead + zmq_check_cxx_flag_prepend("/wd26472") # Don't use a static_cast for arithmetic conversions. Use brace + # initialization, gsl::narrow_cast or gsl::narow + zmq_check_cxx_flag_prepend("/wd26448") # Consider using gsl::finally if final action is intended + zmq_check_cxx_flag_prepend("/wd26400") # Do not assign the result of an allocation or a function call with an + # owner return value to a raw pointer, use owner instead + zmq_check_cxx_flag_prepend("/wd26485") # Expression '...': No array to pointer decay(bounds.3) + else() + zmq_check_cxx_flag_prepend("/W3") + endif() + + if(MSVC_IDE) + set(MSVC_TOOLSET "-${CMAKE_VS_PLATFORM_TOOLSET}") + else() + set(MSVC_TOOLSET "") + endif() +else() + zmq_check_cxx_flag_prepend("-Wall") +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + zmq_check_cxx_flag_prepend("-Wextra") +endif() + +option(LIBZMQ_PEDANTIC "" ON) +option(LIBZMQ_WERROR "" OFF) + +# TODO: why is -Wno-long-long defined differently than in configure.ac? +if(NOT MSVC) + zmq_check_cxx_flag_prepend("-Wno-long-long") + zmq_check_cxx_flag_prepend("-Wno-uninitialized") + + if(LIBZMQ_PEDANTIC) + zmq_check_cxx_flag_prepend("-pedantic") + + if(${CMAKE_CXX_COMPILER_ID} MATCHES "Intel") + zmq_check_cxx_flag_prepend("-strict-ansi") + endif() + + if(${CMAKE_CXX_COMPILER_ID} MATCHES "SunPro") + zmq_check_cxx_flag_prepend("-compat=5") + endif() + endif() +endif() + +if(LIBZMQ_WERROR) + if(MSVC) + zmq_check_cxx_flag_prepend("/WX") + else() + zmq_check_cxx_flag_prepend("-Werror") + if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + zmq_check_cxx_flag_prepend("-errwarn=%all") + endif() + endif() +endif() + +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^sparc") + zmq_check_cxx_flag_prepend("-mcpu=v9") +endif() + +if(${CMAKE_CXX_COMPILER_ID} MATCHES "SunPro") + zmq_check_cxx_flag_prepend("-features=zla") +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "SunOS" + OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" + OR CMAKE_SYSTEM_NAME MATCHES "QNX") + message(STATUS "Checking whether atomic operations can be used") + check_c_source_compiles( + "\ + #include \ + \ + int main() \ + { \ + uint32_t value; \ + atomic_cas_32(&value, 0, 0); \ + return 0; \ + } \ + " + HAVE_ATOMIC_H) + + if(NOT HAVE_ATOMIC_H) + set(ZMQ_FORCE_MUTEXES 1) + endif() +endif() + +if(NOT ANDROID) + zmq_check_noexcept() +endif() + +# ----------------------------------------------------------------------------- +if(NOT CMAKE_CROSSCOMPILING AND NOT MSVC) + zmq_check_sock_cloexec() + zmq_check_o_cloexec() + zmq_check_so_bindtodevice() + zmq_check_so_keepalive() + zmq_check_so_priority() + zmq_check_tcp_keepcnt() + zmq_check_tcp_keepidle() + zmq_check_tcp_keepintvl() + zmq_check_tcp_keepalive() + zmq_check_tcp_tipc() + zmq_check_pthread_setname() + zmq_check_pthread_setaffinity() + zmq_check_getrandom() +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "Linux" + OR CMAKE_SYSTEM_NAME MATCHES "GNU/kFreeBSD" + OR CMAKE_SYSTEM_NAME MATCHES "GNU/Hurd" + OR CYGWIN) + add_definitions(-D_GNU_SOURCE) +elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_definitions(-D__BSD_VISIBLE) +elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD") + add_definitions(-D_NETBSD_SOURCE) +elseif(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + add_definitions(-D_OPENBSD_SOURCE) +elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS") + add_definitions(-D_PTHREADS) +elseif(CMAKE_SYSTEM_NAME MATCHES "HP-UX") + add_definitions(-D_POSIX_C_SOURCE=200112L) + zmq_check_cxx_flag_prepend(-Ae) +elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_definitions(-D_DARWIN_C_SOURCE) +endif() + +find_package(AsciiDoc) + +cmake_dependent_option(WITH_DOC "Build Reference Guide documentation(requires DocBook)" ON "ASCIIDOC_FOUND;NOT WIN32" + OFF) # Do not build docs on Windows due to issues with symlinks + +if(MSVC) + if(WITH_OPENPGM) + # set(OPENPGM_ROOT "" CACHE PATH "Location of OpenPGM") + set(OPENPGM_VERSION_MAJOR 5) + set(OPENPGM_VERSION_MINOR 2) + set(OPENPGM_VERSION_MICRO 122) + if(CMAKE_CL_64) + find_path( + OPENPGM_ROOT include/pgm/pgm.h + PATHS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Miru\\OpenPGM ${OPENPGM_VERSION_MAJOR}.${OPENPGM_VERSION_MINOR}.${OPENPGM_VERSION_MICRO}]" + NO_DEFAULT_PATH) + message(STATUS "OpenPGM x64 detected - ${OPENPGM_ROOT}") + else() + find_path( + OPENPGM_ROOT include/pgm/pgm.h + PATHS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Miru\\OpenPGM ${OPENPGM_VERSION_MAJOR}.${OPENPGM_VERSION_MINOR}.${OPENPGM_VERSION_MICRO}]" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Miru\\OpenPGM ${OPENPGM_VERSION_MAJOR}.${OPENPGM_VERSION_MINOR}.${OPENPGM_VERSION_MICRO}]" + NO_DEFAULT_PATH) + message(STATUS "OpenPGM x86 detected - ${OPENPGM_ROOT}") + endif() + set(OPENPGM_INCLUDE_DIRS ${OPENPGM_ROOT}/include) + set(OPENPGM_LIBRARY_DIRS ${OPENPGM_ROOT}/lib) + set(OPENPGM_LIBRARIES + optimized + libpgm${MSVC_TOOLSET}-mt-${OPENPGM_VERSION_MAJOR}_${OPENPGM_VERSION_MINOR}_${OPENPGM_VERSION_MICRO}.lib debug + libpgm${MSVC_TOOLSET}-mt-gd-${OPENPGM_VERSION_MAJOR}_${OPENPGM_VERSION_MINOR}_${OPENPGM_VERSION_MICRO}.lib) + endif() +else() + if(WITH_OPENPGM) + # message(FATAL_ERROR "WITH_OPENPGM not implemented") + + if(NOT OPENPGM_PKGCONFIG_NAME) + set(OPENPGM_PKGCONFIG_NAME "openpgm-5.2") + endif() + + set(OPENPGM_PKGCONFIG_NAME + ${OPENPGM_PKGCONFIG_NAME} + CACHE STRING "Name pkg-config shall use to find openpgm libraries and include paths" FORCE) + + pkg_check_modules(OPENPGM ${OPENPGM_PKGCONFIG_NAME}) + + if(OPENPGM_FOUND) + message(STATUS ${OPENPGM_PKGCONFIG_NAME}" found") + set(pkg_config_names_private "${pkg_config_names_private} ${OPENPGM_PKGCONFIG_NAME}") + else() + message( + FATAL_ERROR + ${OPENPGM_PKGCONFIG_NAME}" not found. openpgm is searchd via `pkg-config ${OPENPGM_PKGCONFIG_NAME}`. Consider providing a valid OPENPGM_PKGCONFIG_NAME" + ) + endif() + + # DSO symbol visibility for openpgm + if(HAVE_FLAG_VISIBILITY_HIDDEN) + + elseif(HAVE_FLAG_LDSCOPE_HIDDEN) + + endif() + endif() +endif() + +# ----------------------------------------------------------------------------- +# force off-tree build + +if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + message( + FATAL_ERROR + "CMake generation is not allowed within the source directory! \ + Remove the CMakeCache.txt file and try again from another folder, e.g.: \ + \ + rm CMakeCache.txt \ + mkdir cmake-make \ + cd cmake-make \ + cmake ..") +endif() + +# ----------------------------------------------------------------------------- +# default to Release build + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + # CMAKE_BUILD_TYPE is not used for multi-configuration generators like Visual Studio/XCode which instead use + # CMAKE_CONFIGURATION_TYPES + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif() + +# ----------------------------------------------------------------------------- +# output directories + +zmq_set_with_default(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${ZeroMQ_BINARY_DIR}/bin") +if(UNIX) + set(zmq_library_directory "lib") +else() + set(zmq_library_directory "bin") +endif() +zmq_set_with_default(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ZeroMQ_BINARY_DIR}/${zmq_library_directory}") +zmq_set_with_default(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${ZeroMQ_BINARY_DIR}/lib") + +# ----------------------------------------------------------------------------- +# platform specifics + +if(WIN32) + # Socket limit is 16K(can be raised arbitrarily) + add_definitions(-DFD_SETSIZE=16384) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS) +endif() + +if(MSVC) + # Parallel make. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") + + # Compile the static lib with debug information included note: we assume here that the default flags contain some /Z + # flag + string(REGEX REPLACE "/Z.[^:]" "/Z7 " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REGEX REPLACE "/Z.[^:]" "/Z7 " CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + + # Optimization flags. http://msdn.microsoft.com/en-us/magazine/cc301698.aspx + if(NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GL") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LTCG") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /LTCG") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /LTCG") + endif() +endif() + +# ----------------------------------------------------------------------------- +# source files + +set(cxx-sources + precompiled.cpp + address.cpp + channel.cpp + client.cpp + clock.cpp + ctx.cpp + curve_mechanism_base.cpp + curve_client.cpp + curve_server.cpp + dealer.cpp + devpoll.cpp + dgram.cpp + dist.cpp + endpoint.cpp + epoll.cpp + err.cpp + fq.cpp + io_object.cpp + io_thread.cpp + ip.cpp + ipc_address.cpp + ipc_connecter.cpp + ipc_listener.cpp + kqueue.cpp + lb.cpp + mailbox.cpp + mailbox_safe.cpp + mechanism.cpp + mechanism_base.cpp + metadata.cpp + msg.cpp + mtrie.cpp + norm_engine.cpp + object.cpp + options.cpp + own.cpp + null_mechanism.cpp + pair.cpp + peer.cpp + pgm_receiver.cpp + pgm_sender.cpp + pgm_socket.cpp + pipe.cpp + plain_client.cpp + plain_server.cpp + poll.cpp + poller_base.cpp + polling_util.cpp + pollset.cpp + proxy.cpp + pub.cpp + pull.cpp + push.cpp + random.cpp + raw_encoder.cpp + raw_decoder.cpp + raw_engine.cpp + reaper.cpp + rep.cpp + req.cpp + router.cpp + select.cpp + server.cpp + session_base.cpp + signaler.cpp + socket_base.cpp + socks.cpp + socks_connecter.cpp + stream.cpp + stream_engine_base.cpp + sub.cpp + tcp.cpp + tcp_address.cpp + tcp_connecter.cpp + tcp_listener.cpp + thread.cpp + trie.cpp + radix_tree.cpp + v1_decoder.cpp + v1_encoder.cpp + v2_decoder.cpp + v2_encoder.cpp + v3_1_encoder.cpp + xpub.cpp + xsub.cpp + zmq.cpp + zmq_utils.cpp + decoder_allocators.cpp + socket_poller.cpp + timers.cpp + config.hpp + radio.cpp + dish.cpp + udp_engine.cpp + udp_address.cpp + scatter.cpp + gather.cpp + ip_resolver.cpp + zap_client.cpp + zmtp_engine.cpp + # at least for VS, the header files must also be listed + address.hpp + array.hpp + atomic_counter.hpp + atomic_ptr.hpp + blob.hpp + channel.hpp + client.hpp + clock.hpp + command.hpp + compat.hpp + condition_variable.hpp + config.hpp + ctx.hpp + curve_client.hpp + curve_client_tools.hpp + curve_mechanism_base.hpp + curve_server.hpp + dbuffer.hpp + dealer.hpp + decoder.hpp + decoder_allocators.hpp + devpoll.hpp + dgram.hpp + dish.hpp + dist.hpp + encoder.hpp + endpoint.hpp + epoll.hpp + err.hpp + fd.hpp + fq.hpp + gather.hpp + generic_mtrie.hpp + generic_mtrie_impl.hpp + gssapi_client.hpp + gssapi_mechanism_base.hpp + gssapi_server.hpp + i_decoder.hpp + i_encoder.hpp + i_engine.hpp + i_mailbox.hpp + i_poll_events.hpp + io_object.hpp + io_thread.hpp + ip.hpp + ipc_address.hpp + ipc_connecter.hpp + ipc_listener.hpp + kqueue.hpp + lb.hpp + likely.hpp + macros.hpp + mailbox.hpp + mailbox_safe.hpp + mechanism.hpp + mechanism_base.hpp + metadata.hpp + msg.hpp + mtrie.hpp + mutex.hpp + norm_engine.hpp + null_mechanism.hpp + object.hpp + options.hpp + own.hpp + pair.hpp + peer.hpp + pgm_receiver.hpp + pgm_sender.hpp + pgm_socket.hpp + pipe.hpp + plain_client.hpp + plain_common.hpp + plain_server.hpp + poll.hpp + poller.hpp + poller_base.hpp + polling_util.hpp + pollset.hpp + precompiled.hpp + proxy.hpp + pub.hpp + pull.hpp + push.hpp + radio.hpp + random.hpp + raw_decoder.hpp + raw_encoder.hpp + raw_engine.hpp + reaper.hpp + rep.hpp + req.hpp + router.hpp + scatter.hpp + secure_allocator.hpp + select.hpp + server.hpp + session_base.hpp + signaler.hpp + socket_base.hpp + socket_poller.hpp + socks.hpp + socks_connecter.hpp + stdint.hpp + stream.hpp + stream_engine_base.hpp + stream_connecter_base.hpp + stream_connecter_base.cpp + stream_listener_base.hpp + stream_listener_base.cpp + sub.hpp + tcp.hpp + tcp_address.hpp + tcp_connecter.hpp + tcp_listener.hpp + thread.hpp + timers.hpp + tipc_address.hpp + tipc_connecter.hpp + tipc_listener.hpp + trie.hpp + udp_address.hpp + udp_engine.hpp + v1_decoder.hpp + v1_encoder.hpp + v2_decoder.hpp + v2_encoder.hpp + v3_1_encoder.hpp + v2_protocol.hpp + vmci.hpp + vmci_address.hpp + vmci_connecter.hpp + vmci_listener.hpp + windows.hpp + wire.hpp + xpub.hpp + xsub.hpp + ypipe.hpp + ypipe_base.hpp + ypipe_conflate.hpp + yqueue.hpp + zap_client.hpp + zmtp_engine.hpp) + +if(MINGW) + # Generate the right type when using -m32 or -m64 + macro(set_rc_arch rc_target) + set(CMAKE_RC_COMPILER_INIT windres) + enable_language(RC) + set(CMAKE_RC_COMPILE_OBJECT + " -O coff --target=${rc_target} -i -o ") + endmacro() + + if(NOT CMAKE_SYSTEM_PROCESSOR) + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) + endif() + + # Also happens on x86_64 systems...what a worthless variable + if(CMAKE_SYSTEM_PROCESSOR MATCHES "i386" + OR CMAKE_SYSTEM_PROCESSOR MATCHES "i486" + OR CMAKE_SYSTEM_PROCESSOR MATCHES "i586" + OR CMAKE_SYSTEM_PROCESSOR MATCHES "i686" + OR CMAKE_SYSTEM_PROCESSOR MATCHES "x86" + OR CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" + OR CMAKE_SYSTEM_PROCESSOR MATCHES "amd64") + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set_rc_arch("pe-x86-64") + else() + set_rc_arch("pe-i386") + endif() + endif() +endif() + +set(public_headers include/zmq.h include/zmq_utils.h) + +set(readme-docs AUTHORS COPYING COPYING.LESSER NEWS) + +# ----------------------------------------------------------------------------- +# optional modules + +if(WITH_OPENPGM) + add_definitions(-DZMQ_HAVE_OPENPGM) + include_directories(${OPENPGM_INCLUDE_DIRS}) + link_directories(${OPENPGM_LIBRARY_DIRS}) + set(OPTIONAL_LIBRARIES ${OPENPGM_LIBRARIES}) +endif() + +if(WITH_NORM) + find_package(norm) + if(norm_FOUND) + message(STATUS "Building with NORM") + set(ZMQ_HAVE_NORM 1) + else() + message(FATAL_ERROR "NORM not found") + endif() +endif() + +if(WITH_VMCI) + add_definitions(-DZMQ_HAVE_VMCI) + include_directories(${VMCI_INCLUDE_DIRS}) + list(APPEND cxx-sources vmci_address.cpp vmci_connecter.cpp vmci_listener.cpp vmci.cpp) +endif() + +if(ZMQ_HAVE_TIPC) + list(APPEND cxx-sources tipc_address.cpp tipc_connecter.cpp tipc_listener.cpp) +endif() + +# ----------------------------------------------------------------------------- +# source generators + +foreach(source ${cxx-sources}) + list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/src/${source}) +endforeach() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + +# Delete any src/platform.hpp left by configure +file(REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/src/platform.hpp) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/builds/cmake/platform.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/platform.hpp) +list(APPEND sources ${CMAKE_CURRENT_BINARY_DIR}/platform.hpp) + +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix ${prefix}) +set(libdir ${prefix}/lib) +set(includedir ${prefix}/include) +set(VERSION ${ZMQ_VERSION_MAJOR}.${ZMQ_VERSION_MINOR}.${ZMQ_VERSION_PATCH}) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/libzmq.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libzmq.pc @ONLY) +set(zmq-pkgconfig ${CMAKE_CURRENT_BINARY_DIR}/libzmq.pc) + +if(NOT ZMQ_BUILD_FRAMEWORK) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libzmq.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() + +if(MSVC) + if(CMAKE_CL_64) + set(nsis-template ${CMAKE_CURRENT_SOURCE_DIR}/builds/cmake/NSIS.template64.in) + else() + set(nsis-template ${CMAKE_CURRENT_SOURCE_DIR}/builds/cmake/NSIS.template32.in) + endif() + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/NSIS.template.in + COMMAND ${CMAKE_COMMAND} ARGS -E copy ${nsis-template} ${CMAKE_CURRENT_BINARY_DIR}/NSIS.template.in + DEPENDS ${nsis-template}) +endif() + +option(WITH_DOCS "Build html docs" ON) +if(WITH_DOCS) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc) + file( + GLOB docs + RELATIVE ${CMAKE_CURRENT_BINARY_DIR}/ + "${CMAKE_CURRENT_SOURCE_DIR}/doc/*.txt") + set(html-docs) + foreach(txt ${docs}) + string(REGEX REPLACE ".*/(.*)\\.txt" "\\1.html" html ${txt}) + set(src ${txt}) + set(dst doc/${html}) + if(WITH_DOC) + add_custom_command( + OUTPUT ${dst} + COMMAND ${ASCIIDOC_EXECUTABLE} -d manpage -b xhtml11 -f ${CMAKE_CURRENT_SOURCE_DIR}/doc/asciidoc.conf + -azmq_version=${ZMQ_VERSION} -o ${dst} ${src} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${src} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating ${html}") + list(APPEND html-docs ${CMAKE_CURRENT_BINARY_DIR}/${dst}) + endif() + endforeach() +endif() + +if(ZMQ_BUILD_FRAMEWORK) + add_custom_command( + TARGET libzmq + POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E make_directory + "${CMAKE_LIBRARY_OUTPUT_PATH}/ZeroMQ.framework/Versions/${ZMQ_VERSION}/MacOS" + COMMENT "Perf tools") +endif() + +option(ENABLE_PRECOMPILED "Enable precompiled headers, if possible" ON) +if(MSVC AND ENABLE_PRECOMPILED) + # default for all sources is to use precompiled headers + foreach(source ${sources}) + # C and C++ can not use the same precompiled header + if(${source} MATCHES ".cpp$" AND NOT ${source} STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}/src/precompiled.cpp") + set_source_files_properties(${source} PROPERTIES COMPILE_FLAGS "/Yuprecompiled.hpp" OBJECT_DEPENDS + precompiled.hpp) + endif() + endforeach() + # create precompiled header + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/precompiled.cpp + PROPERTIES COMPILE_FLAGS "/Ycprecompiled.hpp" OBJECT_OUTPUTS precompiled.hpp) +endif() + +# ----------------------------------------------------------------------------- +# output +option(BUILD_SHARED "Whether or not to build the shared object" ON) +option(BUILD_STATIC "Whether or not to build the static archive" ON) + +if(MSVC) + # Suppress linker warnings caused by #ifdef omission of file content. + set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") + set(PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") + set(PDB_NAME + "lib${ZMQ_OUTPUT_BASENAME}${MSVC_TOOLSET}-mt-gd-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}") + function(enable_vs_guideline_checker target) + set_target_properties( + ${target} PROPERTIES VS_GLOBAL_EnableCppCoreCheck true VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset + VS_GLOBAL_RunCodeAnalysis true) + endfunction() + if(BUILD_SHARED) + add_library(libzmq SHARED ${sources} ${public_headers} ${html-docs} ${readme-docs} + ${CMAKE_CURRENT_BINARY_DIR}/NSIS.template.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + if(ENABLE_ANALYSIS) + enable_vs_guideline_checker(libzmq) + endif() + set_target_properties( + libzmq + PROPERTIES PUBLIC_HEADER "${public_headers}" + RELEASE_POSTFIX "${MSVC_TOOLSET}-mt-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + RELWITHDEBINFO_POSTFIX + "${MSVC_TOOLSET}-mt-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + MINSIZEREL_POSTFIX "${MSVC_TOOLSET}-mt-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + DEBUG_POSTFIX "${MSVC_TOOLSET}-mt-gd-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" + COMPILE_DEFINITIONS "DLL_EXPORT" + OUTPUT_NAME "lib${ZMQ_OUTPUT_BASENAME}") + if(ZMQ_HAVE_WINDOWS_UWP) + set_target_properties(libzmq PROPERTIES LINK_FLAGS_DEBUG "/OPT:NOICF /OPT:NOREF") + endif() + endif() + + if(BUILD_STATIC) + add_library(libzmq-static STATIC ${sources} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + set_target_properties( + libzmq-static + PROPERTIES PUBLIC_HEADER "${public_headers}" + RELEASE_POSTFIX "${MSVC_TOOLSET}-mt-s-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + RELWITHDEBINFO_POSTFIX + "${MSVC_TOOLSET}-mt-s-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + MINSIZEREL_POSTFIX + "${MSVC_TOOLSET}-mt-s-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + DEBUG_POSTFIX "${MSVC_TOOLSET}-mt-sgd-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}" + COMPILE_FLAGS "/DZMQ_STATIC" + OUTPUT_NAME "lib${ZMQ_OUTPUT_BASENAME}") + endif() +else() + # avoid building everything twice for shared + static only on *nix, as Windows needs different preprocessor defines in + # static builds + if(NOT MINGW) + add_library(objects OBJECT ${sources}) + set_property(TARGET objects PROPERTY POSITION_INDEPENDENT_CODE ON) + target_include_directories( + objects PUBLIC $ + $ $) + endif() + + if(BUILD_SHARED) + if(MINGW) + add_library(libzmq SHARED ${sources} ${public_headers} ${html-docs} ${readme-docs} ${zmq-pkgconfig} + ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + else() + if (CMAKE_GENERATOR STREQUAL "Xcode") + add_library(libzmq SHARED ${sources} ${public_headers} ${html-docs} ${readme-docs} + ${zmq-pkgconfig} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + else() + add_library(libzmq SHARED $ ${public_headers} ${html-docs} ${readme-docs} + ${zmq-pkgconfig} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + endif() + + endif() + # NOTE: the SOVERSION and VERSION MUST be the same as the one generated by libtool! It is NOT the same as the + # version of the package. + set_target_properties( + libzmq PROPERTIES COMPILE_DEFINITIONS "DLL_EXPORT" PUBLIC_HEADER "${public_headers}" VERSION "5.2.4" + SOVERSION "5" OUTPUT_NAME "${ZMQ_OUTPUT_BASENAME}" PREFIX "lib") + if(ZMQ_BUILD_FRAMEWORK) + set_target_properties( + libzmq + PROPERTIES FRAMEWORK TRUE MACOSX_FRAMEWORK_IDENTIFIER "org.zeromq.libzmq" MACOSX_FRAMEWORK_SHORT_VERSION_STRING + ${ZMQ_VERSION} + MACOSX_FRAMEWORK_BUNDLE_VERSION ${ZMQ_VERSION}) + set_source_files_properties(${html-docs} PROPERTIES MACOSX_PACKAGE_LOCATION doc) + set_source_files_properties(${readme-docs} PROPERTIES MACOSX_PACKAGE_LOCATION etc) + set_source_files_properties(${zmq-pkgconfig} PROPERTIES MACOSX_PACKAGE_LOCATION lib/pkgconfig) + endif() + endif() + + if(BUILD_STATIC) + if(MINGW) + add_library(libzmq-static STATIC ${sources} ${public_headers} ${html-docs} ${readme-docs} ${zmq-pkgconfig} + ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + else() + if (CMAKE_GENERATOR STREQUAL "Xcode") + add_library(libzmq-static STATIC ${sources} ${public_headers} ${html-docs} ${readme-docs} + ${zmq-pkgconfig} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + else() + add_library(libzmq-static STATIC $ ${public_headers} ${html-docs} ${readme-docs} + ${zmq-pkgconfig} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + endif() + endif() + if(CMAKE_SYSTEM_NAME MATCHES "QNX") + target_link_libraries(libzmq-static m) + endif() + set_target_properties( + libzmq-static PROPERTIES PUBLIC_HEADER "${public_headers}" OUTPUT_NAME "${ZMQ_OUTPUT_BASENAME}" PREFIX "lib") + endif() +endif() + +if(BUILD_STATIC) + target_compile_definitions(libzmq-static PUBLIC ZMQ_STATIC) +endif() + +list(APPEND target_outputs "") + +if(BUILD_SHARED) + list(APPEND target_outputs "libzmq") +endif() + +if(BUILD_STATIC) + list(APPEND target_outputs "libzmq-static") +endif() + +foreach(target ${target_outputs}) + target_include_directories( + ${target} PUBLIC $ + $ $) +endforeach() + +if(BUILD_SHARED) + target_link_libraries(libzmq ${CMAKE_THREAD_LIBS_INIT}) + if(GNUTLS_FOUND) + target_link_libraries(libzmq ${GNUTLS_LIBRARIES}) + endif() + + if(NSS3_FOUND) + target_link_libraries(libzmq ${NSS3_LIBRARIES}) + endif() + + if(LIBBSD_FOUND) + target_link_libraries(libzmq ${LIBBSD_LIBRARIES}) + endif() + + if(sodium_FOUND) + target_link_libraries(libzmq ${sodium_LIBRARIES}) + # On Solaris, libsodium depends on libssp + if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + target_link_libraries(libzmq ssp) + endif() + endif() + + if(HAVE_WS2_32) + target_link_libraries(libzmq ws2_32) + elseif(HAVE_WS2) + target_link_libraries(libzmq ws2) + endif() + + if(HAVE_RPCRT4) + target_link_libraries(libzmq rpcrt4) + endif() + + if(HAVE_IPHLAPI) + target_link_libraries(libzmq iphlpapi) + endif() + + if(RT_LIBRARY) + target_link_libraries(libzmq -lrt) + endif() + + if(norm_FOUND) + target_link_libraries(libzmq norm::norm) + endif() +endif() + +if(BUILD_STATIC) + target_link_libraries(libzmq-static ${CMAKE_THREAD_LIBS_INIT}) + if(GNUTLS_FOUND) + target_link_libraries(libzmq-static ${GNUTLS_LIBRARIES}) + endif() + + if(LIBBSD_FOUND) + target_link_libraries(libzmq-static ${LIBBSD_LIBRARIES}) + endif() + + if(NSS3_FOUND) + target_link_libraries(libzmq-static ${NSS3_LIBRARIES}) + endif() + + if(SODIUM_FOUND) + target_link_libraries(libzmq-static ${SODIUM_LIBRARIES}) + # On Solaris, libsodium depends on libssp + if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + target_link_libraries(libzmq-static ssp) + endif() + endif() + + if(HAVE_WS2_32) + target_link_libraries(libzmq-static ws2_32) + elseif(HAVE_WS2) + target_link_libraries(libzmq-static ws2) + endif() + + if(HAVE_RPCRT4) + target_link_libraries(libzmq-static rpcrt4) + endif() + + if(HAVE_IPHLAPI) + target_link_libraries(libzmq-static iphlpapi) + endif() + + if(RT_LIBRARY) + target_link_libraries(libzmq-static -lrt) + endif() + + if(CMAKE_SYSTEM_NAME MATCHES "QNX") + add_definitions(-DUNITY_EXCLUDE_MATH_H) + endif() + + if(norm_FOUND) + target_link_libraries(libzmq-static norm::norm) + endif() +endif() + +if(BUILD_SHARED) + set(perf-tools + local_lat + remote_lat + local_thr + remote_thr + inproc_lat + inproc_thr + proxy_thr) + + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") # Why? + option(WITH_PERF_TOOL "Build with perf-tools" ON) + else() + option(WITH_PERF_TOOL "Build with perf-tools" OFF) + endif() + + if(WITH_PERF_TOOL) + foreach(perf-tool ${perf-tools}) + add_executable(${perf-tool} perf/${perf-tool}.cpp) + target_link_libraries(${perf-tool} libzmq) + + if(GNUTLS_FOUND) + target_link_libraries(${perf-tool} ${GNUTLS_LIBRARIES}) + endif() + + if(LIBBSD_FOUND) + target_link_libraries(${perf-tool} ${LIBBSD_LIBRARIES}) + endif() + + if(NSS3_FOUND) + target_link_libraries(${perf-tool} ${NSS3_LIBRARIES}) + endif() + + if(SODIUM_FOUND) + target_link_libraries(${perf-tool} ${SODIUM_LIBRARIES}) + endif() + + if(ZMQ_BUILD_FRAMEWORK) + # Copy perf-tools binaries into Framework + add_custom_command( + TARGET libzmq + ${perf-tool} POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E copy "$" + "${LIBRARY_OUTPUT_PATH}/ZeroMQ.framework/Versions/${ZMQ_VERSION_STRING}/MacOS/${perf-tool}" + VERBATIM + COMMENT "Perf tools") + else() + install(TARGETS ${perf-tool} RUNTIME DESTINATION bin COMPONENT PerfTools) + endif() + if(ZMQ_HAVE_WINDOWS_UWP) + set_target_properties(${perf-tool} PROPERTIES LINK_FLAGS_DEBUG "/OPT:NOICF /OPT:NOREF") + endif() + endforeach() + + if(BUILD_STATIC) + add_executable(benchmark_radix_tree perf/benchmark_radix_tree.cpp) + target_link_libraries(benchmark_radix_tree libzmq-static) + target_include_directories(benchmark_radix_tree PUBLIC "${CMAKE_CURRENT_LIST_DIR}/src") + if(ZMQ_HAVE_WINDOWS_UWP) + set_target_properties(benchmark_radix_tree PROPERTIES LINK_FLAGS_DEBUG "/OPT:NOICF /OPT:NOREF") + endif() + endif() + elseif(WITH_PERF_TOOL) + message(FATAL_ERROR "Shared library disabled - perf-tools unavailable.") + endif() +endif() + +# ----------------------------------------------------------------------------- +# tests + +option(BUILD_TESTS "Whether or not to build the tests" ON) + +set(ZMQ_BUILD_TESTS + ${BUILD_TESTS} + CACHE BOOL "Build the tests for ZeroMQ") + +if(ZMQ_BUILD_TESTS) + enable_testing() # Enable testing only works in root scope + add_subdirectory(tests) + if(BUILD_STATIC) + add_subdirectory(unittests) + else() + message(WARNING "Not building unit tests, since BUILD_STATIC is not enabled") + endif() +endif() + +# ----------------------------------------------------------------------------- +# installer + +if(MSVC AND (BUILD_SHARED OR BUILD_STATIC)) + install( + TARGETS ${target_outputs} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT SDK) + if(MSVC_IDE) + install( + FILES ${PDB_OUTPUT_DIRECTORY}/\${CMAKE_INSTALL_CONFIG_NAME}/${PDB_NAME}.pdb + DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT SDK + OPTIONAL) + else() + install( + FILES ${PDB_OUTPUT_DIRECTORY}/${PDB_NAME}.pdb + DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT SDK + OPTIONAL) + endif() + if(BUILD_SHARED) + install( + TARGETS libzmq + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT Runtime) + endif() +elseif(BUILD_SHARED OR BUILD_STATIC) + install( + TARGETS ${target_outputs} + EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + FRAMEWORK DESTINATION "Library/Frameworks" + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif() + +foreach(readme ${readme-docs}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${readme} ${CMAKE_CURRENT_BINARY_DIR}/${readme}.txt) + + if(NOT ZMQ_BUILD_FRAMEWORK) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${readme}.txt DESTINATION share/zmq) + endif() +endforeach() + +if(WITH_DOC) + if(NOT ZMQ_BUILD_FRAMEWORK) + install( + FILES ${html-docs} + DESTINATION doc/zmq + COMPONENT RefGuide) + endif() +endif() + +if(WIN32) + set(ZEROMQ_CMAKECONFIG_INSTALL_DIR + "CMake" + CACHE STRING "install path for ZeroMQConfig.cmake") +else() + # CMake search path wants either "share" (AKA GNUInstallDirs DATAROOTDIR) for arch-independent, or LIBDIR for arch- + # dependent, plus "cmake" as prefix + set(ZEROMQ_CMAKECONFIG_INSTALL_DIR + "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + CACHE STRING "install path for ZeroMQConfig.cmake") +endif() + +if((NOT CMAKE_VERSION VERSION_LESS 3.0) AND (BUILD_SHARED OR BUILD_STATIC)) + export(EXPORT ${PROJECT_NAME}-targets FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") +endif() +configure_package_config_file( + builds/cmake/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${ZEROMQ_CMAKECONFIG_INSTALL_DIR}) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${ZMQ_VERSION_MAJOR}.${ZMQ_VERSION_MINOR}.${ZMQ_VERSION_PATCH} + COMPATIBILITY AnyNewerVersion) +if(BUILD_SHARED OR BUILD_STATIC) + install( + EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}Targets.cmake + DESTINATION ${ZEROMQ_CMAKECONFIG_INSTALL_DIR}) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION ${ZEROMQ_CMAKECONFIG_INSTALL_DIR}) +endif() + +option(ENABLE_CPACK "Enables cpack rules" ON) +if(MSVC AND ENABLE_CPACK) + if(${CMAKE_BUILD_TYPE} MATCHES "Debug") + set(CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY TRUE) + set(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) + set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) + endif() + include(InstallRequiredSystemLibraries) + + if(CMAKE_CL_64) + set(arch_name "x64") + else() + set(arch_name "x86") + endif() + + set(CPACK_NSIS_DISPLAY_NAME "ZeroMQ ${ZMQ_VERSION_MAJOR}.${ZMQ_VERSION_MINOR}.${ZMQ_VERSION_PATCH}(${arch_name})") + set(CPACK_PACKAGE_FILE_NAME "ZeroMQ-${ZMQ_VERSION_MAJOR}.${ZMQ_VERSION_MINOR}.${ZMQ_VERSION_PATCH}-${arch_name}") + + # TODO: I think this part was intended to be used when running cpack separately from cmake but I don't know how that + # works. + # + # macro(add_crt_version version) set(rel_dir + # "${CMAKE_CURRENT_BINARY_DIR}/build/${arch_name}/${version};ZeroMQ;ALL;/") + # set(debug_dir + # "${CMAKE_CURRENT_BINARY_DIR}/debug/${arch_name}/${version};ZeroMQ;ALL;/") + # if(EXISTS ${rel_dir}) list(APPEND CPACK_INSTALL_CMAKE_PROJECTS ${rel_dir}) endif() + + # if(EXISTS ${debug_dir}) list(APPEND CPACK_INSTALL_CMAKE_PROJECTS ${rel_dir}) endmacro() endmacro() + + # add_crt_version(v110) add_crt_version(v100) add_crt_version(v90) + + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_BINARY_DIR}) + set(CPACK_GENERATOR "NSIS") + set(CPACK_PACKAGE_NAME "ZeroMQ") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ZeroMQ lightweight messaging kernel") + set(CPACK_PACKAGE_VENDOR "Miru") + set(CPACK_NSIS_CONTACT "Steven McCoy ") + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_BINARY_DIR}\\\\COPYING.txt") + # set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_BINARY_DIR}\\\\README.txt") set(CPACK_RESOURCE_FILE_WELCOME + # "${CMAKE_CURRENT_BINARY_DIR}\\\\WELCOME.txt") There is a bug in NSI that does not handle full unix paths properly. + # Make sure there is at least one set of four(4) backslashes. + set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\installer.ico") + set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\installer.ico") + + set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\branding.bmp") + set(CPACK_NSIS_COMPRESSOR "/SOLID lzma") + set(CPACK_PACKAGE_VERSION ${ZMQ_VERSION}) + set(CPACK_PACKAGE_VERSION_MAJOR ${ZMQ_VERSION_MAJOR}) + set(CPACK_PACKAGE_VERSION_MINOR ${ZMQ_VERSION_MINOR}) + set(CPACK_PACKAGE_VERSION_PATCH ${ZMQ_VERSION_PATCH}) + # set(CPACK_PACKAGE_INSTALL_DIRECTORY "ZMQ Install Directory") set(CPACK_TEMPORARY_DIRECTORY "ZMQ Temporary CPack + # Directory") + + include(CPack) + + cpack_add_component_group(Development DISPLAY_NAME "ZeroMQ software development kit" EXPANDED) + cpack_add_component(PerfTools DISPLAY_NAME "ZeroMQ performance tools" INSTALL_TYPES FullInstall DevInstall) + cpack_add_component(SourceCode DISPLAY_NAME "ZeroMQ source code" DISABLED INSTALL_TYPES FullInstall) + cpack_add_component( + SDK + DISPLAY_NAME + "ZeroMQ headers and libraries" + INSTALL_TYPES + FullInstall + DevInstall + GROUP + Development) + if(WITH_DOC) + cpack_add_component( + RefGuide + DISPLAY_NAME + "ZeroMQ reference guide" + INSTALL_TYPES + FullInstall + DevInstall + GROUP + Development) + endif() + cpack_add_component( + Runtime + DISPLAY_NAME + "ZeroMQ runtime files" + REQUIRED + INSTALL_TYPES + FullInstall + DevInstall + MinInstall) + cpack_add_install_type(FullInstall DISPLAY_NAME "Full install, including source code") + cpack_add_install_type(DevInstall DISPLAY_NAME "Developer install, headers and libraries") + cpack_add_install_type(MinInstall DISPLAY_NAME "Minimal install, runtime only") +endif() + +# Export this for library to help build this as a sub-project +set(ZEROMQ_LIBRARY + libzmq + CACHE STRING "ZeroMQ library") + +# Workaround for MSVS10 to avoid the Dialog Hell FIXME: This could be removed with future version of CMake. +if(MSVC_VERSION EQUAL 1600) + set(ZMQ_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/ZeroMQ.sln") + if(EXISTS "${ZMQ_SLN_FILENAME}") + file(APPEND "${ZMQ_SLN_FILENAME}" "\n# This should be regenerated!\n") + endif() +endif() + +# this cannot be moved, as it does not only contain function/macro definitions +option(ENABLE_CLANG "Include Clang" ON) +if (ENABLE_CLANG) + include(ClangFormat) +endif() + +# fixes https://github.com/zeromq/libzmq/issues/3776 The problem is, both libzmq-static libzmq try to use/generate +# precompiled.pch at the same time Add a dependency, so they run in order and so they dont get in each others way TODO +# still generates warning "build\x64-Debug\ninja : warning : multiple rules generate precompiled.hpp. builds involving +# this target will not be correct; continuing anyway [-w dupbuild=warn]" +if(MSVC + AND ENABLE_PRECOMPILED + AND BUILD_SHARED + AND BUILD_STATIC) + add_dependencies(libzmq-static libzmq) +endif() diff --git a/3rd/libzmq/COPYING b/3rd/libzmq/COPYING new file mode 100644 index 00000000..b6f3fd5d --- /dev/null +++ b/3rd/libzmq/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/3rd/libzmq/COPYING.LESSER b/3rd/libzmq/COPYING.LESSER new file mode 100644 index 00000000..02e943c4 --- /dev/null +++ b/3rd/libzmq/COPYING.LESSER @@ -0,0 +1,181 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +-------------------------------------------------------------------------------- + + SPECIAL EXCEPTION GRANTED BY COPYRIGHT HOLDERS + +As a special exception, copyright holders 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. + +Note: this exception relieves you of any obligations under sections 4 and 5 +of this license, and section 6 of the GNU General Public License. diff --git a/3rd/libzmq/Jenkinsfile b/3rd/libzmq/Jenkinsfile new file mode 100644 index 00000000..3593665c --- /dev/null +++ b/3rd/libzmq/Jenkinsfile @@ -0,0 +1,485 @@ +pipeline { + agent { label "linux || macosx || bsd || solaris || posix || windows" } + parameters { + // Use DEFAULT_DEPLOY_BRANCH_PATTERN and DEFAULT_DEPLOY_JOB_NAME if + // defined in this jenkins setup -- in Jenkins Management Web-GUI + // see Configure System / Global properties / Environment variables + // Default (if unset) is empty => no deployment attempt after good test + // See zproject Jenkinsfile-deploy.example for an example deploy job. + // TODO: Try to marry MultiBranchPipeline support with pre-set defaults + // directly in MultiBranchPipeline plugin, or mechanism like Credentials, + // or a config file uploaded to master for all jobs or this job, see + // https://jenkins.io/doc/pipeline/examples/#configfile-provider-plugin + string ( + defaultValue: '${DEFAULT_DEPLOY_BRANCH_PATTERN}', + description: 'Regular expression of branch names for which a deploy action would be attempted after a successful build and test; leave empty to not deploy. Reasonable value is ^(master|release/.*|feature/*)$', + name : 'DEPLOY_BRANCH_PATTERN') + string ( + defaultValue: '${DEFAULT_DEPLOY_JOB_NAME}', + description: 'Name of your job that handles deployments and should accept arguments: DEPLOY_GIT_URL DEPLOY_GIT_BRANCH DEPLOY_GIT_COMMIT -- and it is up to that job what to do with this knowledge (e.g. git archive + push to packaging); leave empty to not deploy', + name : 'DEPLOY_JOB_NAME') + booleanParam ( + defaultValue: true, + description: 'If the deployment is done, should THIS job wait for it to complete and include its success or failure as the build result (true), or should it schedule the job and exit quickly to free up the executor (false)', + name: 'DEPLOY_REPORT_RESULT') + booleanParam ( + defaultValue: true, + description: 'Attempt stable build without DRAFT API in this run?', + name: 'DO_BUILD_WITHOUT_DRAFT_API') + booleanParam ( + defaultValue: true, + description: 'Attempt build with DRAFT API in this run?', + name: 'DO_BUILD_WITH_DRAFT_API') + booleanParam ( + defaultValue: true, + description: 'Attempt a build with docs in this run? (Note: corresponding tools are required in the build environment)', + name: 'DO_BUILD_DOCS') + booleanParam ( + defaultValue: false, + description: 'Publish as an archive a "dist" tarball from a build with docs in this run? (Note: corresponding tools are required in the build environment; enabling this enforces DO_BUILD_DOCS too)', + name: 'DO_DIST_DOCS') + booleanParam ( + defaultValue: true, + description: 'Attempt "make check" in this run?', + name: 'DO_TEST_CHECK') + booleanParam ( + defaultValue: false, + description: 'Attempt "make memcheck" in this run?', + name: 'DO_TEST_MEMCHECK') + booleanParam ( + defaultValue: true, + description: 'Attempt "make distcheck" in this run?', + name: 'DO_TEST_DISTCHECK') + booleanParam ( + defaultValue: true, + description: 'Attempt a "make install" check in this run?', + name: 'DO_TEST_INSTALL') + string ( + defaultValue: "`pwd`/tmp/_inst", + description: 'If attempting a "make install" check in this run, what DESTDIR to specify? (absolute path, defaults to "BUILD_DIR/tmp/_inst")', + name: 'USE_TEST_INSTALL_DESTDIR') + booleanParam ( + defaultValue: true, + description: 'Attempt "cppcheck" analysis before this run? (Note: corresponding tools are required in the build environment)', + name: 'DO_CPPCHECK') + booleanParam ( + defaultValue: true, + description: 'Require that there are no files not discovered changed/untracked via .gitignore after builds and tests?', + name: 'REQUIRE_GOOD_GITIGNORE') + string ( + defaultValue: "30", + description: 'When running tests, use this timeout (in minutes; be sure to leave enough for double-job of a distcheck too)', + name: 'USE_TEST_TIMEOUT') + booleanParam ( + defaultValue: true, + description: 'When using temporary subdirs in build/test workspaces, wipe them after successful builds?', + name: 'DO_CLEANUP_AFTER_BUILD') + } + triggers { + pollSCM 'H/2 * * * *' + } +// Note: your Jenkins setup may benefit from similar setup on side of agents: +// PATH="/usr/lib64/ccache:/usr/lib/ccache:/usr/bin:/bin:${PATH}" + stages { + stage ('prepare') { + steps { + dir("tmp") { + sh 'if [ -s Makefile ]; then make -k distclean || true ; fi' + sh 'chmod -R u+w .' + deleteDir() + } + sh './autogen.sh' + stash (name: 'prepped', includes: '**/*', excludes: '**/cppcheck.xml') + } + } + stage ('compile') { + parallel { + stage ('build with DRAFT') { + when { expression { return ( params.DO_BUILD_WITH_DRAFT_API ) } } + steps { + dir("tmp/build-withDRAFT") { + deleteDir() + unstash 'prepped' + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; ./configure --enable-drafts=yes --with-docs=no' + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; make -k -j4 || make' + sh 'echo "Are GitIgnores good after make with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + stash (name: 'built-draft', includes: '**/*', excludes: '**/cppcheck.xml') + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('build without DRAFT') { + when { expression { return ( params.DO_BUILD_WITHOUT_DRAFT_API ) } } + steps { + dir("tmp/build-withoutDRAFT") { + deleteDir() + unstash 'prepped' + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; ./configure --enable-drafts=no --with-docs=no' + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; make -k -j4 || make' + sh 'echo "Are GitIgnores good after make without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + stash (name: 'built-nondraft', includes: '**/*', excludes: '**/cppcheck.xml') + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('build with DOCS') { + when { expression { return ( params.DO_BUILD_DOCS || params.DO_DIST_DOCS ) } } + steps { + dir("tmp/build-DOCS") { + deleteDir() + unstash 'prepped' + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; ./configure --enable-drafts=yes --with-docs=yes' + script { + if ( params.DO_DIST_DOCS ) { + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; make dist-gzip || exit ; DISTFILE="`ls -1tc *.tar.gz | head -1`" && [ -n "$DISTFILE" ] && [ -s "$DISTFILE" ] || exit ; mv -f "$DISTFILE" __dist.tar.gz' + archiveArtifacts artifacts: '__dist.tar.gz' + sh "rm -f __dist.tar.gz" + } + } + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; make -k -j4 || make' + sh 'echo "Are GitIgnores good after make with docs? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + stash (name: 'built-docs', includes: '**/*', excludes: '**/cppcheck.xml') + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + } + } + stage ('check') { + parallel { + stage ('cppcheck') { + when { expression { return ( params.DO_CPPCHECK ) } } + steps { + dir("tmp/test-cppcheck") { + deleteDir() + unstash 'prepped' + sh 'cppcheck --std=c++11 --enable=all --inconclusive --xml --xml-version=2 . 2>cppcheck.xml' + archiveArtifacts artifacts: '**/cppcheck.xml' + sh 'rm -f cppcheck.xml' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('check with DRAFT') { + when { expression { return ( params.DO_BUILD_WITH_DRAFT_API && params.DO_TEST_CHECK ) } } + steps { + dir("tmp/test-check-withDRAFT") { + deleteDir() + unstash 'built-draft' + script { + def RETRY_NUMBER = 0 + retry(3) { + RETRY_NUMBER++ + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + try { + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" check' + } + catch (Exception e) { + currentBuild.result = 'UNSTABLE' // Jenkins should not let the verdict "improve" + sh """D="`pwd`"; B="`basename "\$D"`" ; [ "${RETRY_NUMBER}" -gt 0 ] && T="_try-${RETRY_NUMBER}" || T="" ; tar czf "test-suite_${BUILD_TAG}_\${B}\${T}.tar.gz" `find . -name '*.trs'` `find . -name '*.log'`""" + archiveArtifacts artifacts: "**/test-suite*.tar.gz", allowEmpty: true + throw e + } + } + } + } + sh 'echo "Are GitIgnores good after make check with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('check without DRAFT') { + when { expression { return ( params.DO_BUILD_WITHOUT_DRAFT_API && params.DO_TEST_CHECK ) } } + steps { + dir("tmp/test-check-withoutDRAFT") { + deleteDir() + unstash 'built-nondraft' + script { + def RETRY_NUMBER = 0 + retry(3) { + RETRY_NUMBER++ + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + try { + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" check' + } + catch (Exception e) { + currentBuild.result = 'UNSTABLE' // Jenkins should not let the verdict "improve" + sh """D="`pwd`"; B="`basename "\$D"`" ; [ "${RETRY_NUMBER}" -gt 0 ] && T="_try-${RETRY_NUMBER}" || T="" ; tar czf "test-suite_${BUILD_TAG}_\${B}\${T}.tar.gz" `find . -name '*.trs'` `find . -name '*.log'`""" + archiveArtifacts artifacts: "**/test-suite*.tar.gz", allowEmpty: true + throw e + } + } + } + } + sh 'echo "Are GitIgnores good after make check without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('memcheck with DRAFT') { + when { expression { return ( params.DO_BUILD_WITH_DRAFT_API && params.DO_TEST_MEMCHECK ) } } + steps { + dir("tmp/test-memcheck-withDRAFT") { + deleteDir() + unstash 'built-draft' + script { + def RETRY_NUMBER = 0 + retry(3) { + RETRY_NUMBER++ + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + try { + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" memcheck && exit 0 ; echo "Re-running failed ($?) memcheck with greater verbosity" >&2 ; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" VERBOSE=1 memcheck-verbose' + } + catch (Exception e) { + currentBuild.result = 'UNSTABLE' // Jenkins should not let the verdict "improve" + sh """D="`pwd`"; B="`basename "\$D"`" ; [ "${RETRY_NUMBER}" -gt 0 ] && T="_try-${RETRY_NUMBER}" || T="" ; tar czf "test-suite_${BUILD_TAG}_\${B}\${T}.tar.gz" `find . -name '*.trs'` `find . -name '*.log'`""" + archiveArtifacts artifacts: "**/test-suite*.tar.gz", allowEmpty: true + throw e + } + } + } + } + sh 'echo "Are GitIgnores good after make memcheck with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('memcheck without DRAFT') { + when { expression { return ( params.DO_BUILD_WITHOUT_DRAFT_API && params.DO_TEST_MEMCHECK ) } } + steps { + dir("tmp/test-memcheck-withoutDRAFT") { + deleteDir() + unstash 'built-nondraft' + script { + def RETRY_NUMBER = 0 + retry(3) { + RETRY_NUMBER++ + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + try { + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" memcheck && exit 0 ; echo "Re-running failed ($?) memcheck with greater verbosity" >&2 ; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" VERBOSE=1 memcheck-verbose' + } + catch (Exception e) { + currentBuild.result = 'UNSTABLE' // Jenkins should not let the verdict "improve" + sh """D="`pwd`"; B="`basename "\$D"`" ; [ "${RETRY_NUMBER}" -gt 0 ] && T="_try-${RETRY_NUMBER}" || T="" ; tar czf "test-suite_${BUILD_TAG}_\${B}\${T}.tar.gz" `find . -name '*.trs'` `find . -name '*.log'`""" + archiveArtifacts artifacts: "**/test-suite*.tar.gz", allowEmpty: true + throw e + } + } + } + } + sh 'echo "Are GitIgnores good after make memcheck without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('distcheck with DRAFT') { + when { expression { return ( params.DO_BUILD_WITH_DRAFT_API && params.DO_TEST_DISTCHECK ) } } + steps { + dir("tmp/test-distcheck-withDRAFT") { + deleteDir() + unstash 'built-draft' + script { + def RETRY_NUMBER = 0 + retry(3) { + RETRY_NUMBER++ + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + try { + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; DISTCHECK_CONFIGURE_FLAGS="--enable-drafts=yes --with-docs=no" ; export DISTCHECK_CONFIGURE_FLAGS; make DISTCHECK_CONFIGURE_FLAGS="$DISTCHECK_CONFIGURE_FLAGS" LD_LIBRARY_PATH="$LD_LIBRARY_PATH" distcheck' + } + catch (Exception e) { + currentBuild.result = 'UNSTABLE' // Jenkins should not let the verdict "improve" + sh """D="`pwd`"; B="`basename "\$D"`" ; [ "${RETRY_NUMBER}" -gt 0 ] && T="_try-${RETRY_NUMBER}" || T="" ; tar czf "test-suite_${BUILD_TAG}_\${B}\${T}.tar.gz" `find . -name '*.trs'` `find . -name '*.log'`""" + archiveArtifacts artifacts: "**/test-suite*.tar.gz", allowEmpty: true + throw e + } + } + } + } + sh 'echo "Are GitIgnores good after make distcheck with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('distcheck without DRAFT') { + when { expression { return ( params.DO_BUILD_WITHOUT_DRAFT_API && params.DO_TEST_DISTCHECK ) } } + steps { + dir("tmp/test-distcheck-withoutDRAFT") { + deleteDir() + unstash 'built-nondraft' + script { + def RETRY_NUMBER = 0 + retry(3) { + RETRY_NUMBER++ + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + try { + sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; DISTCHECK_CONFIGURE_FLAGS="--enable-drafts=no --with-docs=no" ; export DISTCHECK_CONFIGURE_FLAGS; make DISTCHECK_CONFIGURE_FLAGS="$DISTCHECK_CONFIGURE_FLAGS" LD_LIBRARY_PATH="$LD_LIBRARY_PATH" distcheck' + } + catch (Exception e) { + currentBuild.result = 'UNSTABLE' // Jenkins should not let the verdict "improve" + sh """D="`pwd`"; B="`basename "\$D"`" ; [ "${RETRY_NUMBER}" -gt 0 ] && T="_try-${RETRY_NUMBER}" || T="" ; tar czf "test-suite_${BUILD_TAG}_\${B}\${T}.tar.gz" `find . -name '*.trs'` `find . -name '*.log'`""" + archiveArtifacts artifacts: "**/test-suite*.tar.gz", allowEmpty: true + throw e + } + } + } + } + sh 'echo "Are GitIgnores good after make distcheck without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('install with DRAFT') { + when { expression { return ( params.DO_BUILD_WITH_DRAFT_API && params.DO_TEST_INSTALL ) } } + steps { + dir("tmp/test-install-withDRAFT") { + deleteDir() + unstash 'built-draft' + retry(3) { + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + sh """CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:\${LD_LIBRARY_PATH}"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="\${LD_LIBRARY_PATH}" DESTDIR="${params.USE_TEST_INSTALL_DESTDIR}/withDRAFT" install""" + } + } + sh """cd "${params.USE_TEST_INSTALL_DESTDIR}/withDRAFT" && find . -ls""" + sh 'echo "Are GitIgnores good after make install with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('install without DRAFT') { + when { expression { return ( params.DO_BUILD_WITHOUT_DRAFT_API && params.DO_TEST_INSTALL ) } } + steps { + dir("tmp/test-install-withoutDRAFT") { + deleteDir() + unstash 'built-nondraft' + retry(3) { + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + sh """CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:\${LD_LIBRARY_PATH}"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="\${LD_LIBRARY_PATH}" DESTDIR="${params.USE_TEST_INSTALL_DESTDIR}/withoutDRAFT" install""" + } + } + sh """cd "${params.USE_TEST_INSTALL_DESTDIR}/withoutDRAFT" && find . -ls""" + sh 'echo "Are GitIgnores good after make install without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + stage ('install with DOCS') { + when { expression { return ( params.DO_BUILD_DOCS && params.DO_TEST_INSTALL ) } } + steps { + dir("tmp/test-install-withDOCS") { + deleteDir() + unstash 'built-docs' + retry(3) { + timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') { + sh """CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:\${LD_LIBRARY_PATH}"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="\${LD_LIBRARY_PATH}" DESTDIR="${params.USE_TEST_INSTALL_DESTDIR}/withDOCS" install""" + } + } + sh """cd "${params.USE_TEST_INSTALL_DESTDIR}/withDOCS" && find . -ls""" + sh 'echo "Are GitIgnores good after make install with Docs? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi' + script { + if ( params.DO_CLEANUP_AFTER_BUILD ) { + deleteDir() + } + } + } + } + } + } + } + stage ('deploy if appropriate') { + steps { + script { + def myDEPLOY_JOB_NAME = sh(returnStdout: true, script: """echo "${params["DEPLOY_JOB_NAME"]}" """).trim(); + def myDEPLOY_BRANCH_PATTERN = sh(returnStdout: true, script: """echo "${params["DEPLOY_BRANCH_PATTERN"]}" """).trim(); + def myDEPLOY_REPORT_RESULT = sh(returnStdout: true, script: """echo "${params["DEPLOY_REPORT_RESULT"]}" """).trim().toBoolean(); + echo "Original: DEPLOY_JOB_NAME : ${params["DEPLOY_JOB_NAME"]} DEPLOY_BRANCH_PATTERN : ${params["DEPLOY_BRANCH_PATTERN"]} DEPLOY_REPORT_RESULT : ${params["DEPLOY_REPORT_RESULT"]}" + echo "Used: myDEPLOY_JOB_NAME:${myDEPLOY_JOB_NAME} myDEPLOY_BRANCH_PATTERN:${myDEPLOY_BRANCH_PATTERN} myDEPLOY_REPORT_RESULT:${myDEPLOY_REPORT_RESULT}" + if ( (myDEPLOY_JOB_NAME != "") && (myDEPLOY_BRANCH_PATTERN != "") ) { + if ( env.BRANCH_NAME =~ myDEPLOY_BRANCH_PATTERN ) { + def GIT_URL = sh(returnStdout: true, script: """git remote -v | egrep '^origin' | awk '{print \$2}' | head -1""").trim() + def GIT_COMMIT = sh(returnStdout: true, script: 'git rev-parse --verify HEAD').trim() + def DIST_ARCHIVE = "" + if ( params.DO_DIST_DOCS ) { DIST_ARCHIVE = env.BUILD_URL + "artifact/__dist.tar.gz" } + build job: "${myDEPLOY_JOB_NAME}", parameters: [ + string(name: 'DEPLOY_GIT_URL', value: "${GIT_URL}"), + string(name: 'DEPLOY_GIT_BRANCH', value: env.BRANCH_NAME), + string(name: 'DEPLOY_GIT_COMMIT', value: "${GIT_COMMIT}"), + string(name: 'DEPLOY_DIST_ARCHIVE', value: "${DIST_ARCHIVE}") + ], quietPeriod: 0, wait: myDEPLOY_REPORT_RESULT, propagate: myDEPLOY_REPORT_RESULT + } else { + echo "Not deploying because branch '${env.BRANCH_NAME}' did not match filter '${myDEPLOY_BRANCH_PATTERN}'" + } + } else { + echo "Not deploying because deploy-job parameters are not set" + } + } + } + } + } + post { + success { + script { + if (currentBuild.getPreviousBuild()?.result != 'SUCCESS') { + // Uncomment desired notification + + //slackSend (color: "#008800", message: "Build ${env.JOB_NAME} is back to normal.") + //emailext (to: "qa@example.com", subject: "Build ${env.JOB_NAME} is back to normal.", body: "Build ${env.JOB_NAME} is back to normal.") + } + } + } + failure { + // Uncomment desired notification + // Section must not be empty, you can delete the sleep once you set notification + sleep 1 + //slackSend (color: "#AA0000", message: "Build ${env.BUILD_NUMBER} of ${env.JOB_NAME} ${currentBuild.result} (<${env.BUILD_URL}|Open>)") + //emailext (to: "qa@example.com", subject: "Build ${env.JOB_NAME} failed!", body: "Build ${env.BUILD_NUMBER} of ${env.JOB_NAME} ${currentBuild.result}\nSee ${env.BUILD_URL}") + } + } +} diff --git a/3rd/libzmq/NEWS b/3rd/libzmq/NEWS new file mode 100644 index 00000000..c52a1d48 --- /dev/null +++ b/3rd/libzmq/NEWS @@ -0,0 +1,2208 @@ +0MQ version 4.3.4 stable, released on 2020/01/17 +================================================ + +* New DRAFT (see NEWS for 4.2.0) socket option: + - ZMQ_PRIORITY will set the SO_PRIORITY socket option on the underlying + sockets. Only supported on Linux. + See doc/zmq_setsockopt.txt and doc/zmq_getsockopt.txt for details. + +* Fixed #4113 - compilation errors on kFreeBSD and GNU/Hurd + +* Fixed #4086 - excessive amount of socket files left behind in Windows TMP + directory + +* Fixed #4108 - regression that breaks using IPv6 link-local addresses on Linux + +* Fixed #4078 - compilation errors on Android + +* Fixed #4074 - compilation error with ulibc and libbsd + +* Fixed #4060 - stack overflow on Windows x64 + +* Fixed #4051 - various compilation errors on Windows ARM 32bit + +* Fixed #4043 - various compilation warnings with XCode + +* Fixed #4038 - return value of zmq_ctx_get changed unintentionally + + +0MQ version 4.3.3 stable, released on 2020/09/07 +================================================ + +* Security advisories: + * CVE-2020-15166: Denial-of-Service on CURVE/ZAP-protected servers by + unauthenticated clients. + If a raw TCP socket is opened and connected to an endpoint that is fully + configured with CURVE/ZAP, legitimate clients will not be able to exchange + any message. Handshakes complete successfully, and messages are delivered to + the library, but the server application never receives them. + For more information see the security advisory: + https://github.com/zeromq/libzmq/security/advisories/GHSA-25wp-cf8g-938m + * Stack overflow on server running PUB/XPUB socket (CURVE disabled). + The PUB/XPUB subscription store (mtrie) is traversed using recursive + function calls. In the remove (unsubscription) case, the recursive calls are + NOT tail calls, so even with optimizations the stack grows linearly with the + length of a subscription topic. Topics are under the control of remote + clients - they can send a subscription to arbitrary length topics. An + attacker can thus cause a server to create an mtrie sufficiently large such + that, when unsubscribing, traversal will cause a stack overflow. + For more information see the security advisory: + https://github.com/zeromq/libzmq/security/advisories/GHSA-qq65-x72m-9wr8 + * Memory leak in PUB server induced by malicious client(s) without CURVE/ZAP. + Messages with metadata are never processed by PUB sockets, but the metadata + is kept referenced in the PUB object and never freed. + For more information see the security advisory: + https://github.com/zeromq/libzmq/security/advisories/GHSA-4p5v-h92w-6wxw + * Memory leak in client induced by malicious server(s) without CURVE/ZAP. + When a pipe processes a delimiter and is already not in active state but + still has an unfinished message, the message is leaked. + For more information see the security advisory: + https://github.com/zeromq/libzmq/security/advisories/GHSA-wfr2-29gj-5w87 + * Heap overflow when receiving malformed ZMTP v1 packets (CURVE disabled). + By crafting a packet which is not valid ZMTP v2/v3, and which has two + messages larger than 8192 bytes, the decoder can be tricked into changing + the recorded size of the 8192 bytes static buffer, which then gets overflown + by the next message. The content that gets written in the overflown memory + is entirely decided by the sender. + For more information see the security advisory: + https://github.com/zeromq/libzmq/security/advisories/GHSA-fc3w-qxf5-7hp6 + +* Note for packagers: an external, self-contained sha1 library is now + included in the source tree under external/sha1/ - it is licensed + under BSD-3-Clause and thus it is fully compatible with libzmq's + license. + It is only used if WebSockets support is enabled, and if neither GnuTLS nor + NSS are available. + +* Note for packagers: an internal reimplementation of strlcpy is now included, + for wider platform compatibility. + libbsd can be used and is enabled by default if available instead of the + internal implementation, for better security maintenance in distros. + +* Note for packagers: ZeroMQConfig.cmake is now installed in the arch-dependent + subdirectory - eg: /usr/lib/x86_64-linux-gnu/cmake/ + +* New DRAFT (see NEWS for 4.2.0) socket type: + - ZMQ_CHANNEL is a thread-safe alternative to ZMQ_PAIR. + See doc/zmq_socket.txt for details. + +* New DRAFT (see NEWS for 4.2.0) socket option: + - ZMQ_ONLY_FIRST_SUBSCRIBE will cause only the first part of a multipart + message to be processed as a subscribe/unsubscribe message, and the rest + will be forwarded as user data to the application. + - ZMQ_RECONNECT_STOP will cause a connecting socket to stop trying to + reconnect in specific circumstances. See the manpage for details. + - ZMQ_HELLO_MSG to set a message that will be automatically sent to a new + connection. + - ZMQ_DISCONNECT_MSG to set a message that will be automatically received when + a peer disconnects. + See doc/zmq_setsockopt.txt and doc/zmq_getsockopt.txt for details. + +* New DRAFT (see NEWS for 4.2.0) zmq_ctx_get_ext/zmq_ctx_set_ext APIs were added + to allow enhancing the context options with variable data inputs. + See doc/zmq_ctx_get_ext.txt and doc/zmq_ctx_set_ext.txt for details. + +* New DRAFT (see NEWS for 4.2.0) transport options WS and WSS added for support + of WebSockets (and secure WebSockets via TLS) via the ZWS 2.0 protocol. + WSS requires the GnuTLS library for TLS support. ZMQ_WSS_ specific socket + options were added to support TLS. + WebSockets support is disabled by default if DRAFT APIs are disabled. + +* New DRAFT (see NEWS for 4.2.0) socket type, PEER, which is thread safe and a + related zmq_connect_peer function which atomically and thread-safely connects + and returns a routing-id. + +* New DRAFT (see NEWS for 4.2.0) zmq_msg_init_buffer API was added to allow + the construction of a message by copying from an existing buffer. + +* New DRAFT (see NEWS for 4.2.0) zmq_poller_size API was added to allow querying + the number of sockets/fds registered in a zmq_poller. + +* ZMTP 3.1 peers will receive subscribe/cancel on PUB/SUB via commands rather + than using the first byte of the payload. + +* zmq_z85_decode now checks that the input string's length is at least 5 characters + and always a multiple of 5 as per API specification. + +* Fixed #3566 - malformed CURVE message can cause memory leak + +* Fixed #3567 - missing ZeroMQ_INCLUDE_DIR in ZeroMQConfig.cmake when only + static lib is built + +* Fixed #3576 - CURVE plaintext secrets now stored in libsodium's secure memory + +* Fixed #3588 - install debug libraries for debug msvc builds with CMake + +* Fixed #3591 - incorrect ZMQ_MAX_SOCKETS default value in doc + +* Fixed #3594 - fixed stream_engine use after free due to concurrent heartbeats + +* Fixed #3586 - error when compiling with MinGW due to usage of MS-specific + __except keyword + +* Fixed #3603 - fixed CMake build on SL6.9 + +* Fixed #3607 - added scripts to ease performance graph generation + +* Fixed #3608 - fix for IPv4 mapping not supported in DragonFlyBSD + +* Fixed #3636 - added ENABLE_PRECOMPILED CMake option to fix build with Ninja + +* Fixed #2862 - UDP engine aborts on networking-related errors from socket + syscalls + +* Fixed #3656 - segfault on sending data from XSUB to XPUB + +* Fixed #3646 - static-only test run fails + +* Fixed #3668 - fixed CMAKE_CXX_FLAGS_* regexes on MSVC + +* Fixed #110 - do not include winsock2.h in public zmq.h header + +* Fixed #3683 - allow "configure --disable-maintainer-mode" + +* Fixed #3686 - fix documentation about sockets blocking on send operations + +* Fixed #3323 - fix behavior of ZMQ_CONFLATE on PUB sockets + +* Fixed #3698 - fix build on IBM i/PASE/os400 + +* Fixed #3705 - zero-sized messages cause assertion when glibc assertion are on + +* Fixed #3713 - remove dependency on math library by avoiding std::ceil + +* Fixed #3694 - build targeting Windows XP is broken + +* Fixed #3691 - added support for IPC on Windows 10 via AF_UNIX + +* Fixed #3725 - disable by default test that requires sudo on CMake + +* Fixed #3727 - fix zmq_poller documentation example + +* Fixed #3729 - do not check for FD_OOB when using WSAEventSelect on Windows + +* Fixed #3738 - allow renaming the library in CMake + +* Fixed #1808 - use AF_UNIX instead of TCP for the internal socket on Windows 10 + +* Fixed #3758 - fix pthread_set_affinity detection in CMake + +* Fixed #3769 - fix undefined behaviour in array.hpp + +* Fixed #3772 - fix compiling under msys2-mingw + +* Fixed #3775 - add -latomic to the private libs flag in pkg-config if needed + +* Fixed #3778 - fix documentation of zmq_poller's thread safety + +* Fixed #3792 - do not allow creation of new sockets after zmq_ctx_shutdown + +* Fixed #3805 - improve performance of CURVE by reducing copies + +* Fixed #3814 - send subscribe/cancel as commands to ZMTP 3.1 peers + +* Fixed #3847 - fix building without PGM and NORM + +* Fixed #3849 - install .cmake file in arch-dependent subdirectory + +* Fixed #4005 - allow building on Windows ARM/ARM64 + +0MQ version 4.3.2 stable, released on 2019/07/08 +================================================ + +* CVE-2019-13132: a remote, unauthenticated client connecting to a + libzmq application, running with a socket listening with CURVE + encryption/authentication enabled, may cause a stack overflow and + overwrite the stack with arbitrary data, due to a buffer overflow in + the library. Users running public servers with the above configuration + are highly encouraged to upgrade as soon as possible, as there are no + known mitigations. All versions from 4.0.0 and upwards are affected. + Thank you Fang-Pen Lin for finding the issue and reporting it! + +* New DRAFT (see NEWS for 4.2.0) zmq_socket_monitor_versioned API that supports + a versioned monitoring events protocol as a parameter. Passing 1 results in + the same behaviour as zmq_socket_monitor. + Version 2 of the events protocol allows new events, new metadata, different + socket types for the monitors and more. It is described in details in + doc/zmq_socket_monitor_versioned.txt + +* New DRAFT (see NEWS for 4.2.0) zmq_socket_monitor_pipes_stats that triggers + a new ZMQ_EVENT_PIPES_STATS to be delivered via zmq_socket_monitor_versioned + v2 API, which contains the current status of all the queues owned by the + monitored socket. See doc/zmq_socket_monitor_versioned.txt for details. + +* New DRAFT (see NEWS for 4.2.0) zmq_poller_fd that returns the FD of a thread + safe socket. See doc/zmq_poller.txt for details. + +* New DRAFT (see NEWS for 4.2.0) socket options: + - ZMQ_XPUB_MANUAL_LAST_VALUE is similar to ZMQ_XPUB_MANUAL but allows to avoid + duplicates when using last value caching. + - ZMQ_SOCKS_USERNAME and ZMQ_SOCKS_PASSWORD that implement SOCKS5 proxy + authentication. + See doc/zmq_setsockopt.txt and doc/zmq_getsockopt.txt for details. + +* Implemented background thread names for Windows, when the Visual Studio + debugger is being used. + +* Fixed #3358 - test_security_zap failing due to SIGBUS on SPARC64, hard-coded + IPC socket binds in tests cause race conditions + +* Fixed #3361 - enabling GSSAPI support (when using autools) does not work due + to regression introduced in 4.2.3 + +* Fixed #3362 - remove documentation for ZMQ_THREAD_PRIORITY context option + getter, it's not implemented + +* Fixed #3363 - tests fail to build due to stricter compiler printf validation + in new versions of GCC + +* Fixed #3367 - try to infer cacheline size at build time, first with + getconf LEVEL1_DCACHE_LINESIZE, and then by reading + /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size + (autoconf only), and only then falling back to the previous + default of 64 bytes. Avoids false sharing on POWER and s390x. + Import ax_func_posix_memalign.m4 as a more reliable check for + posix_memalign presence on some unix platforms. + Prefer c++11 atomic primitives to compiler intrinsics, when + both are available, as the former is more reliable. + Set test_pair_ipc and test_rebind_ipc to XFAIL on GNU/Hurd due + to non-functioning getsockname on AF_UNIX. + +* Fixed #3370 - Make queue length and HWM state observable + +* Fixed #3373 - performance regression in zmq_poll on CentOS 6/7 + +* Fixed #3375 - assign names to all pthreads created by the library to ease + debugging + +* Fixed #3376 - assigned random TIPC port is not returned by ZMQ_LAST_ENDPOINT + +* Fixed #3385 - TCP port in ZMQ_LAST_ENDPOINT depends on global locale + +* Fixed #3404 - use std::condition_variable_any when possible + +* Fixed #3436 - reconnect interval exponential backoff and may lead to integer + overflows + +* Fixed #3440 - improve zmq_proxy performance by batching of up to 1000 + consecutive messages (if any) and add perf/proxy_thr tool + +* Fixed #3451 - fix support of /dev/poll on Solaris + +* Fixed #3452 - strnlen may not be available + +* Fixed #1462 - test failure in test_filter_ipc due to invalid system groups + +* Fixed #3269 - Boost.ASIO integration stopped working with v4.3.0 + +* Fixed #3479 - ZeroMQ does not build for QNX 6.6 with CMake + +* Fixed #3481 - add include to fix uClibc++ compilation + +* Fixed #3491 - build broken on Fedora 30 + +* Fixed #3494 - ZeroMQConfig.cmake fails if shared libraries are not built + +* Fixed #3498 - syntax error on Windows related to socket descriptor type + +* Fixed #3500 - PLAIN HELLO message incorrectly uses WELCOME literal, regression + introduced in 4.3.0 + +* Fixed #3517 - configure errors because of syntax errors in the use of test + shell command + +* Fixed #3521 - document how to achieve high performance with the PGM transport + +* Fixed #3526 - failure case behavior unclear in zmq_msg_send documentation + +* Fixed #3537 - fix build on z/OS by using pthread_equal instead of comparing + variables directly + +* Fixed #3546 - CMake links with librt on MinGW which is not available + +* Many coding style, duplication, testing and static analysis improvements. + + +0MQ version 4.3.1 stable, released on 2019/01/12 +================================================ + +* CVE-2019-6250: A vulnerability has been found that would allow attackers to + direct a peer to jump to and execute from an address indicated by the + attacker. + This issue has been present since v4.2.0. Older releases are not affected. + NOTE: The attacker needs to know in advance valid addresses in the peer's + memory to jump to, so measures like ASLR are effective mitigations. + NOTE: this attack can only take place after authentication, so peers behind + CURVE/GSSAPI are not vulnerable to unauthenticated attackers. + See https://github.com/zeromq/libzmq/issues/3351 for more details. + Thanks to Guido Vranken for uncovering the issue and providing the fix! + +* Note for packagers: as pkg-config's Requires.private is now used to properly + propagate dependencies for static builds, the libzmq*-dev or zeromq-devel or + equivalent package should now depend on the libfoo-dev or foo-devel packages + of all the libraries that zmq is linked against, or pkg-config --libs libzmq + will fail due to missing dependencies on end users machines. + +* Fixed #3351 - remote code execution vulnerability. + +* Fixed #3343 - race condition in ZMQ_PUSH when quickly disconnecting and + reconnecting causes last part of multi-part message to get + "stuck" and resent by mistake to the new socket. + +* Fixed #3336 - set Requires.private in generate pkg-config file. + +* Fixed #3334 - set TCP_NODELAY after connect() on Windows for the I/O socket. + +* Fixed #3326 - assert on Android when opening a socket and disabling WiFi. + +* Fixed #3320 - build failure on OpenBSD with GCC. + +0MQ version 4.3.0 stable, released on 2018/11/28 +================================================ + +* The following DRAFT APIs have been marked as STABLE and will not change + anymore: + - ZMQ_MSG_T_SIZE context option (see doc/zmq_ctx_get.txt) + - ZMQ_THREAD_AFFINITY_CPU_ADD and ZMQ_THREAD_AFFINITY_CPU_REMOVE (Posix only) + context options, to add/remove CPUs to the affinity set of the I/O threads. + See doc/zmq_ctx_set.txt and doc/zmq_ctx_get.txt for details. + - ZMQ_THREAD_NAME_PREFIX (Posix only) context option, to add a specific + integer prefix to the background threads names, to easily identify them. + See doc/zmq_ctx_set.txt and doc/zmq_ctx_get.txt for details. + - ZMQ_GSSAPI_PRINCIPAL_NAMETYPE and ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE + socket options, for the corresponding GSSAPI features. Additional + definitions for principal name types: + - ZMQ_GSSAPI_NT_HOSTBASED + - ZMQ_GSSAPI_NT_USER_NAME + - ZMQ_GSSAPI_NT_KRB5_PRINCIPAL + See doc/zmq_gssapi.txt for details. + - ZMQ_BINDTODEVICE socket option (Linux only), which will bind the + socket(s) to the specified interface. Allows to use Linux VRF, see: + https://www.kernel.org/doc/Documentation/networking/vrf.txt + NOTE: requires the program to be ran as root OR with CAP_NET_RAW + - zmq_timers_* APIs. These functions can be used for cross-platforms timed + callbacks. See doc/zmq_timers.txt for details. + - The following socket monitor events: + - ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL: unknown errors during handshake. + - ZMQ_EVENT_HANDSHAKE_SUCCEEDED: Handshake completed with authentication. + - ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL: Protocol errors with peers or ZAP. + - ZMQ_EVENT_HANDSHAKE_FAILED_AUTH: Failed authentication requests. + See doc/zmq_socket_monitor.txt for more details and error codes. + - zmq_stopwatch_intermediate which returns the time elapsed without stopping + the stopwatch. + - zmq_proxy_steerable command 'STATISTICS' to retrieve stats about the amount + of messages and bytes sent and received by the proxy. + See doc/zmq_proxy_steerable.txt for more information. + +* The build-time configuration option to select the poller has been split, and + new API_POLLER (CMake) and --with-api-poller (autoconf) options will now + determine what system call is used to implement the zmq_poll/zmq_poller APIs. + The previous POLLER and --with-poller options now only affects the + internal I/O thread. In case API_POLLER is not specified, the behaviour keeps + backward compatibility intact and will be the same as with previous releases. + +* The non-default "poll" poller for the internal I/O thread (note: NOT for the + zmq_poll/zmq_poller user APIs!) has been disabled on Windows as WSAPoll does + not report connection failures. For more information see: + - https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/ + - https://curl.haxx.se/mail/lib-2012-10/0038.html + - https://bugs.python.org/issue16507 + +* New epoll implementation for Windows, using the following implementation: + https://github.com/piscisaureus/wepoll/tree/v1.5.4 + To use this, select "epoll" as the poller option in the build system. + Note for distributors: the wepoll source code is embedded and distributed. + It is licensed under the BSD-2-Clause and thus it is compatible with LGPL-3.0. + Note that, if selected at build time, the license text must be distributed + with the binary in accordance to the license terms. A copy can be found at: + external/wepoll/license.txt + +* The pre-made Visual Studio solutions file are deprecated, and users are + encouraged to use the CMake solution generation feature instead. + +* New DRAFT (see NEWS for 4.2.0) socket options: + - ZMQ_ROUTER_NOTIFY to deliver a notification when a peer connects and/or + disconnects in the form of a routing id plus a zero-length frame. + - ZMQ_MULTICAST_LOOP to control whether the data sent should be looped back + on local listening sockets for UDP multicast sockets (ZMQ_RADIO). + See doc/zmq_setsockopt.txt and doc/zmq_getsockopt.txt for details. + +* New perf tool, perf/benchmark_radix_tree, to measure the performance of the + different internal implementations of the trie algorithm used to track + subscriptions. Requires a compiler that supports C++11. + +* New autoconf flag "--enable-force-CXX98-compat" which will force -std=gnu++98 + and, if the compiler supports them (clang++ at the moment), it will also add + -Wc++98-compat -Wc++98-compat-pedantic so that compatibility with C++98 can + be tested. + +* Many, many coding style, duplication and static analysis improvements. + +* Many, many improvements to the CMake build system, especially on Windows. + +* Many, many improvements to unit tests. + +* Fixed #3036 - Compilation error when -pedantic is used. + +* Fixed #3028 - Failure when zmq_poller_destroy is called after zmq_ctx_term. + +* Fixed #2989 - CMake: Linker PDB install rule does not work when Visual Studio + generators are used. + +* Fixed #3045 - configure.ac: search for dladdr only when using libunwind. + +* Fixed #3060 - REQ sockets terminate TCP connection after first heartbeat if + ZMQ_HEARTBEAT_IVL is set. + +* Fixed #2212 - UDP: need ability to specify bind address separately from + multicast address for multi-homed hosts. + +* Fixed #2891 - UDP: address name resolution is limited to dotted IPv4 rather + than being capable of IPv4, IPv6, and hostname lookup. + +* Fixed #3085 - autoconf/CMake getrandom test does not check if it's working but + only for its presence. + +* Fixed #3090 - compilation broken with Solaris Studio. + +* Fixed #3094 - UDP: pass interface via IP[V6]_MULTICAST_IF if provided. + +* Fixed #3061 - implement ZMTP 3.1 ping/pong context sending/receiving. + +* Fixed #2188 - Added documentation for new zmq_poller API. + +* Fixed #3088 - zmq_poller_add/zmq_poller_modify should reject invalid events + arguments. + +* Fixed #3042 - Fixed compilation on ARM with ZMQ_ATOMIC_PTR_MUTEX. + +* Fixed #3107 - test_immediate_3/test_reconnect_inproc do not terminate with + POLL as the I/O thread poller under Windows. + +* Fixed #3046 - Aborts when iOS abuses EBADF to report a socket has been + reclaimed. + +* Fixed #3136 - Cannot set ZMQ_HEARTBEAT_TTL to more than 655.3 seconds. + +* Fixed #3083 - link with -latomic when needed. + +* Fixed #3162 - build failure with MUSL libc. + +* Fixed #3158 - -1 value of ZMQ_RECONNECT_IVL was not correctly handled on some + platforms. + +* Fixed #3170 - improved documentation for ZMQ_PAIR. + +* Fixed #3168 - correctly use symbols map on Debian/kFreeBSD and Debian/HURD + to avoid exporting standard library symbols. + +* Fixed #3168 - correctly process ZMTP 3.1 cancel/subscribe commands. + +* Fixed #3171 - improve documentation for ZMQ_CONFLATE. + +* Fixed #2876 - stack overflow on Windows 64. + +* Fixed #3191 - race condition with received message causes + ZMQ_CONNECT_ROUTING_ID to be assigned to wrong socket. + +* Fixed #3005 - added documentation for new zmq_timers_* API. + +* Fixed #3222 - use /Z7 debug on Release builds too on Windows (CMake). + +* Fixed #3226 - possible PGM receiver crash. + +* Fixed #3236 - UDP dish socket can't bind to a multicast port already in use. + +* Fixed #3242 - improve HWM documentation. + +* Fixed #2488 - improve zmq_msg_send doc return value documentation. + +* Fixed #3268 - HWM in ZMQ_DGRAM socket does not respect multipart message. + +* Fixed #3284 - added support for ZMQ_MULTICAST_HOPS with UDP sockets. + +* Fixed #3245 - use-after-free reported in zmq::pipe_t::terminate. + +* Fixed #1400 - use patricia trie for subscription to improve performances and + memory usage. Note: only active in DRAFT builds for now. + +* Fixed #3263 - fix abort on Windows when a large TCP read is requested and + fails. + +* Fixed #3312 - fix build on Android Things 1.06 with Termux. + + +0MQ version 4.2.5 stable, released on 2018/03/23 +================================================ + +* Fixed #3018 - fix backward-incompatible change in the NULL auth + mechanism that slipped in 4.2.3 and made connections + with a ZAP domain set on a socket but without a working + ZAP handler fail. See ZMQ_ZAP_ENFORCE_DOMAIN and RFC27. + +* Fixed #3016 - clarify in zmq_close manpage that the operation will + complete asynchronously. + +* Fixed #3012 - fix CMake build problem when using LIBZMQ_WERROR and a + compiler other than GCC. + + +0MQ version 4.2.4 stable, released on 2018/03/21 +================================================ + +* New DRAFT (see NEWS for 4.2.0) socket options: + - ZMQ_LOOPBACK_FASTPATH to enable faster TCP loopback on Windows + - ZMQ_METADATA to set application-specific metadata on a socket + See doc/zmq_setsockopt.txt and doc/zmq_getsockopt.txt for details. + +* New DRAFT (see NEWS for 4.2.0) context options: + - ZMQ_ZERO_COPY_RECV to disable zero-copy receive to save memory + at the expense of slower performance + See doc/zmq_ctx_set.txt and doc/zmq_ctx_get.txt for details. + +* New DRAFT API zmq_stopwatch_intermediate which returns the time + elapsed without stopping the stopwatch. + +* TIPC: support addressing TIPC Port Identity addresses. + +* Added CMake option to disable tests: BUILD_TESTS + +* Added CMake and autotools make targets to support clang-formatter: + make clang-format, clang-format-check and clang-format-diff to + help developers make sure their code conforms to the style guidelines + +* For distributors: a new test framework has been added, which + includes a copy of the Unity source code. This source code library is + distributed under the MIT license and thus is compatible with + libzmq's LGPL3. + +* Fixed #2867 - add ZeroMQConfig.cmake.in to distributable tarball + +* Fixed #2868 - fix OpenBSD build + +* Fixed #2870 - fix VC++ 11.0 (VS2012) build + +* Fixed #2879 - prevent duplicate connections on PUB sockets + +* Fixed #2872 - fix CMake tests on Windows + +* Fixed #2895 - fix assert on Windows with POLL + +* Fixed #2920 - fix Windows build with Intel compiler + +* Fixed #2930 - use std::atomic when available with VC++ and VS2015 + +* Fixed #2910 - fix race condition with ZMQ_LINGER socket option + +* Fixed #2927 - add support for ZMQ_XPUB_NODROP on ZMQ_RADIO + +* Fixed #2820 - further clarify ZMQ_XPUB_VERBOSE(R) documentation. + +* Fixed #2911 - ZMQ_DISH over UDP triggers errno_assert() after hitting + watermark + +* Fixed #2942 - ZMQ_PUB crash when due to high volume of subscribe and + unsubscribe messages, an unmatched unsubscribe message is + received in certain conditions + +* Fixed #2946 - fix Windows CMake build when BUILD_SHARED is off + +* Fixed #2960 - fix build with GCC 8 + +* Fixed #2967 - fix race condition on thread safe sockets due to pthread + condvar timeouts on OSX + +* Fixed #2977 - fix TIPC build-time availability check to be more relaxed + +* Fixed #2966 - add support for WindRiver VxWorks 6.x + +* Fixed #2963 - fix some PVS Studio static analysis warnings + +* Fixed #2983 - fix MinGW cross-compilation + +* Fixed #2991 - fix mutex assert at shutdown when the zmq context is part + of a class declared as a global static + + +0MQ version 4.2.3 stable, released on 2017/12/13 +================================================ + +* API change: previously ZMQ_POLLOUT on a ZMQ_ROUTER socket returned always + true due to how the type works. When ZMQ_ROUTER_MANDATORY is set, sending + fails when the peer is not available, but ZMQ_POLLOUT always returns true + anyway, which does not make sense. Now when ZMQ_ROUTER_MANDATORY is set, + ZMQ_POLLOUT on a ZMQ_ROUTER will return true only if at least one peer is + available. + Given ZMQ_POLLOUT with ZMQ_ROUTER was not usable at all previously, we do + not consider this a breakage warranting a major or minor version increase. + +* ZMQ_IDENTITY has been renamed to ZMQ_ROUTING_ID and ZMQ_CONNECT_RID has been + renamed to ZMQ_CONNTECT_ROUTING_ID to disambiguate. ZMQ_IDENTITY and + ZMQ_CONNECT_RID are still available to keep backward compatibility, and will + be removed in a future release after further advance notice. + +* DRAFT API change: zmq_poller_wait, zmq_poller_wait_all and zmq_poller_poll + have been changed to be inline with other existing APIs that have a timeout + to return EAGAIN instead of ETIMEDOUT as the errno value. + See #2713 for details. + +* Existing non-DRAFT socket types ZMQ_REP/REQ, ZMQ_ROUTER/DEALER and + ZMQPUB/SUB, that were previously declared deprecated, have been reinstated + as stable and supported. See #2699 for details. + +* Tweetnacl: add support for, and use preferably if available, getrandom() as + a simpler and less error-prone alternative to /dev/urandom on OSes where it + is available (eg: Linux 3.18 with glibc 2.25). + +* Curve: all remaining traces of debug output to console are now removed, and + new DRAFT events are available to properly debug CURVE, PLAIN, GSSAPI and + ZAP events and failures. See below for details on the new events. + +* New DRAFT (see NEWS for 4.2.0) socket options: + - ZMQ_GSSAPI_PRINCIPAL_NAMETYPE and ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE, for + the corresponding GSSAPI features. Additional definitions for principal + name types: + - ZMQ_GSSAPI_NT_HOSTBASED + - ZMQ_GSSAPI_NT_USER_NAME + - ZMQ_GSSAPI_NT_KRB5_PRINCIPAL + See doc/zmq_gssapi.txt for details. + - ZMQ_BINDTODEVICE (Linux only), which will bind the socket(s) to the + specified interface. Allows to use Linux VRF, see: + https://www.kernel.org/doc/Documentation/networking/vrf.txt + NOTE: requires the program to be ran as root OR with CAP_NET_RAW + - ZMQ_ZAP_ENFORCE_DOMAIN, enables strict RFC 27 compatibility mode and makes + the ZAP Domain mandatory when using security. See: + https://rfc.zeromq.org/spec:27/ZAP + See doc/zmq_setsockopt.txt and doc/zmq_getsockopt.txt for details. + +* New DRAFT (see NEWS for 4.2.0) context options: + - ZMQ_THREAD_AFFINITY_CPU_ADD and ZMQ_THREAD_AFFINITY_CPU_REMOVE (Posix only), + to add and remove CPUs to the affinity set of the I/O threads. Useful to pin + the background threads to specific CPUs. + - ZMQ_THREAD_NAME_PREFIX (Posix only), to add a specific integer prefix to the + background threads names, to easily identify them for debugging purposes. + See doc/zmq_ctx_set.txt and doc/zmq_ctx_get.txt for details. + +* New DRAFT (see NEWS for 4.2.0) message property name definitions to facilitate + the use of zmq_msg_gets: + - ZMQ_MSG_PROPERTY_ROUTING_ID + - ZMQ_MSG_PROPERTY_SOCKET_TYPE + - ZMQ_MSG_PROPERTY_USER_ID + - ZMQ_MSG_PROPERTY_PEER_ADDRESS + See doc/zmq_msg_gets.txt for details. + +* New DRAFT (see NEWS for 4.2.0) API zmq_socket_get_peer_state, to be used to + query the state of a specific peer (via routing-id) of a ZMQ_ROUTER socket. + +* New DRAFT (see NEWS for 4.2.0) Socket Monitor events: + - ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL, unknown system error and returns errno + - ZMQ_EVENT_HANDSHAKE_SUCCEEDED, handshake was successful + - ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL, protocol errors between peers or server + and ZAP handler. Returns one of ZMQ_PROTOCOL_ERROR_* - see manpage for list + - ZMQ_EVENT_HANDSHAKE_FAILED_AUTH, failed authentication, returns ZAP status + These events trigger when the ZMTP security mechanism handshake is + completed or failed. See doc/zmq_socket_monitor.txt for more information. + +* New DRAFT (see NEWS for 4.2.0) zmq_proxy_steerable command 'STATISTICS' to + retrieve stats about the amount of messages and bytes sent and received by + the proxy. See doc/zmq_proxy_steerable.txt for more information. + +* Add new autoconf --disable-libunwind option to stop building with libunwind + even if it is available. + +* Add new autoconf --disable-Werror option to avoid building with the Werror + flag. + +* Use pkg-config as the first method for finding and building with external + optional dependencies such as libnorm, libpgm and gssapi. + +* On Posix platform where the feature is available, name the ZMQ background + threads to simplify debugging: "ZMQbg/" + +* Improve performance of zmq_poller_* (and zmq_poll and zmq_proxy when building + with DRAFT APIs enabled). + +* The TCP unit tests have been refactored to bind and connect to random ports + rather than hard-coded ones, to allow running tests in parallel. + There are 6 exceptions where it is necessary to use an hard-coded port to + test specific code paths that would not be exercised when binding to a + wildcard port. These are listed in tests/testutil.hpp so that distributions + can easily patch them if they wish to and so that they can be unique across + all the tests, allowing parallel runs. + The IPC unit tests have been changed as well to use unique socket file names + per test, where before there were some clashes. + +* Fixed #2349 - fix building with libsodium when using CMake + +* Fixed #2334 - do not assert when tuning socket options fails due to network + errors, but simply retry again when connecting or send a socket monitor + ZMQ_EVENT_ACCEPT_FAILED event when binding + +* Fixed #2341 - fix source files path in VS2015 solution + +* Fixed #2344 - Note that on Windows with VS2012 it is mandatory to increase + the default stack size to at least 2MB + +* Fixed #2348 - ZMQ_ROUTER send with ZMQ_ROUTER_MANDATORY can be stuck in case of + network problem + +* Fixed #2358 - occasional abort on zmq_connect on Windows + +* Fixed #2370 - zmq_curve_keypair should return an error on failure rather + than ignoring them and always returning 0 + +* Fixed #2452 - __STDC_LIMIT_MACROS before precompiled headers causes VC++ + warning + +* Fixed #2457 - fix building with libsodium in Visual Studio solutions + +* Fixed #2466 - add const qualifier to internal and public API that does not + modify parameters + +* Fixed #2471 - do more checks for OOM conditions when dynamic allocations is + used + +* Fixed #2476 - assertion causes abort after ZAP stop at shutdown + +* Fixed #2479 - improve zmq_poller performance on Windows + +* Fixed #2481 - potential memory leaks due to ZMTP handshake failures + +* Fixed #2531 - ZMQ_GSSAPI_PRINCIPAL sockopt has no effect on client side + +* Fixed #2535 - add BUILD_SHARED and BUILD_STATIC options to CMake, both on by + default, to toggle shared and static library builds + +* Fixed #2537 - use SYSTEM_CLOCK on OSX and CLOCK_MONOTONIC elsewhere for + internal timers to avoid races + +* Fixed #2540 - new zmq_poller used by zmq_poll without DRAFTs + +* Fixed #2552 - Fix WITH_DOC CMake build to avoid checking for asciidoc if the + option is disabled + +* Fixed #2567 - Memory leak in REP socket handling + +* Fixed #2579 - Compilation issue on Windows with CMake + ninja + +* Fixed #2588 - SIGBUS under 64-bit SunOS Sparc + +* Fixed #2590 - crash when using ZMQ_IMMEDIATE and ZMQ_LINGER to non-zero + +* Fixed #2601 - XPUB_MANUAL subscriptions not removed on peer term + +* Fixed #2602 - intermittent memory leak for ZMQ_REQ/REP send/recv + +* Fixed #2608 - CURVE server (connect) fails when client rebinds + +* Fixed #2610 - print backtraces in mutual exclusion to avoid mixing + different traces + +* Fixed #2621 - add missing CMake files to distributable tarball + +* Fixed #2630 - improve compatibility with OpenBSD w.r.t. IPV6_V6ONLY + +* Fixed #2638 - note in INSTALL that when using Windows builds on Linux with + Wine it is necessary to increase the minimum TCP buffers + +* Fixed #2632 - Fix file descriptor leak when using Tweetnacl (internal NACL + implementation) instead of Libsodium, and fix race condition when using + multiple ZMQ contexts with Tweetnacl + +* Fixed #2681 - Possible buffer overflow in CURVE mechanism handshake. + NOTE: this was protected by an assert previously, so there is no security + risk. + +* Fixed #2704 - test_sockopt_hwm fails occasionally on Windows + +* Fixed #2701 - pgm build via cmake doesn't link libzmq with libpgm + +* Fixed #2711 - ZAP handler communication errors should be handled consistently + +* Fixed #2723 - assertion in src\select.cpp:111 or hang on zmq_ctx_destroy on + Windows + +* Fixed #2728 - fix support O_CLOEXEC when building with CMake + +* Fixed #2761 - improve compatibility with TrueOS (FreeBSD 12) + +* Fixed #2764 - do not unlink IPC socket files when closing a socket to avoid + race conditions + +* Fixed #2770 - support lcov 1.13 and newer + +* Fixed #2787 - add libiphlpapi to PKGCFG_LIBS_PRIVATE for static mingw builds + +* Fixed #2788 - document that adding -DZMQ_STATIC is required for Windows + static builds with Mingw + +* Fixed #2789 - description of zmq_atomic_counter_value return value is cloned + from zmq_atomic_counter_new + +* Fixed #2791 - fix building with DRAFT APIs on CentOS 6 + +* Fixed #2794 - router_t methods should not allocate memory for lookup in + outpipes + +* Fixed #2809 - optimize select() usage on Windows + +* Fixed #2816 - add CMake and autoconf check for accept4, as it is not + available on old Linux releases, and fallback to accept + FD_CLOEXEC + +* Fixed #2824 - ZMQ_REQ socket does not report ZMQ_POLLOUT when ZMQ_REQ_RELAXED + is set + +* Fixed #2827 - add support for Haiku + +* Fixed #2840 - fix building with VS2008 + +* Fixed #2845 - correct the ZMQ_LINGER documentation to accurately reflect that + the default value is -1 (infinite). It never was 30 second in any released + version, it was only changed briefly and then changed back, but the manpage + was not reverted. + +* Fixed #2861 - CMake/MSVC: export ZMQ_STATIC when needed. + +0MQ version 4.2.2 stable, released on 2017/02/18 +============================================= + +* Improve compatibility with GNU Hurd + +* Fixed #2286 - improve CMake on Windows documentation + +* Fixed #1235 - improved compatibility with mingw64 + +* Improve zmq_proxy documentation to state it can return ETERM as well + +* Fixed #1442 - SO_NOSIGPIPE and connection closing by peer race condition + +* Improve CMake functionality on Windows: ZeroMQConfig.cmake generation CPack + option, correct static library filename, ship FindSodium.cmake in tarball + +* Fixed #2228 - setting HWM after connect on inproc transport leads to infinite + HWM + +* Add support for Visual Studio 2017 + +* New DRAFT (see NEWS for 4.2.0) zmq_has option "draft" option that returns + true if the library was built with DRAFT enabled. Useful for FFI bindings. + See doc/zmq_has.txt for more information + +* Fixed #2321 - zmq_z85_decode does not validate its input. The function has + been fixed to correctly follow RFC32 and return NULL if the input is invalid + +* Fixed #2323 - clock_t related crash on Apple iOS 9.3.2 and 9.3.5 + +* Fixed #1801 - OSX: Cmake installs libzmq in a weird PATH + +* Fixed potential divide by zero in zmq::lb_t::sendpipe + +* Improve compatibility with OpenIndiana by skipping epoll and using poll/select + +* Fix IPv4-in-IPv6 mapped addresses parsing error + + +0MQ version 4.2.1 stable, released on 2016/12/31 +============================================= + +* New DRAFT (see NEWS for 4.2.0) Socket Monitor events: + - ZMQ_EVENT_HANDSHAKE_SUCCEED + - ZMQ_EVENT_HANDSHAKE_FAILED + These events trigger when the ZMTP security mechanism handshake is + completed. See doc/zmq_socket_monitor.txt for more information. + +* New DRAFT (see NEWS for 4.2.0) Context options: + - ZMQ_MSG_T_SIZE + See doc/zmq_ctx_get.txt for more information. + +* Fixed #2268 - improved compatibility with mingw32 + +* Fixed #2254 - ZMQ_PUB compatibility with libzmq 2.x broken + +* Fixed #2245 - added support for VS2017, Windows SDK 10.0.14393.0, toolset v141 + +* Fixed #2242 - file descriptors leaks on fork+exec + +* Fixed #2239 - retired poller item crash from reaper thread + +* Fixed #2234 - improved compatibility with AIX 7.1 + +* Fixed #2225 - cannot pick select for poller + +* Fixed #2217 - CMake build uses library version as the ABI version + +* Fixed #2208 - added support for ZMQ_TOS on IPv6 + +* Fixed #2200 - no documentation for ZMQ_SOCKS_PROXY + +* Fixed #2199 - no documentation for zmq_curve_public + +* Fixed #2196 - fixed build and runtime errors on kFreeBSD + + +0MQ version 4.2.0 stable, released on 2016/11/04 +============================================= + +* For Pieter. Thanks for making all of this possible. + + "Tell them I was a writer. + A maker of software. + A humanist. A father. + And many things. + But above all, a writer. + Thank You. :)" + - Pieter Hintjens + +* This release introduces new APIs, but it is ABI compatible with + libzmq 4.1.2 and up. + +* Note for ARM and SPARC users: an alignment problem in zmq_msg_t that could in + some cases and on some CPUs cause a SIGBUS error was solved, but it requires + a rebuild of your application against the 4.2.0 version of include/zmq.h. + To clarify, this change does not affect the internals of the library but only + the public definition of zmq_msg_t, so there is no ABI incompatibility. + +* Security with Curve is now available by default thanks to Tweetnacl sources: + https://tweetnacl.cr.yp.to/index.html + Libsodium is still fully supported but has to be enabled with the build flag + --with-libsodium. Distribution and package maintainers are encouraged to use + libsodium so that the security implementation can be audited and maintained + separately. + +* New Context options: + - ZMQ_MAX_MSGSZ + - ZMQ_BLOCKY + See doc/zmq_ctx_set.txt and doc/zmq_ctx_get.txt for details. + +* New Socket options: + - ZMQ_HANDSHAKE_IVL + - ZMQ_SOCKS_PROXY + - ZMQ_XPUB_NODROP + - ZMQ_XPUB_MANUAL + - ZMQ_XPUB_WELCOME_MSG + - ZMQ_STREAM_NOTIFY + - ZMQ_INVERT_MATCHING + - ZMQ_HEARTBEAT_IVL + - ZMQ_HEARTBEAT_TTL + - ZMQ_HEARTBEAT_TIMEOUT + - ZMQ_XPUB_VERBOSER + - ZMQ_CONNECT_TIMEOUT + - ZMQ_TCP_MAXRT + - ZMQ_THREAD_SAFE + - ZMQ_MULTICAST_MAXTPDU + - ZMQ_VMCI_BUFFER_SIZE + - ZMQ_VMCI_BUFFER_MIN_SIZE + - ZMQ_VMCI_BUFFER_MAX_SIZE + - ZMQ_VMCI_CONNECT_TIMEOUT + - ZMQ_USE_FD + See doc/zmq_setsockopt.txt and doc/zmq_getsockopt.txt for details. + +* New CURVE helper function to derive z85 public key from secret key: + zmq_curve_public + +* New cross-platform atomic counter helper functions: + zmq_atomic_counter_new, zmq_atomic_counter_set, zmq_atomic_counter_inc, + zmq_atomic_counter_dec, zmq_atomic_counter_value, zmq_atomic_counter_destroy + See doc/zmq_atomic_*.txt for details. + +* New DRAFT APIs early-release mechanism. New APIs will be introduced early + in public releases, and until they are stabilized and guaranteed not to + change anymore they will be unavailable unless the new build flag + --enable-drafts is used. This will allow developers and early adopters to + test new APIs before they are finalized. + NOTE: as the name implies, NO GUARANTEE is made on the stability of these APIs. + They might change or disappear entirely. Distributions are recommended NOT to + build with them. + + New socket types have been introduced in DRAFT state: + ZMQ_SERVER, ZMQ_CLIENT, ZMQ_RADIO, ZMQ_DISH, ZMQ_GATHER, ZMQ_SCATTER, + ZMQ_DGRAM + All these sockets are THREAD SAFE, unlike the existing socket types. They do + NOT support multipart messages (ZMQ_SNDMORE/ZMQ_RCVMORE). + ZMQ_RADIO, ZMQ_DISH and ZMQ_DGRAM also support UDP as transport, + both unicast and multicast. See doc/zmq_udp.txt for more details. + New methods to support the new socket types functionality: + zmq_join, zmq_leave, zmq_msg_set_routing_id, zmq_msg_routing_id, + zmq_msg_set_group, zmq_msg_group + See doc/zmq_socket.txt for more details. + + New poller mechanism and APIs have been introduced in DRAFT state: + zmq_poller_new, zmq_poller_destroy, zmq_poller_add, zmq_poller_modify, + zmq_poller_remove, zmq_poller_wait, zmq_poller_wait_all, zmq_poller_add_fd + zmq_poller_modify_fd, zmq_poller_remove_fd + and a new supporting struct typedef: zmq_poller_event_t + They support existing socket type, new thread-safe socket types and file + descriptors (cross-platform). + Documentation will be made available in the future before these APIs are declared + stable. + + New cross-platform timers helper functions have been introduced in DRAFT state: + zmq_timers_new, zmq_timers_destroy, zmq_timers_add, zmq_timers_cancel, + zmq_timers_set_interval, zmq_timers_reset, zmq_timers_timeout, + zmq_timers_execute + and a new supporting callback typedef: zmq_timer_fn + +* Many, many bug fixes. The most important fixes are backported and captured in the + 4.1.x and 4.0.x changelogs. + + +0MQ version 4.2.0 rc1, released on 2016/11/01 +============================================= + +* Many changes, see ChangeLog. + + +0MQ version 4.1.6 stable, released on 2016/11/01 +================================================ + +* Fixed #2051 - getifaddrs can fail with ECONNREFUSED + +* Fixed #2091 - testutil.hpp fails to build on Windows XP + +* Fixed #2096 - add tests/CMakeLists.in and version.rc.in to dist tar + +* Fixed #2107 - zmq_connect with IPv6 "source:port;dest:port" broken + +* Fixed #2117 - ctx_term assert with inproc zmq_router connect-before-bind + +* Fixed #2158 - Socket monitor uses internal Pair from multiple threads + +* Fixed #2161 - messages dropped due to HWM race + +* Fixed #1325 - alignment issue with zmq_msg_t causes SIGBUS on SPARC and ARM + + +0MQ version 4.1.5 stable, released on 2016/06/17 +================================================ + +* Fixed #1673 - CMake on Windows put PDB in wrong directory. + +* Fixed #1723 - Family is not set when resolving NIC on Android. + +* Fixed #1608 - Windows 7 TCP slow start issue. + +* Fixed #1806 - uninitialised read in curve getsockopt. + +* Fixed #1807 - build broken with GCC 6. + +* Fixed #1831 - potential assertion failure with latest libsodium. + +* Fixed #1850 - detection issues with tweetnacl/libsodium. + +* Fixed #1877 - Avoid terminating connections prematurely + +* Fixed #1887 - zmq_bind IPv4 fallback still tries IPv6 + +* Fixed #1866 - fails to build on SunOS 5.10 / Solaris 10 + +* Fixed #919 - ZMQ_LINGER (related to #1877) + +* Fixed #114 - cannot unbind with same endpoint with IPv6 enabled. + +* Fixed #1952 - CMake scripts not part of release tarballs + +* Fixed #1542 - Fix a crash on Windows when port 5905 is in use. + +* Fixed #2021 - Fix building on sparc32. + + +0MQ version 4.1.4 stable, released on 2015/12/18 +================================================ + +* Fixed #1315 - socket monitor hangs if bind/setsockopt failed. + +* Fixed #1399 - assertion failure in tcp.cpp after network reconnect. + +* Fixed #1632 - build failure using latest libsodium. + +* Fixed #1644 - assertion failure in msg.cpp:390 on STREAM sockets. + +* Fixed #1661 - does not handle IPv6 link local addresses. + + +0MQ version 4.1.3 stable, released on 2015/08/17 +================================================ + +* Fixed #1532 - getsockopt ZMQ_RCVMORE now resets all bits instead of only 32 + +* Fixed #1445 - zmq::socket_base_t::connect fails on tcp ipv6 address + + +0MQ version 4.1.2 stable, released on 2015/06/15 +================================================ + +* Added explicit reference to static link exception in every source file. + +* Bumped ABI version to 5:0:0 since 4.1.x changed the ABI. + +* Fixed STDINT event interface macros to work with CZMQ 3.0. + +* Fixed installation of man pages when BUILD_DOC is not set. + +* Fixed #1428 - regression on single-socket proxies. + + +0MQ version 4.1.1 stable, released on 2015/06/02 +================================================ + +* Fixed #1208 - fix recursion in automake packaging. + +* Fixed #1224 - crash when processing empty unsubscribe message. + +* Fixed #1213 - properties files were missing from source packages. + +* Fixed #1273 - V3 protocol handler vulnerable to downgrade attacks. + +* Fixed #1347 - lack way to get peer address. + +* Fixed #1362 - SUB socket sometimes fails to resubscribe properly. + +* Fixed #1377, #1144 - failed with WSANOTINITIALISED in some cases. + +* Fixed #1389 - PUB, PUSH sockets had slow memory leak. + +* Fixed #1382 - zmq_proxy did not terminate if there were no readers. + + +0MQ version 4.1.0 rc1, released on 2014/10/14 +============================================= + +* All issues that were fixed in 4.0.x + +* Improved client reconnection strategy on errors + +* GSSAPI security mechanism + +* SOCKS5 support (ZMQ_SOCKS_PROXY) + +* ZMQ_ROUTER_HANDOVER + +* ZMQ_TOS + +* ZMQ_CONNECT_RID + +* ZMQ_HANDSHAKE_IVL + +* ZMQ_IDENTITY_FD + +* ZMQ_XPUB_NODROP + +* ZMQ_SRCFD and ZMQ_SHARED message options + +* Message metadata -- zmq_msg_gets () + +* Probe library configuration -- zmq_has () + + +0MQ version 4.0.8 stable, released on 2016/06/17 +================================================ + +* Fixed LIBZMQ-949 - zmq_unbind fails for inproc and wildcard endpoints + +* Fixed #1806 - uninitialised read in curve getsockopt. + +* Fixed #1807 - build broken with GCC 6. + +* Fixed #1877 - Avoid terminating connections prematurely + +* Fixed #1887 - zmq_bind IPv4 fallback still tries IPv6 + +* Fixed #98 - don't require libssp without libsodium on Solaris + +* Fixed #919 - ZMQ_LINGER (related to #1877) + +* Fixed #139 - "tempnam" is deprecated. + + +0MQ version 4.0.7 stable, released on 2015/06/15 +================================================ + +* Fixed #1428 - regression on single-socket proxies. + + +0MQ version 4.0.6 stable, released on 2015/06/02 +================================================ + +* Fixed #1273 - V3 protocol handler vulnerable to downgrade attacks. + +* Fixed #1362 - SUB socket sometimes fails to resubscribe properly. + +* Fixed #1377, #1144 - failed with WSANOTINITIALISED in some cases. + +* Fixed #1389 - PUB, PUSH sockets had slow memory leak. + +* Fixed #1382 - zmq_proxy did not terminate if there were no readers. + + +0MQ version 4.0.5 stable, released on 2014/10/14 +================================================ + +* Fixed #1191; CURVE mechanism does not verify short term nonces. + +* Fixed #1190; stream_engine is vulnerable to downgrade attacks. + +* Fixed #1088; assertion failure for WSAENOTSOCK on Windows. + +* Fixed #1015; race condition while connecting inproc sockets. + +* Fixed #994; bump so library number to 4.0.0 + +* Fixed #939, assertion failed: !more (fq.cpp:99) after many ZAP requests. + +* Fixed #872; lost first part of message over inproc://. + +* Fixed #797, keep-alive on Windows. + + +0MQ version 4.0.4 stable, released on 2014/03/10 +================================================ + +Bug Fixes +--------- + +* Fixed #909; out of tree build issue on Linux. + +* Fixed #888; hangs on terminate when inproc connected but never bound. + +* Fixed #868; assertion failure at ip.cpp:137 when using port scanner. + +* Fixed #818; fix timestamp counter on s390/s390x. + +* Fixed #817; only export zmq_* symbols. + +* Fixed #797; fixed setting TCP keepalive on Windows. + +* Fixed #775; compile error on Windows. + +* Fixed #763; when talking to a ZMTP v1 peer (libzmq 2.2), a socket would + send an extra identity frame at the start of the connection. + +* Fixed LIBZMQ-576 - Crash closing a socket after zmq_msg_send returns + EAGAIN (reverts LIBZMQ-497) + +* Fixed LIBZMQ-584; subscription filters getting lost on reconnection. + + +0MQ version 4.0.3 stable, released on 2013/11/24 +================================================ + +Bug Fixes +--------- + +* Fixed test_many_sockets case, which failed when process socket limit + was 1024. + + +0MQ version 4.0.2 stable, released on 2013/11/24 +================================================ + +Bug Fixes +--------- + +* Fixed LIBZMQ-583 - improved low-res timer for Windows +* Fixed LIBZMQ-578 - z85_decode was extremely slow +* Fixed LIBZMQ-577 - fault in man pages. +* Fixed LIBZMQ-574 - assertion failure when ran out of system file handles +* Fixed LIBZMQ-571 - test_stream failing in some cases +* Fixed LIBZMQ-569 - Socket server crashes with random client data and when + talking to 2.2 versions +* Fixed LIBZMQ-39 - Bad file descriptor during shutdown +* Pulled expected failing test_linger.cpp from release +* Reduced pause time in tests to allow "make check" to run faster + + +0MQ version 4.0.1 stable, released on 2013/10/08 +================================================ + +Changes +------- + +* Updated CURVE mechanism to track revised RFC 27 (INITIATE vouch). + + The INITIATE command vouch box is Box[C',S](C->S') instead of + Box[C'](C->S), to reduce the risk of client impersonation, as per + https://codesinchaos.wordpress.com/2012/09/09/curvecp-1/. + +* Fixed LIBZMQ-567, adding abstract namespaces for IPC sockets on Linux. + + Converts an initial strudel or "at sign" (@) in the Unix socket path to + a NULL character ('\0') indicating that the socket uses the abstract + namespace instead of the filesystem namespace. For instance, binding a + socket to 'ipc://@/tmp/tester' will not create a file associated with + the socket whereas binding to 'ipc:///tmp/tester' will create the file + /tmp/tester. See issue 567 for more information. + +* Added zmq_z85_encode and zmq_z85_decode to core libzmq API. + +* Added zmq_curve_keypair to core libzmq API. + +* Bumped library ABI version to 4:0:1. + +Bug fixes +--------- + +* Fixed some build/test errors on OS/X + Clang++. + +* Fixed LIBZMQ-565, typo in code. + +* Fixed LIBZMQ-566, dealer-to-router connections sometimes failing. + +* Fixed builds for AIX, MSVC 2008, OS/X with clang++, Solaris. + +* Improved CURVE handshake error handling. + + +0MQ version 4.0.0 (RC1), released on 2013/09/20 +=============================================== + +Major changes +------------- + +* New wire level protocol, ZMTP/3.0, see http://rfc.zeromq.org/spec:23. + Does not yet implement the SUBSCRIBE, CANCEL, PING, and PONG commands. + +* New security framework, from plain user+password to strong encryption, + see section below. See http://hintjens.com/blog:49 for a tutorial. + +* New ZMQ_STREAM socket type for working as a TCP client or server. See: + tests/test_stream.cpp. + +Improvements +------------ + +* You can now connect to an inproc:// endpoint that does not already + exist. This means inproc:// no longer needs careful set-up, but it may + break code that relied on the old behaviour. See: + tests/test_inproc_connect.cpp. + +* Libzmq now checks socket types at connection time, so that trying to + connect a 'wrong' socket type will fail. + +* New zmq_ctx_shutdown API method will shutdown a context and send ETERM + to blocking calls, without blocking. Use zmq_ctx_term to finalise the + process. + +* The regression test suite has been significantly extended and improved. + +* Contexts can now be terminated in forked child processes. See: + tests/test_fork.cpp. + +* zmq_disconnect now respects the linger setting on sockets. + +* New zmq_send_const API method to send constant data (without copying). + See: tests/test_inproc_connect.cpp. + +* Added CMake support for static libraries. + +* Added test cases for socket semantics as defined in RFCs 28, 29, 30, 31. + See: tests/test_spec_*.cpp. + +* New socket option, ZMQ_PROBE_ROUTER triggers an empty message on connect. + See: tests/test_probe_router.cpp. + +* New socket option, ZMQ_REQ_CORRELATE allows for correlation of replies + from a REP socket. See: tests/test_req_correlate.cpp. + +* New socket option, ZMQ_REQ_RELAXED, lets you disable the state machine + on a REQ socket, so you can send multiple requests without waiting for + replies, and without getting an EFSM error. See: + tests/test_req_relaxed.cpp. + +* New socket option, ZMQ_CONFLATE restricts the outgoing and incoming + socket buffers to a single message. See: tests/test_conflate.cpp. + +Deprecated Options +------------------ + +* ZMQ_IPV4ONLY deprecated and renamed to ZMQ_IPV6 so that options are + consistently "off" by default. + +* ZMQ_DELAY_ATTACH_ON_CONNECT deprecated, and renamed to ZMQ_IMMEDIATE. + See: tests/test_immediate.cpp. + +Security Framework +------------------ + +Based on new ZMTP wire level protocol that negotiates a security +"mechanism" between client and server before exchanging any other data. + +Security mechanisms are extensible. ZMTP defines three by default: + +* NULL - classic ZeroMQ, with no authentication. See + http://rfc.zeromq.org/spec:23. + +* PLAIN - plain-text username + password authentication. See + http://rfc.zeromq.org/spec:24. + +* CURVE - secure authentication and encryption based on elliptic curve + cryptography, using the Curve25519 algorithm from Daniel Bernstein and + based on CurveCP's security handshake. See http://rfc.zeromq.org/spec:25, + http://rfc.zeromq.org/spec:26, and http://curvecp.org. + +Authentication is done by pluggable "authenticators" that connect to libzmq +over an inproc endpoint, see http://rfc.zeromq.org/spec:27. + +Socket options to configure PLAIN security on client or server: + +* ZMQ_PLAIN_SERVER, ZMQ_PLAIN_USERNAME, ZMQ_PLAIN_PASSWORD. See + tests/test_security_plain. + +Socket options to configure CURVE security on client or server: + +* ZMQ_CURVE_SERVER, ZMQ_CURVE_PUBLICKEY, ZMQ_CURVE_SECRETKEY, + ZMQ_CURVE_SERVERKEY. See tests/test_security_curve.cpp. + +Socket options to configure "domain" for ZAP handler: + +* ZMQ_ZAP_DOMAIN, see tests/test_security_null.cpp. + +Support for encoding/decoding CURVE binary keys to ASCII: + +* zmq_z85_encode, zmq_z85_decode. + +Other issues addressed in this release +-------------------------------------- + +* LIBZMQ-525 Multipart upstreaming from XSUB to XPUB + + +0MQ version 3.2.4 stable, released on 2013/09/20 +================================================ + +* LIBZMQ-84 (Windows) Assertion failed: Address already in use at signaler.cpp:80 +* LIBZMQ-456 ZMQ_XPUB_VERBOSE does not propagate in a tree of XPUB/XSUB devices +* LIBZMQ-532 (Windows) critical section not released on error +* LIBZMQ-569 Detect OpenPGM 5.2 system library +* LIBZMQ-563 Subscribers sometimes stopped receiving messages (aka LIBZMQ-541) +* LIBZMQ-XXX Added support for Travis Continuous Integration +* LIBZMQ-XXX Several improvements to MSVC support + + +0MQ version 3.2.3 stable, released on 2013/05/02 +================================================ + +Issues addressed in this release +-------------------------------- + +* LIBZMQ-526 Assertion failure "Invalid argument (tcp_connecter.cpp:285)" +* LIBZMQ-446 Setting the DSCP bits by default causes CAP_NET_ADMIN error +* LIBZMQ-496 Crash on heavy socket opening/closing: Device or resource busy (mutex.hpp:90) +* LIBZMQ-462 test_connect_delay fails at test_connect_delay.cpp:80 +* LIBZMQ-497 Messages getting dropped +* LIBZMQ-488 signaler.cpp leaks the win32 Event Handle +* LIBZMQ-476 zmq_disconnect has no effect for inproc sockets +* LIBZMQ-475 zmq_disconnect does not sent unsubscribe messages + + +0MQ version 3.2.2 stable, released on 2012/11/23 +================================================ + +Issues addressed in this release +-------------------------------- + +* LIBZMQ-384 No meta data for ZMQ_EVENT_DISCONNECTED monitor event +* LIBZMQ-414 Error in ARM/Thumb2 assembly (atomic_ptr.hpp) +* LIBZMQ-417 zmq_assert (!incomplete_in) in session_base.cpp 228 +* LIBZMQ-447 socket_base_t::recv() packet loss and memory leak at high receiving rate +* LIBZMQ-448 Builds fail on older versions of GCC +* LIBZMQ-449 Builds fail on AIX +* LIBZMQ-450 lt-test_monitor: fails with assertion at test_monitor.cpp:81 +* LIBZMQ-451 ZMQ_ROUTER_MANDATORY blocks forever +* LIBZMQ-452 test_connect_delay.cpp:175:12: error: 'sleep' was not declared in this scope +* LIBZMQ-458 lt-test_router_mandatory fails with assertion at test_router_mandatory.cpp:53 +* LIBZMQ-459 Assertion failed: encoder (stream_engine.cpp:266 +* LIBZMQ-464 PUB socket with HWM set leaks memory +* LIBZMQ-465 PUB/SUB results in 80-90% of CPU load +* LIBZMQ-468 ZMQ_XPUB_VERBOSE & unsubscribe +* LIBZMQ-472 Segfault in zmq_poll in REQ to ROUTER dialog + + +0MQ version 3.2.1 (RC2), released on 2012/10/15 +=============================================== + +Issues addressed in this release +-------------------------------- + +* Fixed issue xxx - handle insufficient resources on accept() properly. +* Fixed issue 443 - added ZMQ_XPUB_VERBOSE setsocket option. +* Fixed issue 433 - ZeroMQ died on receiving EPIPE +* Fixed issue 423 - test_pair_tcp hangs +* Fixed issue 416 - socket_base: fix 'va_list' has not been declared error +* Fixed issue 409 - Pub-sub interoperability between 2.x and 3.x. +* Fixed issue 404 - zmq_term can not safely be re-entered with pgm transport +* Fixed issue 399 - zmq_ctx_set_monitor callback is not works properly +* Fixed issue 393 - libzmq does not build on Android (socklen_t signed comparison) +* Fixed issue 392 - Interaction with pyzmq on Android +* Fixed issue 389 - Assertion failure in mtrie.cpp:317 +* Fixed issue 388 - tests/test_monitor.cpp has no newline at EOF (causes compile error) +* Fixed issue 387 - "sa_family_t sa_family;" in pgm_socket.cpp is unused variable +* Fixed issue 385 - Rework ZMQ_FAIL_UNROUTABLE socket option to actually work +* Fixed issue 382 - Current libzmq doesn't compile on Android NDK +* Fixed issue 377 - ZeroMQ will not build on Windows with OpenPGM +* Fixed issue 375 - error: unused variable 'sa_family' +* Fixed issue 373 - Unable to build libzmq/zeromq3.x on AIX7 +* Fixed issue 372 - Unable to build libzmq/zeromq3.x on HPUX 11iv3 +* Fixed issue 371 - Unable to build libzmq/zeromq3.x on RHEL5/SLES10 +* Fixed issue 329 - wsa_error_to_errno() calls abort() on WSAEACCES +* Fixed issue 309 - Assertion failed: options.recv_identity (socket_base.cpp:864) +* Fixed issue 211 - Assertion failed: msg_->flags & ZMQ_MSG_MORE (rep.cpp:81) + +API changes +----------- + +* zmq_device () deprecated and replaced by zmq_proxy (). +* zmq_ctx_set_monitor () replaced by zmq_socket_monitor (). +* ZMQ_ROUTER_BEHAVIOR/ZMQ_FAIL_UNROUTABLE renamed experimentally to + ZMQ_ROUTER_MANDATORY. + + +0MQ version 3.2.0 (RC1), released on 2012/06/05 +=============================================== + +Bug fixes +--------- + +* Fixed issue 264 - Potential bug with linger, messages dropped during + socket close. + +* Fixed issue 293 - libzmq doesn't follow the ZMTP/1.0 spec (did not + set reserved bits to 0). + +* Fixed issue 303 - Assertion failure in pgm_sender.cpp:102. + +* Fixed issue 320 - Assertion failure in connect_session.cpp:96 when + connecting epgm to an invalid endpoint. + +* Fixed issue 325 - Assertion failure in xrep.cpp:93, when two sockets + connect using the same identity. + +* Fixed issue 327 - Assertion failure in mtrie.cpp:246, when + unsubscribing from channel. + +* Fixed issue 346 - Assertion failure in signaler.cpp:155, when using a + closed socket. + +* Fixed issue 328 - unsubscribe wrongly clears multiple subscriptions. + +* Fixed issue 330 - IPC listener does not remove unix domain stream file + when terminated. + +* Fixed issue 334 - Memory leak in session_base.cpp:59. + +* Fixed issue 369 - ROUTER cannot close/reopen while DEALER connected. + +Operating systems +----------------- + +* Fixed issue 301 - HPUX 11iv2 - build fails, CLOCK_MONOTONIC + undefined. + +* Fixed issue 324 - OS/X - build fails, ECANTROUTE undefined. + +* Fixed issue 368 - Solaris / Sun C++ - build fails, no insert method + in multimap classes. + +* Fixed issue 366 - Windows - ports not freed after crash. + +* Fixed issue 355 - Windows - build fails, MSVC solution file is out of + date. + +* Fixed issue 331 - FreeBSD 8 and 9 - getaddrinfo fails with + EAI_BADFLAGS on AI_V4MAPPED flag. + +* Fixed issue xxx - Added support for WinCE. + +Performance +----------- + +* Fixed issue xxx - Implemented atomic operations for ARMv7a (runs 15-20% faster). + +API changes +----------- + +* Fixed issue 337 - Cleaned-up context API: + + zmq_ctx_new() - create new context (will deprecate zmq_init) + zmq_ctx_destroy() - destroy context (will deprecate zmq_term) + zmq_ctx_set() - set context property + zmq_ctx_get() - get context property + +* Fixed issue xxx - Cleaned-up message API: + + zmq_msg_send() - send a message (will deprecate zmq_sendmsg) + zmq_msg_recv() - receive a message (will deprecate zmq_recvmsg) + zmq_msg_more() - indicate whether this is final part of message + zmq_msg_get() - get message property + zmq_msg_set() - set message property + +* Fixed issue xxx - Added context monitoring API: + + zmq_ctx_set_monitor() - configure monitor callback. + +* Fixed issue xxx - Added unbind/disconnect API: + + zmq_unbind() - unbind socket. + zmq_disconnect() - disconnect socket. + +* Fixed issue xxx - Added ZMQ_TCP_ACCEPT_FILTER setsockopt() for listening TCP sockets. + +* Fixed issue 336 - Removed sys: transport. + +* Fixed issue 333 - Added zmq_device function back to API (was removed + in 3.0). + +* Fixed issue 340 - Add support for MAX_SOCKETS to new context API. + + +OMQ version 3.1.0 (beta), released on 2011/12/18 +================================================ + +General information +------------------- + +Based on community consensus, the 0MQ 3.1.x release reverts a number of +features introduced in version 3.0. The major reason for these changes is +improving backward compatibility with 0MQ 2.1.x. + +Development of the 0MQ 3.0.x series will be discontinued, and users are +encouraged to upgrade to 3.1. + +The 0MQ 3.1.x releases use ABI version 3. + +Reverted functionality +---------------------- + +The following functionality present in 0MQ 3.0 has been reverted: + +* Wire format changes. The 0MQ 3.1 wire format is identical to that of 0MQ + 2.1. + +* LABELs and COMMANDs have been removed. + +* Explicit identies are re-introduced, however they can be used only for + explicit routing, not for durable sockets. + +* The ZMQ_ROUTER and ZMQ_DEALER socket types are once again aliases for + ZMQ_XREQ and ZMQ_XREP. + +New functionality +----------------- + +* The zmq_getmsgopt() function has been introduced. + +* Experimental IPv6 support has been introduced. This is disabled by + default, see the zmq_setsockopt() documentation for enabling it. + +Other changes +------------- + +* The default HWM for all socket types has been set to 1000. + +* Many bug fixes. + +Building +-------- + +* The dependency on libuuid has been removed. + +* Support for building on Android, and with MSVC 10 has been added. + +0MQ version 3.0.0 (alpha), released on 2011/07/12 +================================================= + +New functionality +----------------- + +* A zmq_ctx_set_monitor() API to register a callback / event sink for changes + in socket state. + +* POSIX-compliant zmq_send and zmq_recv introduced (uses raw buffer + instead of message object). + +* ZMQ_MULTICAST_HOPS socket option added. Sets the appropriate field in + IP headers of PGM packets. + +* Subscription forwarding. Instead of filtering on consumer, the + subscription is moved as far as possible towards the publisher and + filtering is done there. + +* ZMQ_XPUB, ZMQ_XSUB introduced. Allow to create subscription- + forwarding-friendly intermediate devices. + +* Add sockopt ZMQ_RCVTIMEO/ZMQ_SNDTIMEO. Allow to set timeout for + blocking send/recv calls. + +* A new LABEL flag was added to the wire format. The flag distinguishes + message parts used by 0MQ (labels) from user payload message parts. + +* There is a new wire format for the REQ/REP pattern. First, the empty + bottom-of-the-stack message part is not needed any more, the LABEL + flag is used instead. Secondly, peer IDs are 32-bit integers rather + than 17-byte UUIDs. + +* The REQ socket now drops duplicate replies. + +* Outstanding requests & replies associated with a client are dropped + when the clients dies. This is a performance optimisation. + +* Introduced ZMQ_ROUTER and ZMQ_DEALER sockets. These mimic the + functionality of ZMQ_ROUTER and ZMQ_DEALER in 0MQ/2.1.x. Guarantees + backward compatibility for exsiting code. + +* Removed dependency on OS socketpair buffer size. No more asserts in + mailbox.cpp because of low system limit of sockepair buffer size. + +API improvements +---------------- + +* Obsolete constants ZMQ_UPSTREAM and ZMQ_DOWNSTREAM removed. Use + ZMQ_PUSH and ZMQ_PULL instead. + +* Timeout in zmq_poll is in milliseconds instead of microseconds. This + makes zmq_poll() compliant with POSIX poll() + +* ZMQ_MCAST_LOOP removed. There's no support for multicast over + loopback any more. Use IPC or TCP isntead. + +* zmq_send/zmq_recv was renamed zmq_sendmsg/zmq_recvmsg. + +* ZMQ_RECOVERY_IVL and ZMQ_RECOVERY_IVL_MSEC reconciled. The new option + is named ZMQ_RECOVERY_IVL and the unit is milliseconds. + +* Option types changed. Most of the numeric types are now represented + as 'int'. + +* ZMQ_HWM split into ZMQ_SNDHWM and ZMQ_RCVHWM. This makes it possible + to control message flow separately for each direction. + +* ZMQ_NOBLOCK renamed ZMQ_DONTWAIT. That makes it POSIX-compliant. + +Less is More +------------ + +* Pre-built devices and zmq_device() removed. Should be made available + as a separate project(s). + +* ZMQ_SWAP removed. Writing data to disk should be done on top of 0MQ, + on inside it. + +* C++ binding removed from the core. Now it's a separate project, same + as any other binding. + +Bug fixes +--------- + +* Many. + +Building +-------- + +* Make pkg-config dependency conditional. + +Distribution +------------ + +* Removed Debian packaging, which is now available at packages.debian.org + or via apt-get. + + +0MQ version 2.2.0 (Stable), released on 2012/04/04 +================================================== + +Changes +------- + +* Fixed issue 349, add send/recv timeout socket options. + +Bug fixes +--------- + +* Fixed issue 301, fix builds on HP-UX 11iv3 when using either gcc or aCC. + +* Fixed issue 305, memory leakage when using dynamic subscriptions. + +* Fixed issue 332, libzmq doesn't compile on Android NDK. + +* Fixed issue 293, libzmq doesn't follow ZMTP/1.0 spec. + +* Fixed issue 342, cannot build against zmq.hpp under C++11. + + +0MQ version 2.1.11 (Stable), released on 2011/12/18 +=================================================== + +Bug fixes +--------- + +* Fixed issue 290, zmq_poll was using system time instead of monotonic + clock (Mika Fischer). + +* Fixed issue 281, crash on heavy socket creation - assertion failure in + mutex.hpp:91. (Mika Fischer). + +* Fixed issue 273, O_CLOEXEC flag used in ip.cpp:192 is supported only + on Linux kernels 2.6.27+ + +* Fixed issue 261, assertion failure in kqueue.cpp:76. + +* Fixed issue 269, faulty diagnostic code in 2.1.10. + +* Fixed issue 254, assertion failure at tcp_socket.cpp:229 on ENOTCONN. + +Changes +------- + +* Now builds properly on AIX 6.1 (AJ Lewis). + +* Builds using libdcekt on HP-UX (AJ Lewis). + +* New upstream OpenPGM maintenance release 5.1.118. + +* Enabled debugging on assertion failure on Windows (Paul Betts). + + +0MQ version 2.1.10 (Stable), released on 2011/10/03 +=================================================== + +Bug fixes +--------- + +* Fixed issue 140, SWAP failed with assertion failure in pipe.cpp:187 + if the current directory was not writeable. Behavior now is to return + -1 at zmq_setsockopt in this situation. + +* Fixed issue 207, assertion failure in zmq_connecter.cpp:48, when an + invalid zmq_connect() string was used, or the hostname could not be + resolved. The zmq_connect() call now returns -1 in both those cases. + +* Fixed issue 218, sockets not opened with SOCK_CLOEXEC, causing fork/exec + to sit on sockets unnecessarily. + +* Fixed issue 250, build errors on Windows (Mikko Koppanen). + +* Fixed issue 252, assertion failure in req.cpp:87 and req.cpp:88 (Mikko + Koppanen). + + +0MQ version 2.1.9 (Stable), released on 2011/08/29 +================================================== + +Bug fixes +--------- + +* Fixed issue 240, assertion failure in pgm_socket.cpp:437. + +* Fixed issue 238, assertion failure in zmq.cpp:655, when zmq_poll is + used on an empty set, on Windows. + +* Fixed issue 239, assertion failure in zmq.cpp:223, when ZMQ_SWAP was + used with explicit identities and multiple SUB sockets. + +* Fixed issue 236, zmq_send() and zmq_recv() did not always return + error conditions such as EFSM properly. This bug was introduced in + version 2.1.8 by the backport of changes for issue 231. + +Building +-------- + +* 0MQ support for Android added (Bill Roberts, Mikko Koppanen). + + +0MQ version 2.1.8 (RC), released on 2011/07/28 +============================================== + +Bug fixes +--------- + +* Fixed issue 223, assertion failure in tcp_connecter.cpp:300 when + connecting to a server that is on an unreachable network (errno is + equal to ENETUNREACH). + +* Fixed issue 228, assertion failure at rep.cpp:88 when HWM was reached. + +* Fixed issue 231, assertion failure at mailbox.cpp:183 when too many + pending socketpair operations were queued (major backport from 3.0). + +* Fixed issue 234, assertion failure at mailbox.cpp:77 when Ctrl-C was + used (only affected git master following backport for 231). + +* Fixed issue 230, SIGPIPE killing servers when client disconnected, hit + OS/X only. + +Note: this release was renamed "release candidate" due to issue 236, +fixed in 2.1.9. + + +0MQ version 2.1.7 (Stable), released on 2011/05/12 +================================================== + +Bug fixes +--------- + +* Fixed issue 188, assert when closing socket that had unread multipart + data still on it (affected PULL, SUB, ROUTER, and DEALER sockets). + +* Fixed issue 191, message atomicity issue with PUB sockets (an old issue). + +* Fixed issue 199 (affected ROUTER/XREP sockets, an old issue). + +* Fixed issue 206, assertion failure in zmq.cpp:223, affected all sockets + (bug was introduced in 2.1.6 as part of message validity checking). + +* Fixed issue 211, REP socket asserted if sent malformed envelope (old issue + due to abuse of assertions for error checking). + +* Fixed issue 212, reconnect failing after resume from sleep on Windows + (due to not handling WSAENETDOWN). + +* Properly handle WSAENETUNREACH on Windows (e.g. if client connects + before server binds). + +* Fixed memory leak with threads on Windows. + +Changes +------- + +* Checks zmq_msg_t validity at each operation. + +* Inproc performance tests now work on Windows. + +* PGM wire format specification improved in zmq_pgm(7) + +* Added thread latency/throughput performance examples. + +* Added "--with-system-pgm" configure option to use already installed + OpenPGM. + +* Runtime checking of socket and context validity, to catch e.g. using a + socket after closing it, or passing an invalid pointer to context/socket + methods. + +* Test cases moved off port 5555, which conflicts with other services. + +* Clarified zmq_poll man page that the resolution of the timeout is 1msec. + + +0MQ version 2.1.6 (Broken), released on 2011/04/26 +================================================== + +Note that this version contained a malformed patch and is not usable. +It is not available for download, but is available in the git via the +2.1.6 tag. + +0MQ version 2.1.5 (Broken), released on 2011/04/20 +================================================== + +Note that this version contained a malformed patch and is not usable. +It is not available for download, but is available in the git via the +2.1.5 tag. + + +0MQ version 2.1.4 (Stable), released on 2011/03/30 +================================================== + +Bug fixes +--------- + +* Fix to OpenPGM which was asserting on small messages (Steven McCoy). + +Changes +------- + +* Upgraded OpenPGM to version 5.1.115 (Pieter Hintjens). + +* OpenPGM build changed to not install OpenPGM artifacts. + + +0MQ version 2.1.3 (Stable), released on 2011/03/21 +================================================== + +Bug fixes +--------- + +* Fix to PUSH sockets, which would sometimes deliver tail frames of a + multipart message to new subscribers (Martin Sustrik). + +* Fix to PUB sockets, which would sometimes deliver tail frames of a + multipart message to new subscribers (Martin Sustrik). + +* Windows build was broken due to EPROTONOSUPPORT not being defined. This + has now been fixed (Martin Sustrik). + +* Various fixes to make OpenVMS port work (Brett Cameron). + +* Corrected Reference Manual to note that ZMQ_LINGER socket option may be + set at any time, not just before connecting/binding (Pieter Hintjens). + +* Fix to C++ binding to properly close sockets (Guido Goldstein). + +* Removed obsolete assert from pgm_socket.cpp (Martin Sustrik). + +Changes +------- + +* Removed stand-alone devices (/devices subdirectory) from distribution. + These undocumented programs remain available in older packages (Pieter + Hintjens). + +* OpenPGM default rate raised to 40mbps by default (Steven McCoy). + +* ZMQ_DEALER and ZMQ_ROUTER macros provided to ease upgrade to 0MQ/3.0. + These are scheduled to replace ZMQ_XREQ and ZMQ_XREP (Pieter Hintjens). + +* Added man page for zmq_device(3) which was hereto undocumented (Pieter + Hintjens). + +* Removed zmq_queue(3), zmq_forwarder(3), zmq_streamer(3) man pages + (Pieter Hintjens). + +OpenPGM Integration +------------------- + +* Upgraded OpenPGM to version 5.1.114 (Steven McCoy, Mikko Koppanen). + +* Build system now calls OpenPGM build process directly, allowing easier + future upgrades of OpenPGM (Mikko Koppanen). + +* Build system allows configuration with arbitrary versions of OpenPGM + (./configure --with-pgm=libpgm-x.y.z) (Mikko Koppanen). + +* OpenPGM uses new PGM_ODATA_MAX_RTE controlling original data instead of + PGM_TXW_MAX_RTE covering entire channel (Steven McCoy). + +Building +-------- + +* 0MQ builds properly on FreeBSD (Mikko Koppanen). + + +0MQ version 2.1.2 (rc2), released on 2011/03/06 +=============================================== + +Bug fixes +--------- + +* 0MQ now correctly handles durable inproc sockets; previously it ignored + explicit identities on inproc sockets. + +* Various memory leaks were fixed. + +* OpenPGM sender/receiver creation fixed. + + +0MQ version 2.1.1 (rc1), released on 2011/02/23 +=============================================== + +New functionality +----------------- + +* New socket option ZMQ_RECONNECT_IVL_MAX added, allows for exponential + back-off strategy when reconnecting. + +* New socket option ZMQ_RECOVERY_IVL_MSEC added, as a fine-grained + counterpart to ZMQ_RECOVERY_IVL (for multicast transports). + +* If memory is exhausted, 0MQ warns with an explicit message before + aborting the process. + +* Size of inproc HWM and SWAP is sum of peers' HWMs and SWAPs (Douglas + Greager, Martin Sustrik). + +Bug fixes +--------- + +* 0MQ no longer asserts in mailbox.cpp when multiple peers connect with + the same identity. + +* 0MQ no longer asserts when rejecting an oversized message. + +* 0MQ no longer asserts in pipe.cpp when the swap fills up. + +* zmq_poll now works correctly with an empty poll set. + +* Many more. + +Building +-------- + +* 0MQ now builds correctly on CentOS, Debian 6, and SunOS/gcc3. + +* Added WithOpenPGM configuration into MSVC builds. + +Known issues +------------ + +* OpenPGM integration is still not fully stable. + + +0MQ version 2.1.0 (Beta), released on 2010/12/01 +================================================ + +New functionality +----------------- + +* New semantics for zmq_close () and zmq_term () ensure that all messages + are sent before the application terminates. This behaviour may be + modified using the new ZMQ_LINGER socket option; for further details + refer to the reference manual. + +* The new socket options ZMQ_FD and ZMQ_EVENTS provide a way to integrate + 0MQ sockets into existing poll/event loops. + +* Sockets may now be migrated between OS threads, as long as the + application ensures that a full memory barrier is issued. + +* The 0MQ ABI exported by libzmq.so has been formalised; DSO symbol + visibility is used on supported platforms to ensure that only public ABI + symbols are exported. The library ABI version has been set to 1.0.0 for + this release. + +* OpenPGM has been updated to version 5.0.92. This version no longer + depends on GLIB, and integration with 0MQ should be much improved. + +* zmq_poll() now honors timeouts precisely, and no longer returns if no + events are signaled. + +* Blocking calls now return EINTR if interrupted by the delivery of a + signal; this also means that language bindings which previously had + problems with handling SIGINT/^C should now work correctly. + +* The ZMQ_TYPE socket option was added; this allows retrieval of the socket + type after creation. + +* Added a ZMQ_VERSION macro to zmq.h for compile-time API version + detection. + +* The ZMQ_RECONNECT_IVL and ZMQ_BACKLOG socket options have been added. + +Bug fixes +--------- + +* Forwarder and streamer devices now handle multi-part messages correctly. + +* 0MQ no longer asserts when malformed data is received on the wire. + +* 0MQ internal timers now work correctly if the TSC jumps backwards. + +* The internal signalling functionality (mailbox) has been improved + to automatically resize socket buffers on POSIX systems. + +* Many more. + +Building +-------- + +* 0MQ now builds correctly with many more non-GCC compilers (Sun Studio, + Intel ICC, CLang). + +* AIX and HP-UX builds should work now. + +* FD_SETSIZE has been set to 1024 by default for MSVC builds. + +* Windows builds using GCC (MinGW) now work out of the box. + +Distribution +------------ + +* A simple framework for regression tests has been added, along with a few + basic self-tests. The tests can be run using "make check". diff --git a/3rd/libzmq/branding.bmp b/3rd/libzmq/branding.bmp new file mode 100644 index 00000000..cf0ee284 Binary files /dev/null and b/3rd/libzmq/branding.bmp differ diff --git a/3rd/libzmq/builds/cmake/Modules/ClangFormat.cmake b/3rd/libzmq/builds/cmake/Modules/ClangFormat.cmake new file mode 100644 index 00000000..f36ea069 --- /dev/null +++ b/3rd/libzmq/builds/cmake/Modules/ClangFormat.cmake @@ -0,0 +1,41 @@ +# additional target to perform clang-format run, requires clang-format + +# get all project files +file(GLOB_RECURSE ALL_SOURCE_FILES + RELATIVE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src/*.cpp ${CMAKE_SOURCE_DIR}/src/*.h ${CMAKE_SOURCE_DIR}/src/*.hpp + ${CMAKE_SOURCE_DIR}/tests/*.cpp ${CMAKE_SOURCE_DIR}/tests/*.h ${CMAKE_SOURCE_DIR}/tests/*.hpp + ${CMAKE_SOURCE_DIR}/perf/*.cpp ${CMAKE_SOURCE_DIR}/perf/*.h ${CMAKE_SOURCE_DIR}/perf/*.hpp + ${CMAKE_SOURCE_DIR}/tools/*.cpp ${CMAKE_SOURCE_DIR}/tools/*.h ${CMAKE_SOURCE_DIR}/tools/*.hpp + ${CMAKE_SOURCE_DIR}/include/*.h + ) + +if("${CLANG_FORMAT}" STREQUAL "") + set(CLANG_FORMAT "clang-format") +endif() + +add_custom_target( + clang-format + COMMAND ${CLANG_FORMAT} -style=file -i ${ALL_SOURCE_FILES} +) + +function(JOIN VALUES GLUE OUTPUT) + string (REPLACE ";" "${GLUE}" _TMP_STR "${VALUES}") + set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) +endfunction() + +configure_file(builds/cmake/clang-format-check.sh.in clang-format-check.sh @ONLY) + +add_custom_target( + clang-format-check + COMMAND chmod +x clang-format-check.sh + COMMAND ./clang-format-check.sh + COMMENT "Checking correct formatting according to .clang-format file using ${CLANG_FORMAT}" +) + +add_custom_target( + clang-format-diff + COMMAND ${CLANG_FORMAT} -style=file -i ${ALL_SOURCE_FILES} + COMMAND git diff ${ALL_SOURCE_FILES} + COMMENT "Formatting with clang-format (using ${CLANG_FORMAT}) and showing differences with latest commit" +) diff --git a/3rd/libzmq/builds/cmake/Modules/FindAsciiDoc.cmake b/3rd/libzmq/builds/cmake/Modules/FindAsciiDoc.cmake new file mode 100644 index 00000000..049ac007 --- /dev/null +++ b/3rd/libzmq/builds/cmake/Modules/FindAsciiDoc.cmake @@ -0,0 +1,26 @@ +# - Find Asciidoc +# this module looks for asciidoc and a2x +# +# ASCIIDOC_EXECUTABLE - the full path to asciidoc +# ASCIIDOC_FOUND - If false, don't attempt to use asciidoc. +# A2X_EXECUTABLE - the full path to a2x +# A2X_FOUND - If false, don't attempt to use a2x. + +set (PROGRAMFILESX86 "PROGRAMFILES(X86)") + +find_program(ASCIIDOC_EXECUTABLE asciidoc asciidoc.py + PATHS "$ENV{ASCIIDOC_ROOT}" + "$ENV{PROGRAMW6432}/asciidoc" + "$ENV{PROGRAMFILES}/asciidoc" + "$ENV{${PROGRAMFILESX86}}/asciidoc") + +find_program(A2X_EXECUTABLE a2x + PATHS "$ENV{ASCIIDOC_ROOT}" + "$ENV{PROGRAMW6432}/asciidoc" + "$ENV{PROGRAMFILES}/asciidoc" + "$ENV{${PROGRAMFILESX86}}/asciidoc") + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_ARGS(AsciiDoc REQUIRED_VARS ASCIIDOC_EXECUTABLE) +mark_as_advanced(ASCIIDOC_EXECUTABLE A2X_EXECUTABLE) diff --git a/3rd/libzmq/builds/cmake/Modules/FindNSS3.cmake b/3rd/libzmq/builds/cmake/Modules/FindNSS3.cmake new file mode 100644 index 00000000..9adb4db2 --- /dev/null +++ b/3rd/libzmq/builds/cmake/Modules/FindNSS3.cmake @@ -0,0 +1,8 @@ +include(FindPackageHandleStandardArgs) + +if (NOT MSVC) + find_package(PkgConfig REQUIRED) + pkg_check_modules(NSS3 "nss>=3.19") + find_package_handle_standard_args(NSS3 DEFAULT_MSG NSS3_LIBRARIES NSS3_CFLAGS) +endif() + diff --git a/3rd/libzmq/builds/cmake/Modules/FindSodium.cmake b/3rd/libzmq/builds/cmake/Modules/FindSodium.cmake new file mode 100644 index 00000000..9ec36d1d --- /dev/null +++ b/3rd/libzmq/builds/cmake/Modules/FindSodium.cmake @@ -0,0 +1,48 @@ +################################################################################ +# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY # +# Please refer to the README for information about making permanent changes. # +################################################################################ + +if (NOT MSVC) +find_package(PkgConfig REQUIRED) +pkg_check_modules(PC_SODIUM "libsodium") +if (PC_SODIUM_FOUND) + set(pkg_config_names_private "${pkg_config_names_private} libsodium") +endif() +if (NOT PC_SODIUM_FOUND) + pkg_check_modules(PC_SODIUM "sodium") + if (PC_SODIUM_FOUND) + set(pkg_config_names_private "${pkg_config_names_private} sodium") + endif() +endif (NOT PC_SODIUM_FOUND) +if (PC_SODIUM_FOUND) + set(SODIUM_INCLUDE_HINTS ${PC_SODIUM_INCLUDE_DIRS} ${PC_SODIUM_INCLUDE_DIRS}/*) + set(SODIUM_LIBRARY_HINTS ${PC_SODIUM_LIBRARY_DIRS} ${PC_SODIUM_LIBRARY_DIRS}/*) +else() + set(pkg_config_libs_private "${pkg_config_libs_private} -lsodium") +endif() +endif (NOT MSVC) + +# some libraries install the headers is a subdirectory of the include dir +# returned by pkg-config, so use a wildcard match to improve chances of finding +# headers and libraries. +find_path( + SODIUM_INCLUDE_DIRS + NAMES sodium.h + HINTS ${SODIUM_INCLUDE_HINTS} +) + +find_library( + SODIUM_LIBRARIES + NAMES libsodium sodium + HINTS ${SODIUM_LIBRARY_HINTS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(sodium DEFAULT_MSG SODIUM_LIBRARIES SODIUM_INCLUDE_DIRS) +mark_as_advanced(SODIUM_FOUND SODIUM_LIBRARIES SODIUM_INCLUDE_DIRS) + +################################################################################ +# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY # +# Please refer to the README for information about making permanent changes. # +################################################################################ diff --git a/3rd/libzmq/builds/cmake/Modules/TestZMQVersion.cmake b/3rd/libzmq/builds/cmake/Modules/TestZMQVersion.cmake new file mode 100644 index 00000000..49a3f309 --- /dev/null +++ b/3rd/libzmq/builds/cmake/Modules/TestZMQVersion.cmake @@ -0,0 +1,8 @@ + +file(READ "${PROJECT_SOURCE_DIR}/include/zmq.h" _ZMQ_H_CONTENTS) +string(REGEX REPLACE ".*#define ZMQ_VERSION_MAJOR ([0-9]+).*" "\\1" ZMQ_VERSION_MAJOR "${_ZMQ_H_CONTENTS}") +string(REGEX REPLACE ".*#define ZMQ_VERSION_MINOR ([0-9]+).*" "\\1" ZMQ_VERSION_MINOR "${_ZMQ_H_CONTENTS}") +string(REGEX REPLACE ".*#define ZMQ_VERSION_PATCH ([0-9]+).*" "\\1" ZMQ_VERSION_PATCH "${_ZMQ_H_CONTENTS}") +set(ZMQ_VERSION "${ZMQ_VERSION_MAJOR}.${ZMQ_VERSION_MINOR}.${ZMQ_VERSION_PATCH}") + +message(STATUS "Detected ZMQ Version - ${ZMQ_VERSION}") diff --git a/3rd/libzmq/builds/cmake/Modules/ZMQSourceRunChecks.cmake b/3rd/libzmq/builds/cmake/Modules/ZMQSourceRunChecks.cmake new file mode 100644 index 00000000..6f99f0aa --- /dev/null +++ b/3rd/libzmq/builds/cmake/Modules/ZMQSourceRunChecks.cmake @@ -0,0 +1,332 @@ + + +macro(zmq_check_sock_cloexec) + message(STATUS "Checking whether SOCK_CLOEXEC is supported") + check_c_source_runs( + " +#include +#include + +int main(int argc, char *argv []) +{ + int s = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + return(s == -1); +} +" + ZMQ_HAVE_SOCK_CLOEXEC) +endmacro() + +macro(zmq_check_efd_cloexec) + message(STATUS "Checking whether EFD_CLOEXEC is supported") + check_c_source_runs( + " +#include + +int main(int argc, char *argv []) +{ + int s = eventfd (0, EFD_CLOEXEC); + return(s == -1); +} +" + ZMQ_HAVE_EVENTFD_CLOEXEC) +endmacro() + +macro(zmq_check_o_cloexec) + message(STATUS "Checking whether O_CLOEXEC is supported") + check_c_source_runs( + " +#include +#include +#include + +int main(int argc, char *argv []) +{ + int s = open (\"/dev/null\", O_CLOEXEC | O_RDONLY); + return s == -1; +} +" + ZMQ_HAVE_O_CLOEXEC) +endmacro() + +macro(zmq_check_so_bindtodevice) + message(STATUS "Checking whether SO_BINDTODEVICE is supported") + check_c_source_runs( +" +#include + +int main(int argc, char *argv []) +{ +/* Actually making the setsockopt() call requires CAP_NET_RAW */ +#ifndef SO_BINDTODEVICE + return 1; +#else + return 0; +#endif +} +" + ZMQ_HAVE_SO_BINDTODEVICE) +endmacro() + +# TCP keep-alives Checks. + +macro(zmq_check_so_keepalive) + message(STATUS "Checking whether SO_KEEPALIVE is supported") + check_c_source_runs( +" +#include +#include + +int main(int argc, char *argv []) +{ + int s, rc, opt = 1; + return( + ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) || + ((rc = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,(char*) &opt, sizeof(int))) == -1) + ); +} +" + ZMQ_HAVE_SO_KEEPALIVE) +endmacro() + +macro(zmq_check_tcp_keepcnt) + message(STATUS "Checking whether TCP_KEEPCNT is supported") + check_c_source_runs( + " +#include +#include +#include +#include + +int main(int argc, char *argv []) +{ + int s, rc, opt = 1; + return( + ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) || + ((rc = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,(char*) &opt, sizeof(int))) == -1) || + ((rc = setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT,(char*) &opt, sizeof(int))) == -1) + ); +} +" + ZMQ_HAVE_TCP_KEEPCNT) +endmacro() + +macro(zmq_check_tcp_keepidle) + message(STATUS "Checking whether TCP_KEEPIDLE is supported") + check_c_source_runs( + " +#include +#include +#include +#include + +int main(int argc, char *argv []) +{ + int s, rc, opt = 1; + return( + ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) || + ((rc = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,(char*) &opt, sizeof(int))) == -1) || + ((rc = setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE,(char*) &opt, sizeof(int))) == -1) + ); +} +" + ZMQ_HAVE_TCP_KEEPIDLE) +endmacro() + + +macro(zmq_check_tcp_keepintvl) + message(STATUS "Checking whether TCP_KEEPINTVL is supported") + check_c_source_runs( + " +#include +#include +#include +#include + +int main(int argc, char *argv []) +{ + int s, rc, opt = 1; + return( + ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) || + ((rc = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,(char*) &opt, sizeof(int))) == -1) || + ((rc = setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL,(char*) &opt, sizeof(int))) == -1) + ); +} + +" + ZMQ_HAVE_TCP_KEEPINTVL) +endmacro() + + +macro(zmq_check_tcp_keepalive) + message(STATUS "Checking whether TCP_KEEPALIVE is supported") + check_c_source_runs( + " +#include +#include +#include +#include + +int main(int argc, char *argv []) +{ + int s, rc, opt = 1; + return( + ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) || + ((rc = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,(char*) &opt, sizeof(int))) == -1) || + ((rc = setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE,(char*) &opt, sizeof(int))) == -1) + ); +} +" + ZMQ_HAVE_TCP_KEEPALIVE) +endmacro() + + +macro(zmq_check_tcp_tipc) + message(STATUS "Checking whether TIPC is supported") + check_c_source_runs( + " +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv []) +{ + struct sockaddr_tipc topsrv; + int sd = socket(AF_TIPC, SOCK_SEQPACKET, 0); + memset(&topsrv, 0, sizeof(topsrv)); + topsrv.family = AF_TIPC; + topsrv.addrtype = TIPC_ADDR_NAME; + topsrv.addr.name.name.type = TIPC_TOP_SRV; + topsrv.addr.name.name.instance = TIPC_TOP_SRV; + fcntl(sd, F_SETFL, O_NONBLOCK); +} +" + ZMQ_HAVE_TIPC) +endmacro() + + +macro(zmq_check_pthread_setname) + message(STATUS "Checking pthread_setname signature") + set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "-D_GNU_SOURCE -Werror -pthread") + check_c_source_runs( + " +#include + +int main(int argc, char *argv []) +{ + pthread_setname_np (\"foo\"); + return 0; +} +" + ZMQ_HAVE_PTHREAD_SETNAME_1) + check_c_source_runs( + " +#include + +int main(int argc, char *argv []) +{ + pthread_setname_np (pthread_self(), \"foo\"); + return 0; +} +" + ZMQ_HAVE_PTHREAD_SETNAME_2) + check_c_source_runs( + " +#include + +int main(int argc, char *argv []) +{ + pthread_setname_np (pthread_self(), \"foo\", (void *)0); + return 0; +} +" + ZMQ_HAVE_PTHREAD_SETNAME_3) + check_c_source_runs( + " +#include + +int main(int argc, char *argv []) +{ + pthread_set_name_np (pthread_self(), \"foo\"); + return 0; +} +" + ZMQ_HAVE_PTHREAD_SET_NAME) + set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) +endmacro() + +macro(zmq_check_pthread_setaffinity) + message(STATUS "Checking pthread_setaffinity signature") + set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "-D_GNU_SOURCE -Werror -pthread") + check_c_source_runs( + " +#include + +int main(int argc, char *argv []) +{ + cpu_set_t test; + pthread_setaffinity_np (pthread_self(), sizeof(cpu_set_t), &test); + return 0; +} +" + ZMQ_HAVE_PTHREAD_SET_AFFINITY) + set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) +endmacro() + + +macro(zmq_check_getrandom) + message(STATUS "Checking whether getrandom is supported") + check_c_source_runs( + " +#include + +int main (int argc, char *argv []) +{ + char buf[4]; + int rc = getrandom(buf, 4, 0); + return rc == -1 ? 1 : 0; +} +" + ZMQ_HAVE_GETRANDOM) +endmacro() + +macro(zmq_check_noexcept) + message(STATUS "Checking whether noexcept is supported") + check_cxx_source_compiles( +" +struct X +{ + X(int i) noexcept {} +}; + +int main(int argc, char *argv []) +{ + X x(5); + return 0; +} +" + ZMQ_HAVE_NOEXCEPT) +endmacro() + +macro(zmq_check_so_priority) + message(STATUS "Checking whether SO_PRIORITY is supported") + check_c_source_runs( + " +#include +#include + +int main (int argc, char *argv []) +{ + int s, rc, opt = 1; + return ( + ((s = socket (PF_INET, SOCK_STREAM, 0)) == -1) || + ((rc = setsockopt (s, SOL_SOCKET, SO_PRIORITY, (char*) &opt, sizeof (int))) == -1) + ); +} +" + ZMQ_HAVE_SO_PRIORITY) +endmacro() diff --git a/3rd/libzmq/builds/cmake/Modules/ZMQSupportMacros.cmake b/3rd/libzmq/builds/cmake/Modules/ZMQSupportMacros.cmake new file mode 100644 index 00000000..5139015f --- /dev/null +++ b/3rd/libzmq/builds/cmake/Modules/ZMQSupportMacros.cmake @@ -0,0 +1,5 @@ +macro (zmq_set_with_default var value) + if (NOT ${var}) + set(${var} "${value}") + endif () +endmacro () diff --git a/3rd/libzmq/builds/cmake/NSIS.template32.in b/3rd/libzmq/builds/cmake/NSIS.template32.in new file mode 100644 index 00000000..bc6ab8d5 --- /dev/null +++ b/3rd/libzmq/builds/cmake/NSIS.template32.in @@ -0,0 +1,952 @@ +; CPack install script designed for a nmake build + +;-------------------------------- +; You must define these values + + !define VERSION "@CPACK_PACKAGE_VERSION@" + !define PATCH "@CPACK_PACKAGE_VERSION_PATCH@" + !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" + +;-------------------------------- +;Variables + + Var MUI_TEMP + Var STARTMENU_FOLDER + Var SV_ALLUSERS + Var START_MENU + Var DO_NOT_ADD_TO_PATH + Var ADD_TO_PATH_ALL_USERS + Var ADD_TO_PATH_CURRENT_USER + Var INSTALL_DESKTOP + Var IS_DEFAULT_INSTALLDIR +;-------------------------------- +;Include Modern UI + + !include "MUI.nsh" + + ;Default installation folder + InstallDir "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + ;InstallDir "$PROGRAMFILES64\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + +;-------------------------------- +;General + + ;Name and file + Name "@CPACK_NSIS_PACKAGE_NAME@" + OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" + + ;Set compression + SetCompressor @CPACK_NSIS_COMPRESSOR@ + +@CPACK_NSIS_DEFINES@ + + !include Sections.nsh + +;--- Component support macros: --- +; The code for the add/remove functionality is from: +; http://nsis.sourceforge.net/Add/Remove_Functionality +; It has been modified slightly and extended to provide +; inter-component dependencies. +Var AR_SecFlags +Var AR_RegFlags +@CPACK_NSIS_SECTION_SELECTED_VARS@ + +; Loads the "selected" flag for the section named SecName into the +; variable VarName. +!macro LoadSectionSelectedIntoVar SecName VarName + SectionGetFlags ${${SecName}} $${VarName} + IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits +!macroend + +; Loads the value of a variable... can we get around this? +!macro LoadVar VarName + IntOp $R0 0 + $${VarName} +!macroend + +; Sets the value of a variable +!macro StoreVar VarName IntValue + IntOp $${VarName} 0 + ${IntValue} +!macroend + +!macro InitSection SecName + ; This macro reads component installed flag from the registry and + ;changes checked state of the section on the components page. + ;Input: section index constant name specified in Section command. + + ClearErrors + ;Reading component status from registry + ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" "Installed" + IfErrors "default_${SecName}" + ;Status will stay default if registry value not found + ;(component was never installed) + IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags + IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off + IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit + + ; Note whether this component was installed before + !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags + IntOp $R0 $AR_RegFlags & $AR_RegFlags + + ;Writing modified flags + SectionSetFlags ${${SecName}} $AR_SecFlags + + "default_${SecName}:" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected +!macroend + +!macro FinishSection SecName + ; This macro reads section flag set by user and removes the section + ;if it is not selected. + ;Then it writes component installed flag to registry + ;Input: section index constant name specified in Section command. + + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags + ;Checking lowest bit: + IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} + IntCmp $AR_SecFlags 1 "leave_${SecName}" + ;Section is not selected: + ;Calling Section uninstall macro and writing zero installed flag + !insertmacro "Remove_${${SecName}}" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \ + "Installed" 0 + Goto "exit_${SecName}" + + "leave_${SecName}:" + ;Section is selected: + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \ + "Installed" 1 + + "exit_${SecName}:" +!macroend + +!macro RemoveSection SecName + ; This macro is used to call section's Remove_... macro + ;from the uninstaller. + ;Input: section index constant name specified in Section command. + + !insertmacro "Remove_${${SecName}}" +!macroend + +; Determine whether the selection of SecName changed +!macro MaybeSelectionChanged SecName + !insertmacro LoadVar ${SecName}_selected + SectionGetFlags ${${SecName}} $R1 + IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits + + ; See if the status has changed: + IntCmp $R0 $R1 "${SecName}_unchanged" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected + + IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected" + !insertmacro "Deselect_required_by_${SecName}" + goto "${SecName}_unchanged" + + "${SecName}_was_selected:" + !insertmacro "Select_${SecName}_depends" + + "${SecName}_unchanged:" +!macroend +;--- End of Add/Remove macros --- + +;-------------------------------- +;Interface Settings + + !define MUI_HEADERIMAGE + !define MUI_ABORTWARNING + +;-------------------------------- +; path functions + +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 + +;---------------------------------------- +; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" +;---------------------------------------- +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 +;==================================================== +; get_NT_environment +; Returns: the selected environment +; Output : head of the stack +;==================================================== +!macro select_NT_profile UN +Function ${UN}select_NT_profile + StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single + DetailPrint "Selected environment for all users" + Push "all" + Return + environment_single: + DetailPrint "Selected environment for current user only." + Push "current" + Return +FunctionEnd +!macroend +!insertmacro select_NT_profile "" +!insertmacro select_NT_profile "un." +;---------------------------------------------------- +!define NT_current_env 'HKCU "Environment"' +!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + +!ifndef WriteEnvStr_RegKey + !ifdef ALL_USERS + !define WriteEnvStr_RegKey \ + 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + !else + !define WriteEnvStr_RegKey 'HKCU "Environment"' + !endif +!endif + +; AddToPath - Adds the given dir to the search path. +; Input - head of the stack +; Note - Win9x systems requires reboot + +Function AddToPath + Exch $0 + Push $1 + Push $2 + Push $3 + + # don't add if the path doesn't exist + IfFileExists "$0\*.*" "" AddToPath_done + + ReadEnvStr $1 PATH + ; if the path is too long for a NSIS variable NSIS will return a 0 + ; length string. If we find that, then warn and skip any path + ; modification as it will trash the existing path. + StrLen $2 $1 + IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done + CheckPathLength_ShowPathWarning: + Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" + Goto AddToPath_done + CheckPathLength_Done: + Push "$1;" + Push "$0;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$0\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + GetFullPathName /SHORT $3 $0 + Push "$1;" + Push "$3;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$3\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + + Call IsNT + Pop $1 + StrCmp $1 1 AddToPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" a + FileSeek $1 -1 END + FileReadByte $1 $2 + IntCmp $2 26 0 +2 +2 # DOS EOF + FileSeek $1 -1 END # write over EOF + FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" + FileClose $1 + SetRebootFlag true + Goto AddToPath_done + + AddToPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto DoTrim + ReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + DoTrim: + StrCmp $1 "" AddToPath_NTdoIt + Push $1 + Call Trim + Pop $1 + StrCpy $0 "$1;$0" + AddToPath_NTdoIt: + StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $0 + Goto DoSend + WriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $0 + DoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + AddToPath_done: + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; RemoveFromPath - Remove a given dir from the path +; Input: head of the stack + +Function un.RemoveFromPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + + IntFmt $6 "%c" 26 # DOS EOF + + Call un.IsNT + Pop $1 + StrCmp $1 1 unRemoveFromPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" r + GetTempFileName $4 + FileOpen $2 $4 w + GetFullPathName /SHORT $0 $0 + StrCpy $0 "SET PATH=%PATH%;$0" + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoop: + FileRead $1 $3 + StrCpy $5 $3 1 -1 # read last char + StrCmp $5 $6 0 +2 # if DOS EOF + StrCpy $3 $3 -1 # remove DOS EOF so we can compare + StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "" unRemoveFromPath_dosLoopEnd + FileWrite $2 $3 + Goto unRemoveFromPath_dosLoop + unRemoveFromPath_dosLoopRemoveLine: + SetRebootFlag true + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoopEnd: + FileClose $2 + FileClose $1 + StrCpy $1 $WINDIR 2 + Delete "$1\autoexec.bat" + CopyFiles /SILENT $4 "$1\autoexec.bat" + Delete $4 + Goto unRemoveFromPath_done + + unRemoveFromPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto unDoTrim + unReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + unDoTrim: + StrCpy $5 $1 1 -1 # copy last char + StrCmp $5 ";" +2 # if last char != ; + StrCpy $1 "$1;" # append ; + Push $1 + Push "$0;" + Call un.StrStr ; Find `$0;` in $1 + Pop $2 ; pos of our dir + StrCmp $2 "" unRemoveFromPath_done + ; else, it is in path + # $0 - path to add + # $1 - path var + StrLen $3 "$0;" + StrLen $4 $2 + StrCpy $5 $1 -$4 # $5 is now the part before the path to remove + StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove + StrCpy $3 $5$6 + + StrCpy $5 $3 1 -1 # copy last char + StrCmp $5 ";" 0 +2 # if last char == ; + StrCpy $3 $3 -1 # remove last char + + StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $3 + Goto unDoSend + unWriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $3 + unDoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + unRemoveFromPath_done: + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Uninstall sutff +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +########################################### +# Utility Functions # +########################################### + +;==================================================== +; IsNT - Returns 1 if the current system is NT, 0 +; otherwise. +; Output: head of the stack +;==================================================== +; IsNT +; no input +; output, top of the stack = 1 if NT or 0 if not +; +; Usage: +; Call IsNT +; Pop $R0 +; ($R0 at this point is 1 or 0) + +!macro IsNT un +Function ${un}IsNT + Push $0 + ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + StrCmp $0 "" 0 IsNT_yes + ; we are not NT. + Pop $0 + Push 0 + Return + + IsNT_yes: + ; NT!!! + Pop $0 + Push 1 +FunctionEnd +!macroend +!insertmacro IsNT "" +!insertmacro IsNT "un." + +; StrStr +; input, top of stack = string to search for +; top of stack-1 = string to search in +; output, top of stack (replaces with the portion of the string remaining) +; modifies no other variables. +; +; Usage: +; Push "this is a long ass string" +; Push "ass" +; Call StrStr +; Pop $R0 +; ($R0 at this point is "ass string") + +!macro StrStr un +Function ${un}StrStr +Exch $R1 ; st=haystack,old$R1, $R1=needle + Exch ; st=old$R1,haystack + Exch $R2 ; st=old$R1,old$R2, $R2=haystack + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=needle + ; $R2=haystack + ; $R3=len(needle) + ; $R4=cnt + ; $R5=tmp + loop: + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 done + StrCmp $R5 "" done + IntOp $R4 $R4 + 1 + Goto loop +done: + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 +FunctionEnd +!macroend +!insertmacro StrStr "" +!insertmacro StrStr "un." + +Function Trim ; Added by Pelaca + Exch $R1 + Push $R2 +Loop: + StrCpy $R2 "$R1" 1 -1 + StrCmp "$R2" " " RTrim + StrCmp "$R2" "$\n" RTrim + StrCmp "$R2" "$\r" RTrim + StrCmp "$R2" ";" RTrim + GoTo Done +RTrim: + StrCpy $R1 "$R1" -1 + Goto Loop +Done: + Pop $R2 + Exch $R1 +FunctionEnd + +Function ConditionalAddToRegisty + Pop $0 + Pop $1 + StrCmp "$0" "" ConditionalAddToRegisty_EmptyString + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" \ + "$1" "$0" + ;MessageBox MB_OK "Set Registry: '$1' to '$0'" + DetailPrint "Set install registry entry: '$1' to '$0'" + ConditionalAddToRegisty_EmptyString: +FunctionEnd + +;-------------------------------- + +!ifdef CPACK_USES_DOWNLOAD +Function DownloadFile + IfFileExists $INSTDIR\* +2 + CreateDirectory $INSTDIR + Pop $0 + + ; Skip if already downloaded + IfFileExists $INSTDIR\$0 0 +2 + Return + + StrCpy $1 "@CPACK_DOWNLOAD_SITE@" + + try_again: + NSISdl::download "$1/$0" "$INSTDIR\$0" + + Pop $1 + StrCmp $1 "success" success + StrCmp $1 "Cancelled" cancel + MessageBox MB_OK "Download failed: $1" + cancel: + Return + success: +FunctionEnd +!endif + +;-------------------------------- +; Installation types +@CPACK_NSIS_INSTALLATION_TYPES@ + +;-------------------------------- +; Component sections +@CPACK_NSIS_COMPONENT_SECTIONS@ + +;-------------------------------- +; Define some macro setting for the gui +@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ +@CPACK_NSIS_INSTALLER_ICON_CODE@ +@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@ + +;-------------------------------- +;Pages + !insertmacro MUI_PAGE_WELCOME + + !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" + Page custom InstallOptionsPage + !insertmacro MUI_PAGE_DIRECTORY + + ;Start Menu Folder Page Configuration + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" + !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER + + !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\NEWS.txt" + + @CPACK_NSIS_PAGE_COMPONENTS@ + + !insertmacro MUI_PAGE_INSTFILES + !insertmacro MUI_PAGE_FINISH + + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + !insertmacro MUI_UNPAGE_FINISH + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" ;first language is the default language + !insertmacro MUI_LANGUAGE "Albanian" + !insertmacro MUI_LANGUAGE "Arabic" + !insertmacro MUI_LANGUAGE "Basque" + !insertmacro MUI_LANGUAGE "Belarusian" + !insertmacro MUI_LANGUAGE "Bosnian" + !insertmacro MUI_LANGUAGE "Breton" + !insertmacro MUI_LANGUAGE "Bulgarian" + !insertmacro MUI_LANGUAGE "Croatian" + !insertmacro MUI_LANGUAGE "Czech" + !insertmacro MUI_LANGUAGE "Danish" + !insertmacro MUI_LANGUAGE "Dutch" + !insertmacro MUI_LANGUAGE "Estonian" + !insertmacro MUI_LANGUAGE "Farsi" + !insertmacro MUI_LANGUAGE "Finnish" + !insertmacro MUI_LANGUAGE "French" + !insertmacro MUI_LANGUAGE "German" + !insertmacro MUI_LANGUAGE "Greek" + !insertmacro MUI_LANGUAGE "Hebrew" + !insertmacro MUI_LANGUAGE "Hungarian" + !insertmacro MUI_LANGUAGE "Icelandic" + !insertmacro MUI_LANGUAGE "Indonesian" + !insertmacro MUI_LANGUAGE "Irish" + !insertmacro MUI_LANGUAGE "Italian" + !insertmacro MUI_LANGUAGE "Japanese" + !insertmacro MUI_LANGUAGE "Korean" + !insertmacro MUI_LANGUAGE "Kurdish" + !insertmacro MUI_LANGUAGE "Latvian" + !insertmacro MUI_LANGUAGE "Lithuanian" + !insertmacro MUI_LANGUAGE "Luxembourgish" + !insertmacro MUI_LANGUAGE "Macedonian" + !insertmacro MUI_LANGUAGE "Malay" + !insertmacro MUI_LANGUAGE "Mongolian" + !insertmacro MUI_LANGUAGE "Norwegian" + !insertmacro MUI_LANGUAGE "Polish" + !insertmacro MUI_LANGUAGE "Portuguese" + !insertmacro MUI_LANGUAGE "PortugueseBR" + !insertmacro MUI_LANGUAGE "Romanian" + !insertmacro MUI_LANGUAGE "Russian" + !insertmacro MUI_LANGUAGE "Serbian" + !insertmacro MUI_LANGUAGE "SerbianLatin" + !insertmacro MUI_LANGUAGE "SimpChinese" + !insertmacro MUI_LANGUAGE "Slovak" + !insertmacro MUI_LANGUAGE "Slovenian" + !insertmacro MUI_LANGUAGE "Spanish" + !insertmacro MUI_LANGUAGE "Swedish" + !insertmacro MUI_LANGUAGE "Thai" + !insertmacro MUI_LANGUAGE "TradChinese" + !insertmacro MUI_LANGUAGE "Turkish" + !insertmacro MUI_LANGUAGE "Ukrainian" + !insertmacro MUI_LANGUAGE "Welsh" + + +;-------------------------------- +;Reserve Files + + ;These files should be inserted before other files in the data block + ;Keep these lines before any File command + ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) + + ReserveFile "NSIS.InstallOptions.ini" + !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS + +;-------------------------------- +;Installer Sections + +Section "-Core installation" + ;Use the entire tree produced by the INSTALL target. Keep the + ;list of directories here in sync with the RMDir commands below. + SetOutPath "$INSTDIR" + @CPACK_NSIS_FULL_INSTALL@ + + ;Store installation folder + WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR + + ;Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + Push "DisplayName" + Push "@CPACK_NSIS_DISPLAY_NAME@" + Call ConditionalAddToRegisty + Push "DisplayVersion" + Push "@CPACK_PACKAGE_VERSION@" + Call ConditionalAddToRegisty + Push "Publisher" + Push "@CPACK_PACKAGE_VENDOR@" + Call ConditionalAddToRegisty + Push "UninstallString" + Push "$INSTDIR\Uninstall.exe" + Call ConditionalAddToRegisty + Push "NoRepair" + Push "1" + Call ConditionalAddToRegisty + + !ifdef CPACK_NSIS_ADD_REMOVE + ;Create add/remove functionality + Push "ModifyPath" + Push "$INSTDIR\AddRemove.exe" + Call ConditionalAddToRegisty + !else + Push "NoModify" + Push "1" + Call ConditionalAddToRegisty + !endif + + ; Optional registration + Push "DisplayIcon" + Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@" + Call ConditionalAddToRegisty + Push "HelpLink" + Push "@CPACK_NSIS_HELP_LINK@" + Call ConditionalAddToRegisty + Push "URLInfoAbout" + Push "@CPACK_NSIS_URL_INFO_ABOUT@" + Call ConditionalAddToRegisty + Push "Contact" + Push "@CPACK_NSIS_CONTACT@" + Call ConditionalAddToRegisty + !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + + ;Create shortcuts + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" +@CPACK_NSIS_CREATE_ICONS@ +@CPACK_NSIS_CREATE_ICONS_EXTRA@ + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + + ;Read a value from an InstallOptions INI file + !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" + + ; Write special uninstall registry entries + Push "StartMenu" + Push "$STARTMENU_FOLDER" + Call ConditionalAddToRegisty + Push "DoNotAddToPath" + Push "$DO_NOT_ADD_TO_PATH" + Call ConditionalAddToRegisty + Push "AddToPathAllUsers" + Push "$ADD_TO_PATH_ALL_USERS" + Call ConditionalAddToRegisty + Push "AddToPathCurrentUser" + Push "$ADD_TO_PATH_CURRENT_USER" + Call ConditionalAddToRegisty + Push "InstallToDesktop" + Push "$INSTALL_DESKTOP" + Call ConditionalAddToRegisty + + !insertmacro MUI_STARTMENU_WRITE_END + +@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ + +SectionEnd + +Section "-Add to path" + Push $INSTDIR\bin + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath + StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 + Call AddToPath + doNotAddToPath: +SectionEnd + +;-------------------------------- +; Create custom pages +Function InstallOptionsPage + !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@" + !insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" + +FunctionEnd + +;-------------------------------- +; determine admin versus local install +Function un.onInit + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + Goto done + + noLM: + ;Get installation folder from registry if available + + done: + +FunctionEnd + +;--- Add/Remove callback functions: --- +!macro SectionList MacroName + ;This macro used to perform operation on multiple sections. + ;List all of your components in following manner here. +@CPACK_NSIS_COMPONENT_SECTION_LIST@ +!macroend + +Section -FinishComponents + ;Removes unselected components and writes component status to registry + !insertmacro SectionList "FinishSection" + +!ifdef CPACK_NSIS_ADD_REMOVE + ; Get the name of the installer executable + System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' + StrCpy $R3 $R0 + + ; Strip off the last 13 characters, to see if we have AddRemove.exe + StrLen $R1 $R0 + IntOp $R1 $R0 - 13 + StrCpy $R2 $R0 13 $R1 + StrCmp $R2 "AddRemove.exe" addremove_installed + + ; We're not running AddRemove.exe, so install it + CopyFiles $R3 $INSTDIR\AddRemove.exe + + addremove_installed: +!endif +SectionEnd +;--- End of Add/Remove callback functions --- + +;-------------------------------- +; Component dependencies +Function .onSelChange + !insertmacro SectionList MaybeSelectionChanged +FunctionEnd + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + ReadRegStr $START_MENU SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "StartMenu" + ;MessageBox MB_OK "Start menu is in: $START_MENU" + ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "DoNotAddToPath" + ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathAllUsers" + ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathCurrentUser" + ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" + ReadRegStr $INSTALL_DESKTOP SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "InstallToDesktop" + ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " + +@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ + + ;Remove files we installed. + ;Keep the list of directories here in sync with the File commands above. +@CPACK_NSIS_DELETE_FILES@ +@CPACK_NSIS_DELETE_DIRECTORIES@ + +!ifdef CPACK_NSIS_ADD_REMOVE + ;Remove the add/remove program + Delete "$INSTDIR\AddRemove.exe" +!endif + + ;Remove the uninstaller itself. + Delete "$INSTDIR\Uninstall.exe" + DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" + + ;Remove the installation directory if it is empty. + RMDir "$INSTDIR" + + ; Remove the registry entries. + DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + ; Removes all optional components + !insertmacro SectionList "RemoveSection" + + !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP + + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" +@CPACK_NSIS_DELETE_ICONS@ +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + startMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors startMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop + startMenuDeleteLoopDone: + + ; If the user changed the shortcut, then untinstall may not work. This should + ; try to fix it. + StrCpy $MUI_TEMP "$START_MENU" + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + secondStartMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors secondStartMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop + secondStartMenuDeleteLoopDone: + + DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + Push $INSTDIR\bin + StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 + Call un.RemoveFromPath + doNotRemoveFromPath: +SectionEnd + +;-------------------------------- +; determine admin versus local install +; Is install for "AllUsers" or "JustMe"? +; Default to "JustMe" - set to "AllUsers" if admin or on Win9x +; This function is used for the very first "custom page" of the installer. +; This custom page does not show up visibly, but it executes prior to the +; first visible page and sets up $INSTDIR properly... +; Choose different default installation folder based on SV_ALLUSERS... +; "Program Files" for AllUsers, "My Documents" for JustMe... + +Function .onInit + ; Reads components status for registry + !insertmacro SectionList "InitSection" + + ; check to see if /D has been used to change + ; the install directory by comparing it to the + ; install directory that is expected to be the + ; default + StrCpy $IS_DEFAULT_INSTALLDIR 0 + StrCmp "$INSTDIR" "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 + StrCpy $IS_DEFAULT_INSTALLDIR 1 + + StrCpy $SV_ALLUSERS "JustMe" + ; if default install dir then change the default + ; if it is installed for JustMe + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + + noLM: + StrCpy $SV_ALLUSERS "AllUsers" + ;Get installation folder from registry if available + + done: + StrCmp $SV_ALLUSERS "AllUsers" 0 +3 + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + StrCpy $INSTDIR "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage + !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" + + noOptionsPage: +FunctionEnd diff --git a/3rd/libzmq/builds/cmake/NSIS.template64.in b/3rd/libzmq/builds/cmake/NSIS.template64.in new file mode 100644 index 00000000..2deaa4ec --- /dev/null +++ b/3rd/libzmq/builds/cmake/NSIS.template64.in @@ -0,0 +1,960 @@ +; CPack install script designed for a nmake build + +;-------------------------------- +; You must define these values + + !define VERSION "@CPACK_PACKAGE_VERSION@" + !define PATCH "@CPACK_PACKAGE_VERSION_PATCH@" + !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" + +;-------------------------------- +;Variables + + Var MUI_TEMP + Var STARTMENU_FOLDER + Var SV_ALLUSERS + Var START_MENU + Var DO_NOT_ADD_TO_PATH + Var ADD_TO_PATH_ALL_USERS + Var ADD_TO_PATH_CURRENT_USER + Var INSTALL_DESKTOP + Var IS_DEFAULT_INSTALLDIR +;-------------------------------- +;Include Modern UI + + !include "MUI.nsh" + + ;Default installation folder + ;InstallDir "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + InstallDir "$PROGRAMFILES64\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + +;-------------------------------- +;General + + ;Name and file + Name "@CPACK_NSIS_PACKAGE_NAME@" + OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" + + ;Set compression + SetCompressor @CPACK_NSIS_COMPRESSOR@ + +@CPACK_NSIS_DEFINES@ + + !include Sections.nsh + +;--- Component support macros: --- +; The code for the add/remove functionality is from: +; http://nsis.sourceforge.net/Add/Remove_Functionality +; It has been modified slightly and extended to provide +; inter-component dependencies. +Var AR_SecFlags +Var AR_RegFlags +@CPACK_NSIS_SECTION_SELECTED_VARS@ + +; Loads the "selected" flag for the section named SecName into the +; variable VarName. +!macro LoadSectionSelectedIntoVar SecName VarName + SectionGetFlags ${${SecName}} $${VarName} + IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits +!macroend + +; Loads the value of a variable... can we get around this? +!macro LoadVar VarName + IntOp $R0 0 + $${VarName} +!macroend + +; Sets the value of a variable +!macro StoreVar VarName IntValue + IntOp $${VarName} 0 + ${IntValue} +!macroend + +!macro InitSection SecName + ; This macro reads component installed flag from the registry and + ;changes checked state of the section on the components page. + ;Input: section index constant name specified in Section command. + + ClearErrors + ;Reading component status from registry + ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)\Components\${SecName}" "Installed" + IfErrors "default_${SecName}" + ;Status will stay default if registry value not found + ;(component was never installed) + IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags + IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off + IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit + + ; Note whether this component was installed before + !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags + IntOp $R0 $AR_RegFlags & $AR_RegFlags + + ;Writing modified flags + SectionSetFlags ${${SecName}} $AR_SecFlags + + "default_${SecName}:" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected +!macroend + +!macro FinishSection SecName + ; This macro reads section flag set by user and removes the section + ;if it is not selected. + ;Then it writes component installed flag to registry + ;Input: section index constant name specified in Section command. + + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags + ;Checking lowest bit: + IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} + IntCmp $AR_SecFlags 1 "leave_${SecName}" + ;Section is not selected: + ;Calling Section uninstall macro and writing zero installed flag + !insertmacro "Remove_${${SecName}}" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)\Components\${SecName}" \ + "Installed" 0 + Goto "exit_${SecName}" + + "leave_${SecName}:" + ;Section is selected: + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)\Components\${SecName}" \ + "Installed" 1 + + "exit_${SecName}:" +!macroend + +!macro RemoveSection SecName + ; This macro is used to call section's Remove_... macro + ;from the uninstaller. + ;Input: section index constant name specified in Section command. + + !insertmacro "Remove_${${SecName}}" +!macroend + +; Determine whether the selection of SecName changed +!macro MaybeSelectionChanged SecName + !insertmacro LoadVar ${SecName}_selected + SectionGetFlags ${${SecName}} $R1 + IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits + + ; See if the status has changed: + IntCmp $R0 $R1 "${SecName}_unchanged" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected + + IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected" + !insertmacro "Deselect_required_by_${SecName}" + goto "${SecName}_unchanged" + + "${SecName}_was_selected:" + !insertmacro "Select_${SecName}_depends" + + "${SecName}_unchanged:" +!macroend +;--- End of Add/Remove macros --- + +;-------------------------------- +;Interface Settings + + !define MUI_HEADERIMAGE + !define MUI_ABORTWARNING + +;-------------------------------- +; path functions + +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 + +;---------------------------------------- +; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" +;---------------------------------------- +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 +;==================================================== +; get_NT_environment +; Returns: the selected environment +; Output : head of the stack +;==================================================== +!macro select_NT_profile UN +Function ${UN}select_NT_profile + StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single + DetailPrint "Selected environment for all users" + Push "all" + Return + environment_single: + DetailPrint "Selected environment for current user only." + Push "current" + Return +FunctionEnd +!macroend +!insertmacro select_NT_profile "" +!insertmacro select_NT_profile "un." +;---------------------------------------------------- +!define NT_current_env 'HKCU "Environment"' +!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + +!ifndef WriteEnvStr_RegKey + !ifdef ALL_USERS + !define WriteEnvStr_RegKey \ + 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + !else + !define WriteEnvStr_RegKey 'HKCU "Environment"' + !endif +!endif + +; AddToPath - Adds the given dir to the search path. +; Input - head of the stack +; Note - Win9x systems requires reboot + +Function AddToPath + Exch $0 + Push $1 + Push $2 + Push $3 + + # don't add if the path doesn't exist + IfFileExists "$0\*.*" "" AddToPath_done + + ReadEnvStr $1 PATH + ; if the path is too long for a NSIS variable NSIS will return a 0 + ; length string. If we find that, then warn and skip any path + ; modification as it will trash the existing path. + StrLen $2 $1 + IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done + CheckPathLength_ShowPathWarning: + Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" + Goto AddToPath_done + CheckPathLength_Done: + Push "$1;" + Push "$0;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$0\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + GetFullPathName /SHORT $3 $0 + Push "$1;" + Push "$3;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$3\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + + Call IsNT + Pop $1 + StrCmp $1 1 AddToPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" a + FileSeek $1 -1 END + FileReadByte $1 $2 + IntCmp $2 26 0 +2 +2 # DOS EOF + FileSeek $1 -1 END # write over EOF + FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" + FileClose $1 + SetRebootFlag true + Goto AddToPath_done + + AddToPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto DoTrim + ReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + DoTrim: + StrCmp $1 "" AddToPath_NTdoIt + Push $1 + Call Trim + Pop $1 + StrCpy $0 "$1;$0" + AddToPath_NTdoIt: + StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $0 + Goto DoSend + WriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $0 + DoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + AddToPath_done: + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; RemoveFromPath - Remove a given dir from the path +; Input: head of the stack + +Function un.RemoveFromPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + + IntFmt $6 "%c" 26 # DOS EOF + + Call un.IsNT + Pop $1 + StrCmp $1 1 unRemoveFromPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" r + GetTempFileName $4 + FileOpen $2 $4 w + GetFullPathName /SHORT $0 $0 + StrCpy $0 "SET PATH=%PATH%;$0" + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoop: + FileRead $1 $3 + StrCpy $5 $3 1 -1 # read last char + StrCmp $5 $6 0 +2 # if DOS EOF + StrCpy $3 $3 -1 # remove DOS EOF so we can compare + StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "" unRemoveFromPath_dosLoopEnd + FileWrite $2 $3 + Goto unRemoveFromPath_dosLoop + unRemoveFromPath_dosLoopRemoveLine: + SetRebootFlag true + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoopEnd: + FileClose $2 + FileClose $1 + StrCpy $1 $WINDIR 2 + Delete "$1\autoexec.bat" + CopyFiles /SILENT $4 "$1\autoexec.bat" + Delete $4 + Goto unRemoveFromPath_done + + unRemoveFromPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto unDoTrim + unReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + unDoTrim: + StrCpy $5 $1 1 -1 # copy last char + StrCmp $5 ";" +2 # if last char != ; + StrCpy $1 "$1;" # append ; + Push $1 + Push "$0;" + Call un.StrStr ; Find `$0;` in $1 + Pop $2 ; pos of our dir + StrCmp $2 "" unRemoveFromPath_done + ; else, it is in path + # $0 - path to add + # $1 - path var + StrLen $3 "$0;" + StrLen $4 $2 + StrCpy $5 $1 -$4 # $5 is now the part before the path to remove + StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove + StrCpy $3 $5$6 + + StrCpy $5 $3 1 -1 # copy last char + StrCmp $5 ";" 0 +2 # if last char == ; + StrCpy $3 $3 -1 # remove last char + + StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $3 + Goto unDoSend + unWriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $3 + unDoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + unRemoveFromPath_done: + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Uninstall sutff +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +########################################### +# Utility Functions # +########################################### + +;==================================================== +; IsNT - Returns 1 if the current system is NT, 0 +; otherwise. +; Output: head of the stack +;==================================================== +; IsNT +; no input +; output, top of the stack = 1 if NT or 0 if not +; +; Usage: +; Call IsNT +; Pop $R0 +; ($R0 at this point is 1 or 0) + +!macro IsNT un +Function ${un}IsNT + Push $0 + ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + StrCmp $0 "" 0 IsNT_yes + ; we are not NT. + Pop $0 + Push 0 + Return + + IsNT_yes: + ; NT!!! + Pop $0 + Push 1 +FunctionEnd +!macroend +!insertmacro IsNT "" +!insertmacro IsNT "un." + +; StrStr +; input, top of stack = string to search for +; top of stack-1 = string to search in +; output, top of stack (replaces with the portion of the string remaining) +; modifies no other variables. +; +; Usage: +; Push "this is a long ass string" +; Push "ass" +; Call StrStr +; Pop $R0 +; ($R0 at this point is "ass string") + +!macro StrStr un +Function ${un}StrStr +Exch $R1 ; st=haystack,old$R1, $R1=needle + Exch ; st=old$R1,haystack + Exch $R2 ; st=old$R1,old$R2, $R2=haystack + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=needle + ; $R2=haystack + ; $R3=len(needle) + ; $R4=cnt + ; $R5=tmp + loop: + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 done + StrCmp $R5 "" done + IntOp $R4 $R4 + 1 + Goto loop +done: + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 +FunctionEnd +!macroend +!insertmacro StrStr "" +!insertmacro StrStr "un." + +Function Trim ; Added by Pelaca + Exch $R1 + Push $R2 +Loop: + StrCpy $R2 "$R1" 1 -1 + StrCmp "$R2" " " RTrim + StrCmp "$R2" "$\n" RTrim + StrCmp "$R2" "$\r" RTrim + StrCmp "$R2" ";" RTrim + GoTo Done +RTrim: + StrCpy $R1 "$R1" -1 + Goto Loop +Done: + Pop $R2 + Exch $R1 +FunctionEnd + +Function ConditionalAddToRegisty + Pop $0 + Pop $1 + StrCmp "$0" "" ConditionalAddToRegisty_EmptyString + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)" \ + "$1" "$0" + ;MessageBox MB_OK "Set Registry: '$1' to '$0'" + DetailPrint "Set install registry entry: '$1' to '$0'" + ConditionalAddToRegisty_EmptyString: +FunctionEnd + +;-------------------------------- + +!ifdef CPACK_USES_DOWNLOAD +Function DownloadFile + IfFileExists $INSTDIR\* +2 + CreateDirectory $INSTDIR + Pop $0 + + ; Skip if already downloaded + IfFileExists $INSTDIR\$0 0 +2 + Return + + StrCpy $1 "@CPACK_DOWNLOAD_SITE@" + + try_again: + NSISdl::download "$1/$0" "$INSTDIR\$0" + + Pop $1 + StrCmp $1 "success" success + StrCmp $1 "Cancelled" cancel + MessageBox MB_OK "Download failed: $1" + cancel: + Return + success: +FunctionEnd +!endif + +;-------------------------------- +; Installation types +@CPACK_NSIS_INSTALLATION_TYPES@ + +;-------------------------------- +; Component sections +@CPACK_NSIS_COMPONENT_SECTIONS@ + +;-------------------------------- +; Define some macro setting for the gui +@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ +@CPACK_NSIS_INSTALLER_ICON_CODE@ +@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@ + +;-------------------------------- +;Pages + !insertmacro MUI_PAGE_WELCOME + + !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" + Page custom InstallOptionsPage + !insertmacro MUI_PAGE_DIRECTORY + + ;Start Menu Folder Page Configuration + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" + !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER + + !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\NEWS.txt" + + @CPACK_NSIS_PAGE_COMPONENTS@ + + !insertmacro MUI_PAGE_INSTFILES + !insertmacro MUI_PAGE_FINISH + + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + !insertmacro MUI_UNPAGE_FINISH + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" ;first language is the default language + !insertmacro MUI_LANGUAGE "Albanian" + !insertmacro MUI_LANGUAGE "Arabic" + !insertmacro MUI_LANGUAGE "Basque" + !insertmacro MUI_LANGUAGE "Belarusian" + !insertmacro MUI_LANGUAGE "Bosnian" + !insertmacro MUI_LANGUAGE "Breton" + !insertmacro MUI_LANGUAGE "Bulgarian" + !insertmacro MUI_LANGUAGE "Croatian" + !insertmacro MUI_LANGUAGE "Czech" + !insertmacro MUI_LANGUAGE "Danish" + !insertmacro MUI_LANGUAGE "Dutch" + !insertmacro MUI_LANGUAGE "Estonian" + !insertmacro MUI_LANGUAGE "Farsi" + !insertmacro MUI_LANGUAGE "Finnish" + !insertmacro MUI_LANGUAGE "French" + !insertmacro MUI_LANGUAGE "German" + !insertmacro MUI_LANGUAGE "Greek" + !insertmacro MUI_LANGUAGE "Hebrew" + !insertmacro MUI_LANGUAGE "Hungarian" + !insertmacro MUI_LANGUAGE "Icelandic" + !insertmacro MUI_LANGUAGE "Indonesian" + !insertmacro MUI_LANGUAGE "Irish" + !insertmacro MUI_LANGUAGE "Italian" + !insertmacro MUI_LANGUAGE "Japanese" + !insertmacro MUI_LANGUAGE "Korean" + !insertmacro MUI_LANGUAGE "Kurdish" + !insertmacro MUI_LANGUAGE "Latvian" + !insertmacro MUI_LANGUAGE "Lithuanian" + !insertmacro MUI_LANGUAGE "Luxembourgish" + !insertmacro MUI_LANGUAGE "Macedonian" + !insertmacro MUI_LANGUAGE "Malay" + !insertmacro MUI_LANGUAGE "Mongolian" + !insertmacro MUI_LANGUAGE "Norwegian" + !insertmacro MUI_LANGUAGE "Polish" + !insertmacro MUI_LANGUAGE "Portuguese" + !insertmacro MUI_LANGUAGE "PortugueseBR" + !insertmacro MUI_LANGUAGE "Romanian" + !insertmacro MUI_LANGUAGE "Russian" + !insertmacro MUI_LANGUAGE "Serbian" + !insertmacro MUI_LANGUAGE "SerbianLatin" + !insertmacro MUI_LANGUAGE "SimpChinese" + !insertmacro MUI_LANGUAGE "Slovak" + !insertmacro MUI_LANGUAGE "Slovenian" + !insertmacro MUI_LANGUAGE "Spanish" + !insertmacro MUI_LANGUAGE "Swedish" + !insertmacro MUI_LANGUAGE "Thai" + !insertmacro MUI_LANGUAGE "TradChinese" + !insertmacro MUI_LANGUAGE "Turkish" + !insertmacro MUI_LANGUAGE "Ukrainian" + !insertmacro MUI_LANGUAGE "Welsh" + + +;-------------------------------- +;Reserve Files + + ;These files should be inserted before other files in the data block + ;Keep these lines before any File command + ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) + + ReserveFile "NSIS.InstallOptions.ini" + !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS + +;-------------------------------- +;Installer Sections + +Section "-Core installation" + ;Use the entire tree produced by the INSTALL target. Keep the + ;list of directories here in sync with the RMDir commands below. + SetOutPath "$INSTDIR" + @CPACK_NSIS_FULL_INSTALL@ + + ;Store installation folder + WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR + + ;Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + Push "DisplayName" + Push "@CPACK_NSIS_DISPLAY_NAME@" + Call ConditionalAddToRegisty + Push "DisplayVersion" + Push "@CPACK_PACKAGE_VERSION@" + Call ConditionalAddToRegisty + Push "Publisher" + Push "@CPACK_PACKAGE_VENDOR@" + Call ConditionalAddToRegisty + Push "UninstallString" + Push "$INSTDIR\Uninstall.exe" + Call ConditionalAddToRegisty + Push "NoRepair" + Push "1" + Call ConditionalAddToRegisty + + !ifdef CPACK_NSIS_ADD_REMOVE + ;Create add/remove functionality + Push "ModifyPath" + Push "$INSTDIR\AddRemove.exe" + Call ConditionalAddToRegisty + !else + Push "NoModify" + Push "1" + Call ConditionalAddToRegisty + !endif + + ; Optional registration + Push "DisplayIcon" + Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@" + Call ConditionalAddToRegisty + Push "HelpLink" + Push "@CPACK_NSIS_HELP_LINK@" + Call ConditionalAddToRegisty + Push "URLInfoAbout" + Push "@CPACK_NSIS_URL_INFO_ABOUT@" + Call ConditionalAddToRegisty + Push "Contact" + Push "@CPACK_NSIS_CONTACT@" + Call ConditionalAddToRegisty + !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + + ;Create shortcuts + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" +@CPACK_NSIS_CREATE_ICONS@ +@CPACK_NSIS_CREATE_ICONS_EXTRA@ + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + + ;Read a value from an InstallOptions INI file + !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" + + ; Write special uninstall registry entries + Push "StartMenu" + Push "$STARTMENU_FOLDER" + Call ConditionalAddToRegisty + Push "DoNotAddToPath" + Push "$DO_NOT_ADD_TO_PATH" + Call ConditionalAddToRegisty + Push "AddToPathAllUsers" + Push "$ADD_TO_PATH_ALL_USERS" + Call ConditionalAddToRegisty + Push "AddToPathCurrentUser" + Push "$ADD_TO_PATH_CURRENT_USER" + Call ConditionalAddToRegisty + Push "InstallToDesktop" + Push "$INSTALL_DESKTOP" + Call ConditionalAddToRegisty + + !insertmacro MUI_STARTMENU_WRITE_END + +@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ + +SectionEnd + +Section "-Add to path" + Push $INSTDIR\bin + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath + StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 + Call AddToPath + doNotAddToPath: +SectionEnd + +;-------------------------------- +; Create custom pages +Function InstallOptionsPage + !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@" + !insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" + +FunctionEnd + +;-------------------------------- +; determine admin versus local install +Function un.onInit + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + Goto done + + noLM: + ;Get installation folder from registry if available + + done: + + ;Disable WoW64 redirection + SetRegView 64 + +FunctionEnd + +;--- Add/Remove callback functions: --- +!macro SectionList MacroName + ;This macro used to perform operation on multiple sections. + ;List all of your components in following manner here. +@CPACK_NSIS_COMPONENT_SECTION_LIST@ +!macroend + +Section -FinishComponents + ;Removes unselected components and writes component status to registry + !insertmacro SectionList "FinishSection" + +!ifdef CPACK_NSIS_ADD_REMOVE + ; Get the name of the installer executable + System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' + StrCpy $R3 $R0 + + ; Strip off the last 13 characters, to see if we have AddRemove.exe + StrLen $R1 $R0 + IntOp $R1 $R0 - 13 + StrCpy $R2 $R0 13 $R1 + StrCmp $R2 "AddRemove.exe" addremove_installed + + ; We're not running AddRemove.exe, so install it + CopyFiles $R3 $INSTDIR\AddRemove.exe + + addremove_installed: +!endif +SectionEnd +;--- End of Add/Remove callback functions --- + +;-------------------------------- +; Component dependencies +Function .onSelChange + !insertmacro SectionList MaybeSelectionChanged +FunctionEnd + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + ReadRegStr $START_MENU SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)" "StartMenu" + ;MessageBox MB_OK "Start menu is in: $START_MENU" + ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)" "DoNotAddToPath" + ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)" "AddToPathAllUsers" + ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)" "AddToPathCurrentUser" + ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" + ReadRegStr $INSTALL_DESKTOP SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)" "InstallToDesktop" + ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " + +@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ + + ;Remove files we installed. + ;Keep the list of directories here in sync with the File commands above. +@CPACK_NSIS_DELETE_FILES@ +@CPACK_NSIS_DELETE_DIRECTORIES@ + +!ifdef CPACK_NSIS_ADD_REMOVE + ;Remove the add/remove program + Delete "$INSTDIR\AddRemove.exe" +!endif + + ;Remove the uninstaller itself. + Delete "$INSTDIR\Uninstall.exe" + DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@ (x64)" + + ;Remove the installation directory if it is empty. + RMDir "$INSTDIR" + + ; Remove the registry entries. + DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + ; Removes all optional components + !insertmacro SectionList "RemoveSection" + + !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP + + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" +@CPACK_NSIS_DELETE_ICONS@ +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + startMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors startMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop + startMenuDeleteLoopDone: + + ; If the user changed the shortcut, then untinstall may not work. This should + ; try to fix it. + StrCpy $MUI_TEMP "$START_MENU" + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + secondStartMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors secondStartMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop + secondStartMenuDeleteLoopDone: + + DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + Push $INSTDIR\bin + StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 + Call un.RemoveFromPath + doNotRemoveFromPath: +SectionEnd + +;-------------------------------- +; determine admin versus local install +; Is install for "AllUsers" or "JustMe"? +; Default to "JustMe" - set to "AllUsers" if admin or on Win9x +; This function is used for the very first "custom page" of the installer. +; This custom page does not show up visibly, but it executes prior to the +; first visible page and sets up $INSTDIR properly... +; Choose different default installation folder based on SV_ALLUSERS... +; "Program Files" for AllUsers, "My Documents" for JustMe... + +Function .onInit + ; Reads components status for registry + !insertmacro SectionList "InitSection" + + ; check to see if /D has been used to change + ; the install directory by comparing it to the + ; install directory that is expected to be the + ; default + StrCpy $IS_DEFAULT_INSTALLDIR 0 + ;StrCmp "$INSTDIR" "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 + StrCmp "$INSTDIR" "$PROGRAMFILES64\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 + StrCpy $IS_DEFAULT_INSTALLDIR 1 + + StrCpy $SV_ALLUSERS "JustMe" + ; if default install dir then change the default + ; if it is installed for JustMe + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + ;Disable WoW64 redirection + SetRegView 64 + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + + noLM: + StrCpy $SV_ALLUSERS "AllUsers" + ;Get installation folder from registry if available + + done: + StrCmp $SV_ALLUSERS "AllUsers" 0 +3 + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + ;StrCpy $INSTDIR "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + StrCpy $INSTDIR "$PROGRAMFILES64\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage + !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" + + noOptionsPage: +FunctionEnd diff --git a/3rd/libzmq/builds/cmake/ZeroMQConfig.cmake.in b/3rd/libzmq/builds/cmake/ZeroMQConfig.cmake.in new file mode 100644 index 00000000..3f16e595 --- /dev/null +++ b/3rd/libzmq/builds/cmake/ZeroMQConfig.cmake.in @@ -0,0 +1,34 @@ +# ZeroMQ cmake module +# +# The following import targets are created +# +# :: +# +# libzmq-static +# libzmq +# +# This module sets the following variables in your project:: +# +# ZeroMQ_FOUND - true if ZeroMQ found on the system +# ZeroMQ_INCLUDE_DIR - the directory containing ZeroMQ headers +# ZeroMQ_LIBRARY - +# ZeroMQ_STATIC_LIBRARY + +@PACKAGE_INIT@ + +if(NOT TARGET libzmq AND NOT TARGET libzmq-static) + include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + + if (TARGET libzmq) + get_target_property(@PROJECT_NAME@_INCLUDE_DIR libzmq INTERFACE_INCLUDE_DIRECTORIES) + else () + get_target_property(@PROJECT_NAME@_INCLUDE_DIR libzmq-static INTERFACE_INCLUDE_DIRECTORIES) + endif() + + if (TARGET libzmq) + get_target_property(@PROJECT_NAME@_LIBRARY libzmq LOCATION) + endif() + if (TARGET libzmq-static) + get_target_property(@PROJECT_NAME@_STATIC_LIBRARY libzmq-static LOCATION) + endif() +endif() diff --git a/3rd/libzmq/builds/cmake/ci_build.sh b/3rd/libzmq/builds/cmake/ci_build.sh new file mode 100644 index 00000000..dea9756a --- /dev/null +++ b/3rd/libzmq/builds/cmake/ci_build.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +set -x -e + +cd ../.. + +# always install custom builds from dist +# to make sure that `make dist` doesn't omit any files required to build & test +if [ -z "$DO_CLANG_FORMAT_CHECK" -a -z "$CLANG_TIDY" ]; then + ./autogen.sh + ./configure + make -j5 dist-gzip + V=$(./version.sh) + tar -xzf zeromq-$V.tar.gz + cd zeromq-$V +fi + +mkdir tmp || true +BUILD_PREFIX=$PWD/tmp + +CONFIG_OPTS=() +CONFIG_OPTS+=("CFLAGS=-I${BUILD_PREFIX}/include") +CONFIG_OPTS+=("CPPFLAGS=-I${BUILD_PREFIX}/include") +CONFIG_OPTS+=("CXXFLAGS=-I${BUILD_PREFIX}/include") +CONFIG_OPTS+=("LDFLAGS=-L${BUILD_PREFIX}/lib") +CONFIG_OPTS+=("PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig") + +CMAKE_OPTS=() +CMAKE_OPTS+=("-DCMAKE_INSTALL_PREFIX:PATH=${BUILD_PREFIX}") +CMAKE_OPTS+=("-DCMAKE_PREFIX_PATH:PATH=${BUILD_PREFIX}") +CMAKE_OPTS+=("-DCMAKE_LIBRARY_PATH:PATH=${BUILD_PREFIX}/lib") +CMAKE_OPTS+=("-DCMAKE_INCLUDE_PATH:PATH=${BUILD_PREFIX}/include") +CMAKE_OPTS+=("-DENABLE_CAPSH=ON") + +if [ "$CLANG_FORMAT" != "" ] ; then + CMAKE_OPTS+=("-DCLANG_FORMAT=${CLANG_FORMAT}") +fi + +if [ -z $CURVE ]; then + CMAKE_OPTS+=("-DENABLE_CURVE=OFF") +elif [ $CURVE == "libsodium" ]; then + CMAKE_OPTS+=("-DWITH_LIBSODIUM=ON") + + if ! ((command -v dpkg-query >/dev/null 2>&1 && dpkg-query --list libsodium-dev >/dev/null 2>&1) || \ + (command -v brew >/dev/null 2>&1 && brew ls --versions libsodium >/dev/null 2>&1)); then + git clone --depth 1 -b stable git://github.com/jedisct1/libsodium.git + ( cd libsodium; ./autogen.sh; ./configure --prefix=$BUILD_PREFIX; make install) + fi +fi + +CMAKE_PREFIXES=() +MAKE_PREFIXES=() +PARALLEL_MAKE_OPT="-j5" +if [ -n "$CLANG_TIDY" ] ; then + CMAKE_OPTS+=("-DCMAKE_BUILD_TYPE=Debug") # do a debug build to avoid unused variable warnings with assertions, and to speed up build + CMAKE_OPTS+=("-DCMAKE_CXX_CLANG_TIDY:STRING=${CLANG_TIDY}") + if [ -n ${SONARCLOUD_BUILD_WRAPPER_PATH} ] ; then + MAKE_PREFIXES+=("${SONARCLOUD_BUILD_WRAPPER_PATH}build-wrapper-linux-x86-64") + MAKE_PREFIXES+=("--out-dir") + MAKE_PREFIXES+=("${TRAVIS_BUILD_DIR}/bw-output") + + fi + CMAKE_PREFIXES+=("scan-build-10") + MAKE_PREFIXES+=("scan-build-10") + MAKE_PREFIXES+=("-plist-html") + SCAN_BUILD_OUTPUT="$(pwd)/scan-build-report" + MAKE_PREFIXES+=("-o ${SCAN_BUILD_OUTPUT}") + # TODO this does not work with sonarcloud.io as it misses the sonar-cxx plugin + #MAKE_PREFIXES+=("-plist") + IFS="/" read -ra GITHUB_USER <<< "${TRAVIS_REPO_SLUG}" + PARALLEL_MAKE_OPT="" +fi + +# Build, check, and install from local source +mkdir build_cmake +cd build_cmake +if [ "$DO_CLANG_FORMAT_CHECK" = "1" ] ; then + if ! ( PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig cmake "${CMAKE_OPTS[@]}" .. && make clang-format-check) ; then + make clang-format-diff + exit 1 + fi +else + if [ -n "$CLANG_TIDY" ] ; then + ${CLANG_TIDY} -explain-config + fi + + export CTEST_OUTPUT_ON_FAILURE=1 + PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig ${CMAKE_PREFIXES[@]} cmake "${CMAKE_OPTS[@]}" .. + ${MAKE_PREFIXES[@]} make ${PARALLEL_MAKE_OPT} all VERBOSE=1 | tee clang-tidy-report + + if [ -n "${SONAR_SCANNER_CLI_PATH}" ] ; then + find ${SCAN_BUILD_OUTPUT} || echo "WARNING: ${SCAN_BUILD_OUTPUT} does not exist" + + ${SONAR_SCANNER_CLI_PATH}sonar-scanner \ + -Dsonar.projectKey=${GITHUB_USER}-libzmq \ + -Dsonar.organization=${GITHUB_USER}-github \ + -Dsonar.projectBaseDir=.. \ + -Dsonar.sources=${TRAVIS_BUILD_DIR}/include,${TRAVIS_BUILD_DIR}/src,${TRAVIS_BUILD_DIR}/tests,${TRAVIS_BUILD_DIR}/unittests \ + -Dsonar.cfamily.build-wrapper-output=${TRAVIS_BUILD_DIR}/bw-output \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.login=${SONARQUBE_TOKEN} + + # TODO this does not work with sonarcloud.io as it misses the sonar-cxx plugin + # -Dsonar.cxx.clangtidy.reportPath=clang-tidy-report \ + # -Dsonar.cxx.clangsa.reportPath=*.plist \ + + fi + + make install + make ${PARALLEL_MAKE_OPT} test ARGS="-V" +fi diff --git a/3rd/libzmq/builds/cmake/clang-format-check.sh.in b/3rd/libzmq/builds/cmake/clang-format-check.sh.in new file mode 100644 index 00000000..7ff40196 --- /dev/null +++ b/3rd/libzmq/builds/cmake/clang-format-check.sh.in @@ -0,0 +1,14 @@ +#!/bin/sh +FAILED=0 +IFS=";" +FILES="@ALL_SOURCE_FILES@" +IDS=$(echo -en "\n\b") +for FILE in $FILES +do + @CLANG_FORMAT@ -style=file -output-replacements-xml "$FILE" | grep "/dev/null && + { + echo "$FILE is not correctly formatted" + FAILED=1 + } +done +if [ "$FAILED" -eq "1" ] ; then exit 1 ; fi diff --git a/3rd/libzmq/builds/cmake/platform.hpp.in b/3rd/libzmq/builds/cmake/platform.hpp.in new file mode 100644 index 00000000..f7119df1 --- /dev/null +++ b/3rd/libzmq/builds/cmake/platform.hpp.in @@ -0,0 +1,141 @@ +#ifndef __ZMQ_PLATFORM_HPP_INCLUDED__ +#define __ZMQ_PLATFORM_HPP_INCLUDED__ + +#cmakedefine ZMQ_USE_CV_IMPL_STL11 +#cmakedefine ZMQ_USE_CV_IMPL_WIN32API +#cmakedefine ZMQ_USE_CV_IMPL_PTHREADS +#cmakedefine ZMQ_USE_CV_IMPL_NONE + +#cmakedefine ZMQ_IOTHREAD_POLLER_USE_KQUEUE +#cmakedefine ZMQ_IOTHREAD_POLLER_USE_EPOLL +#cmakedefine ZMQ_IOTHREAD_POLLER_USE_EPOLL_CLOEXEC +#cmakedefine ZMQ_IOTHREAD_POLLER_USE_DEVPOLL +#cmakedefine ZMQ_IOTHREAD_POLLER_USE_POLL +#cmakedefine ZMQ_IOTHREAD_POLLER_USE_SELECT + +#cmakedefine ZMQ_POLL_BASED_ON_SELECT +#cmakedefine ZMQ_POLL_BASED_ON_POLL + +#cmakedefine ZMQ_CACHELINE_SIZE @ZMQ_CACHELINE_SIZE@ + +#cmakedefine ZMQ_FORCE_MUTEXES + +#cmakedefine HAVE_FORK +#cmakedefine HAVE_CLOCK_GETTIME +#cmakedefine HAVE_GETHRTIME +#cmakedefine HAVE_MKDTEMP +#cmakedefine ZMQ_HAVE_UIO + +#cmakedefine ZMQ_HAVE_NOEXCEPT + +#cmakedefine ZMQ_HAVE_EVENTFD +#cmakedefine ZMQ_HAVE_EVENTFD_CLOEXEC +#cmakedefine ZMQ_HAVE_IFADDRS +#cmakedefine ZMQ_HAVE_SO_BINDTODEVICE + +#cmakedefine ZMQ_HAVE_SO_PEERCRED +#cmakedefine ZMQ_HAVE_LOCAL_PEERCRED + +#cmakedefine ZMQ_HAVE_O_CLOEXEC + +#cmakedefine ZMQ_HAVE_SOCK_CLOEXEC +#cmakedefine ZMQ_HAVE_SO_KEEPALIVE +#cmakedefine ZMQ_HAVE_SO_PRIORITY +#cmakedefine ZMQ_HAVE_TCP_KEEPCNT +#cmakedefine ZMQ_HAVE_TCP_KEEPIDLE +#cmakedefine ZMQ_HAVE_TCP_KEEPINTVL +#cmakedefine ZMQ_HAVE_TCP_KEEPALIVE +#cmakedefine ZMQ_HAVE_PTHREAD_SETNAME_1 +#cmakedefine ZMQ_HAVE_PTHREAD_SETNAME_2 +#cmakedefine ZMQ_HAVE_PTHREAD_SETNAME_3 +#cmakedefine ZMQ_HAVE_PTHREAD_SET_NAME +#cmakedefine ZMQ_HAVE_PTHREAD_SET_AFFINITY +#cmakedefine HAVE_ACCEPT4 +#cmakedefine HAVE_STRNLEN +#cmakedefine ZMQ_HAVE_STRLCPY +#cmakedefine ZMQ_HAVE_LIBBSD + +#cmakedefine ZMQ_HAVE_IPC + +#cmakedefine ZMQ_USE_BUILTIN_SHA1 +#cmakedefine ZMQ_USE_NSS +#cmakedefine ZMQ_HAVE_WS +#cmakedefine ZMQ_HAVE_WSS +#cmakedefine ZMQ_HAVE_TIPC + +#cmakedefine ZMQ_HAVE_OPENPGM +#cmakedefine ZMQ_HAVE_NORM +#cmakedefine ZMQ_MAKE_VALGRIND_HAPPY + +#cmakedefine ZMQ_BUILD_DRAFT_API +#cmakedefine ZMQ_HAVE_CURVE +#cmakedefine ZMQ_USE_TWEETNACL +#cmakedefine ZMQ_USE_LIBSODIUM +#cmakedefine SODIUM_STATIC +#cmakedefine ZMQ_USE_GNUTLS +#cmakedefine ZMQ_USE_RADIX_TREE +#cmakedefine HAVE_IF_NAMETOINDEX + +#ifdef _AIX + #define ZMQ_HAVE_AIX +#endif + +#if defined __ANDROID__ + #define ZMQ_HAVE_ANDROID +#endif + +#if defined __CYGWIN__ + #define ZMQ_HAVE_CYGWIN +#endif + +#if defined __MINGW32__ + #define ZMQ_HAVE_MINGW32 +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + #define ZMQ_HAVE_FREEBSD +#endif + +#if defined(__DragonFly__) + #define ZMQ_HAVE_FREEBSD + #define ZMQ_HAVE_DRAGONFLY +#endif + +#if defined __hpux + #define ZMQ_HAVE_HPUX +#endif + +#if defined __linux__ + #define ZMQ_HAVE_LINUX +#endif + +#if defined __NetBSD__ + #define ZMQ_HAVE_NETBSD +#endif + +#if defined __OpenBSD__ + #define ZMQ_HAVE_OPENBSD +#endif + +// TODO better move OS-specific defines to the automake files, and check for availability of IPC with an explicit test there +#if defined __VMS + #define ZMQ_HAVE_OPENVMS + #undef ZMQ_HAVE_IPC +#endif + +#if defined __APPLE__ + #define ZMQ_HAVE_OSX +#endif + +#if defined __QNXNTO__ + #define ZMQ_HAVE_QNXNTO +#endif + +#if defined(sun) || defined(__sun) + #define ZMQ_HAVE_SOLARIS +#endif + +#cmakedefine ZMQ_HAVE_WINDOWS +#cmakedefine ZMQ_HAVE_WINDOWS_UWP + +#endif diff --git a/3rd/libzmq/external/sha1/license.txt b/3rd/libzmq/external/sha1/license.txt new file mode 100644 index 00000000..3b5eb95a --- /dev/null +++ b/3rd/libzmq/external/sha1/license.txt @@ -0,0 +1,26 @@ +Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/3rd/libzmq/external/sha1/sha1.c b/3rd/libzmq/external/sha1/sha1.c new file mode 100644 index 00000000..2702c89a --- /dev/null +++ b/3rd/libzmq/external/sha1/sha1.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) + * based on: http://www.itl.nist.gov/fipspubs/fip180-1.htm + * implemented by Jun-ichiro itojun Itoh + */ + +#include "sha1.h" +#include + + +/* constant table */ +static uint32_t _K[] = {0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6}; + +#define K(t) _K[(t) / 20] + +#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d))) +#define F1(b, c, d) (((b) ^ (c)) ^ (d)) +#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) +#define F3(b, c, d) (((b) ^ (c)) ^ (d)) + +#define S(n, x) (((x) << (n)) | ((x) >> (32 - (n)))) + +#define H(n) (ctxt->h.b32[(n)]) +#define COUNT (ctxt->count) +#define BCOUNT (ctxt->c.b64[0] / 8) +#define W(n) (ctxt->m.b32[(n)]) + +#define PUTBYTE(x) \ +do { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + ctxt->c.b64[0] += 8; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ +} while (0) + +#define PUTPAD(x) \ +do { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ +} while (0) + +static void sha1_step(struct sha1_ctxt *); + +static void +sha1_step(struct sha1_ctxt * ctxt) +{ + uint32_t a, + b, + c, + d, + e; + size_t t, + s; + uint32_t tmp; + +#ifndef WORDS_BIGENDIAN + struct sha1_ctxt tctxt; + + memmove(&tctxt.m.b8[0], &ctxt->m.b8[0], 64); + ctxt->m.b8[0] = tctxt.m.b8[3]; + ctxt->m.b8[1] = tctxt.m.b8[2]; + ctxt->m.b8[2] = tctxt.m.b8[1]; + ctxt->m.b8[3] = tctxt.m.b8[0]; + ctxt->m.b8[4] = tctxt.m.b8[7]; + ctxt->m.b8[5] = tctxt.m.b8[6]; + ctxt->m.b8[6] = tctxt.m.b8[5]; + ctxt->m.b8[7] = tctxt.m.b8[4]; + ctxt->m.b8[8] = tctxt.m.b8[11]; + ctxt->m.b8[9] = tctxt.m.b8[10]; + ctxt->m.b8[10] = tctxt.m.b8[9]; + ctxt->m.b8[11] = tctxt.m.b8[8]; + ctxt->m.b8[12] = tctxt.m.b8[15]; + ctxt->m.b8[13] = tctxt.m.b8[14]; + ctxt->m.b8[14] = tctxt.m.b8[13]; + ctxt->m.b8[15] = tctxt.m.b8[12]; + ctxt->m.b8[16] = tctxt.m.b8[19]; + ctxt->m.b8[17] = tctxt.m.b8[18]; + ctxt->m.b8[18] = tctxt.m.b8[17]; + ctxt->m.b8[19] = tctxt.m.b8[16]; + ctxt->m.b8[20] = tctxt.m.b8[23]; + ctxt->m.b8[21] = tctxt.m.b8[22]; + ctxt->m.b8[22] = tctxt.m.b8[21]; + ctxt->m.b8[23] = tctxt.m.b8[20]; + ctxt->m.b8[24] = tctxt.m.b8[27]; + ctxt->m.b8[25] = tctxt.m.b8[26]; + ctxt->m.b8[26] = tctxt.m.b8[25]; + ctxt->m.b8[27] = tctxt.m.b8[24]; + ctxt->m.b8[28] = tctxt.m.b8[31]; + ctxt->m.b8[29] = tctxt.m.b8[30]; + ctxt->m.b8[30] = tctxt.m.b8[29]; + ctxt->m.b8[31] = tctxt.m.b8[28]; + ctxt->m.b8[32] = tctxt.m.b8[35]; + ctxt->m.b8[33] = tctxt.m.b8[34]; + ctxt->m.b8[34] = tctxt.m.b8[33]; + ctxt->m.b8[35] = tctxt.m.b8[32]; + ctxt->m.b8[36] = tctxt.m.b8[39]; + ctxt->m.b8[37] = tctxt.m.b8[38]; + ctxt->m.b8[38] = tctxt.m.b8[37]; + ctxt->m.b8[39] = tctxt.m.b8[36]; + ctxt->m.b8[40] = tctxt.m.b8[43]; + ctxt->m.b8[41] = tctxt.m.b8[42]; + ctxt->m.b8[42] = tctxt.m.b8[41]; + ctxt->m.b8[43] = tctxt.m.b8[40]; + ctxt->m.b8[44] = tctxt.m.b8[47]; + ctxt->m.b8[45] = tctxt.m.b8[46]; + ctxt->m.b8[46] = tctxt.m.b8[45]; + ctxt->m.b8[47] = tctxt.m.b8[44]; + ctxt->m.b8[48] = tctxt.m.b8[51]; + ctxt->m.b8[49] = tctxt.m.b8[50]; + ctxt->m.b8[50] = tctxt.m.b8[49]; + ctxt->m.b8[51] = tctxt.m.b8[48]; + ctxt->m.b8[52] = tctxt.m.b8[55]; + ctxt->m.b8[53] = tctxt.m.b8[54]; + ctxt->m.b8[54] = tctxt.m.b8[53]; + ctxt->m.b8[55] = tctxt.m.b8[52]; + ctxt->m.b8[56] = tctxt.m.b8[59]; + ctxt->m.b8[57] = tctxt.m.b8[58]; + ctxt->m.b8[58] = tctxt.m.b8[57]; + ctxt->m.b8[59] = tctxt.m.b8[56]; + ctxt->m.b8[60] = tctxt.m.b8[63]; + ctxt->m.b8[61] = tctxt.m.b8[62]; + ctxt->m.b8[62] = tctxt.m.b8[61]; + ctxt->m.b8[63] = tctxt.m.b8[60]; +#endif + + a = H(0); + b = H(1); + c = H(2); + d = H(3); + e = H(4); + + for (t = 0; t < 20; t++) + { + s = t & 0x0f; + if (t >= 16) + W(s) = S(1, W((s + 13) & 0x0f) ^ W((s + 8) & 0x0f) ^ W((s + 2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t); + e = d; + d = c; + c = S(30, b); + b = a; + a = tmp; + } + for (t = 20; t < 40; t++) + { + s = t & 0x0f; + W(s) = S(1, W((s + 13) & 0x0f) ^ W((s + 8) & 0x0f) ^ W((s + 2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t); + e = d; + d = c; + c = S(30, b); + b = a; + a = tmp; + } + for (t = 40; t < 60; t++) + { + s = t & 0x0f; + W(s) = S(1, W((s + 13) & 0x0f) ^ W((s + 8) & 0x0f) ^ W((s + 2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t); + e = d; + d = c; + c = S(30, b); + b = a; + a = tmp; + } + for (t = 60; t < 80; t++) + { + s = t & 0x0f; + W(s) = S(1, W((s + 13) & 0x0f) ^ W((s + 8) & 0x0f) ^ W((s + 2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t); + e = d; + d = c; + c = S(30, b); + b = a; + a = tmp; + } + + H(0) = H(0) + a; + H(1) = H(1) + b; + H(2) = H(2) + c; + H(3) = H(3) + d; + H(4) = H(4) + e; + + memset(&ctxt->m.b8[0], 0, 64); +} + +/*------------------------------------------------------------*/ + +void +sha1_init(struct sha1_ctxt * ctxt) +{ + memset(ctxt, 0, sizeof(struct sha1_ctxt)); + H(0) = 0x67452301; + H(1) = 0xefcdab89; + H(2) = 0x98badcfe; + H(3) = 0x10325476; + H(4) = 0xc3d2e1f0; +} + +void +sha1_pad(struct sha1_ctxt * ctxt) +{ + size_t padlen; /* pad length in bytes */ + size_t padstart; + + PUTPAD(0x80); + + padstart = COUNT % 64; + padlen = 64 - padstart; + if (padlen < 8) + { + memset(&ctxt->m.b8[padstart], 0, padlen); + COUNT += (uint8_t) padlen; + COUNT %= 64; + sha1_step(ctxt); + padstart = COUNT % 64; /* should be 0 */ + padlen = 64 - padstart; /* should be 64 */ + } + memset(&ctxt->m.b8[padstart], 0, padlen - 8); + COUNT += ((uint8_t) padlen - 8); + COUNT %= 64; +#ifdef WORDS_BIGENDIAN + PUTPAD(ctxt->c.b8[0]); + PUTPAD(ctxt->c.b8[1]); + PUTPAD(ctxt->c.b8[2]); + PUTPAD(ctxt->c.b8[3]); + PUTPAD(ctxt->c.b8[4]); + PUTPAD(ctxt->c.b8[5]); + PUTPAD(ctxt->c.b8[6]); + PUTPAD(ctxt->c.b8[7]); +#else + PUTPAD(ctxt->c.b8[7]); + PUTPAD(ctxt->c.b8[6]); + PUTPAD(ctxt->c.b8[5]); + PUTPAD(ctxt->c.b8[4]); + PUTPAD(ctxt->c.b8[3]); + PUTPAD(ctxt->c.b8[2]); + PUTPAD(ctxt->c.b8[1]); + PUTPAD(ctxt->c.b8[0]); +#endif +} + +void +sha1_loop(struct sha1_ctxt * ctxt, const uint8_t *input0, size_t len) +{ + const uint8_t *input; + size_t gaplen; + size_t gapstart; + size_t off; + size_t copysiz; + + input = (const uint8_t *) input0; + off = 0; + + while (off < len) + { + gapstart = COUNT % 64; + gaplen = 64 - gapstart; + + copysiz = (gaplen < len - off) ? gaplen : len - off; + memmove(&ctxt->m.b8[gapstart], &input[off], copysiz); + COUNT += (uint8_t) copysiz; + COUNT %= 64; + ctxt->c.b64[0] += copysiz * 8; + if (COUNT % 64 == 0) + sha1_step(ctxt); + off += copysiz; + } +} + +void +sha1_result(struct sha1_ctxt * ctxt, uint8_t *digest0) +{ + uint8_t *digest; + + digest = (uint8_t *) digest0; + sha1_pad(ctxt); +#ifdef WORDS_BIGENDIAN + memmove(digest, &ctxt->h.b8[0], 20); +#else + digest[0] = ctxt->h.b8[3]; + digest[1] = ctxt->h.b8[2]; + digest[2] = ctxt->h.b8[1]; + digest[3] = ctxt->h.b8[0]; + digest[4] = ctxt->h.b8[7]; + digest[5] = ctxt->h.b8[6]; + digest[6] = ctxt->h.b8[5]; + digest[7] = ctxt->h.b8[4]; + digest[8] = ctxt->h.b8[11]; + digest[9] = ctxt->h.b8[10]; + digest[10] = ctxt->h.b8[9]; + digest[11] = ctxt->h.b8[8]; + digest[12] = ctxt->h.b8[15]; + digest[13] = ctxt->h.b8[14]; + digest[14] = ctxt->h.b8[13]; + digest[15] = ctxt->h.b8[12]; + digest[16] = ctxt->h.b8[19]; + digest[17] = ctxt->h.b8[18]; + digest[18] = ctxt->h.b8[17]; + digest[19] = ctxt->h.b8[16]; +#endif +} \ No newline at end of file diff --git a/3rd/libzmq/external/sha1/sha1.h b/3rd/libzmq/external/sha1/sha1.h new file mode 100644 index 00000000..7354d133 --- /dev/null +++ b/3rd/libzmq/external/sha1/sha1.h @@ -0,0 +1,87 @@ +/* contrib/pgcrypto/sha1.h */ +/* $KAME: sha1.h,v 1.4 2000/02/22 14:01:18 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * based on: http://www.itl.nist.gov/fipspubs/fip180-1.htm + * implemented by Jun-ichiro itojun Itoh + */ + +#ifndef _NETINET6_SHA1_H_ +#define _NETINET6_SHA1_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "../../src/stdint.hpp" + +struct sha1_ctxt +{ + union + { + uint8_t b8[20]; + uint32_t b32[5]; + } h; + union + { + uint8_t b8[8]; + uint64_t b64[1]; + } c; + union + { + uint8_t b8[64]; + uint32_t b32[16]; + } m; + uint8_t count; +}; + +void sha1_init(struct sha1_ctxt *); +void sha1_pad(struct sha1_ctxt *); +void sha1_loop(struct sha1_ctxt *, const uint8_t *, size_t); +void sha1_result(struct sha1_ctxt *, uint8_t *); + +// Compatibility with OpenSSL API +#define SHA_DIGEST_LENGTH 20 +typedef struct sha1_ctxt SHA_CTX; + +#define SHA1_Init(x) sha1_init((x)) +#define SHA1_Update(x, y, z) sha1_loop((x), (y), (z)) +#define SHA1_Final(x, y) sha1_result((y), (x)) + +#define SHA1_RESULTLEN (160/8) + +#ifdef __cplusplus +} +#endif + +#endif /* _NETINET6_SHA1_H_ */ diff --git a/3rd/libzmq/external/unity/license.txt b/3rd/libzmq/external/unity/license.txt new file mode 100644 index 00000000..d66fba53 --- /dev/null +++ b/3rd/libzmq/external/unity/license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/3rd/libzmq/external/unity/unity.c b/3rd/libzmq/external/unity/unity.c new file mode 100644 index 00000000..03dcc02a --- /dev/null +++ b/3rd/libzmq/external/unity/unity.c @@ -0,0 +1,1570 @@ +/* ========================================================================= + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +============================================================================ */ + +#define UNITY_INCLUDE_SETUP_STUBS +#include "unity.h" +#include + +/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +void UNITY_OUTPUT_CHAR(int); +#endif + +/* Helpful macros for us to use here in Assert functions */ +#define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; TEST_ABORT(); } +#define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; TEST_ABORT(); } +#define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) return + +struct UNITY_STORAGE_T Unity; + +#ifdef UNITY_OUTPUT_COLOR +static const char UnityStrOk[] = "\033[42mOK\033[00m"; +static const char UnityStrPass[] = "\033[42mPASS\033[00m"; +static const char UnityStrFail[] = "\033[41mFAIL\033[00m"; +static const char UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; +#else +static const char UnityStrOk[] = "OK"; +static const char UnityStrPass[] = "PASS"; +static const char UnityStrFail[] = "FAIL"; +static const char UnityStrIgnore[] = "IGNORE"; +#endif +static const char UnityStrNull[] = "NULL"; +static const char UnityStrSpacer[] = ". "; +static const char UnityStrExpected[] = " Expected "; +static const char UnityStrWas[] = " Was "; +static const char UnityStrGt[] = " to be greater than "; +static const char UnityStrLt[] = " to be less than "; +static const char UnityStrOrEqual[] = "or equal to "; +static const char UnityStrElement[] = " Element "; +static const char UnityStrByte[] = " Byte "; +static const char UnityStrMemory[] = " Memory Mismatch."; +static const char UnityStrDelta[] = " Values Not Within Delta "; +static const char UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; +static const char UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; +static const char UnityStrNullPointerForActual[] = " Actual pointer was NULL"; +#ifndef UNITY_EXCLUDE_FLOAT +static const char UnityStrNot[] = "Not "; +static const char UnityStrInf[] = "Infinity"; +static const char UnityStrNegInf[] = "Negative Infinity"; +static const char UnityStrNaN[] = "NaN"; +static const char UnityStrDet[] = "Determinate"; +static const char UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; +#endif +const char UnityStrErrFloat[] = "Unity Floating Point Disabled"; +const char UnityStrErrDouble[] = "Unity Double Precision Disabled"; +const char UnityStrErr64[] = "Unity 64-bit Support Disabled"; +static const char UnityStrBreaker[] = "-----------------------"; +static const char UnityStrResultsTests[] = " Tests "; +static const char UnityStrResultsFailures[] = " Failures "; +static const char UnityStrResultsIgnored[] = " Ignored "; +static const char UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; +static const char UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; + +/*----------------------------------------------- + * Pretty Printers & Test Result Output Handlers + *-----------------------------------------------*/ + +void UnityPrint(const char* string) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if (*pch == 27 && *(pch + 1) == '[') + { + while (*pch && *pch != 'm') + { + UNITY_OUTPUT_CHAR(*pch); + pch++; + } + UNITY_OUTPUT_CHAR('m'); + } +#endif + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +void UnityPrintLen(const char* string, const UNITY_UINT32 length) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch && (UNITY_UINT32)(pch - string) < length) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + UnityPrintNumber(number); + } + else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) + { + UnityPrintNumberUnsigned((UNITY_UINT)number); + } + else + { + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumber(const UNITY_INT number_to_print) +{ + UNITY_UINT number = (UNITY_UINT)number_to_print; + + if (number_to_print < 0) + { + /* A negative number, including MIN negative */ + UNITY_OUTPUT_CHAR('-'); + number = (UNITY_UINT)(-number_to_print); + } + UnityPrintNumberUnsigned(number); +} + +/*----------------------------------------------- + * basically do an itoa using as little ram as possible */ +void UnityPrintNumberUnsigned(const UNITY_UINT number) +{ + UNITY_UINT divisor = 1; + + /* figure out initial divisor */ + while (number / divisor > 9) + { + divisor *= 10; + } + + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); + divisor /= 10; + } while (divisor > 0); +} + +/*-----------------------------------------------*/ +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) +{ + int nibble; + char nibbles = nibbles_to_print; + if ((unsigned)nibbles > (2 * sizeof(number))) + nibbles = 2 * sizeof(number); + + while (nibbles > 0) + { + nibbles--; + nibble = (int)(number >> (nibbles * 4)) & 0x0F; + if (nibble <= 9) + { + UNITY_OUTPUT_CHAR((char)('0' + nibble)); + } + else + { + UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) +{ + UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); + UNITY_INT32 i; + + for (i = 0; i < UNITY_INT_WIDTH; i++) + { + if (current_bit & mask) + { + if (current_bit & number) + { + UNITY_OUTPUT_CHAR('1'); + } + else + { + UNITY_OUTPUT_CHAR('0'); + } + } + else + { + UNITY_OUTPUT_CHAR('X'); + } + current_bit = current_bit >> 1; + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/* This function prints a floating-point value in a format similar to + * printf("%.6g"). It can work with either single- or double-precision, + * but for simplicity, it prints only 6 significant digits in either case. + * Printing more than 6 digits accurately is hard (at least in the single- + * precision case) and isn't attempted here. */ +void UnityPrintFloat(const UNITY_DOUBLE input_number) +{ + UNITY_DOUBLE number = input_number; + + /* print minus sign (including for negative zero) */ + if (number < 0.0f || (number == 0.0f && 1.0f / number < 0.0f)) + { + UNITY_OUTPUT_CHAR('-'); + number = -number; + } + + /* handle zero, NaN, and +/- infinity */ + if (number == 0.0f) UnityPrint("0"); + else if (isnan(number)) UnityPrint("nan"); + else if (isinf(number)) UnityPrint("inf"); + else + { + int exponent = 0; + int decimals, digits; + UNITY_INT32 n; + char buf[16]; + + /* scale up or down by powers of 10 */ + while (number < 100000.0f / 1e6f) { number *= 1e6f; exponent -= 6; } + while (number < 100000.0f) { number *= 10.0f; exponent--; } + while (number > 1000000.0f * 1e6f) { number /= 1e6f; exponent += 6; } + while (number > 1000000.0f) { number /= 10.0f; exponent++; } + + /* round to nearest integer */ + n = ((UNITY_INT32)(number + number) + 1) / 2; + if (n > 999999) + { + n = 100000; + exponent++; + } + + /* determine where to place decimal point */ + decimals = (exponent <= 0 && exponent >= -9) ? -exponent : 5; + exponent += decimals; + + /* truncate trailing zeroes after decimal point */ + while (decimals > 0 && n % 10 == 0) + { + n /= 10; + decimals--; + } + + /* build up buffer in reverse order */ + digits = 0; + while (n != 0 || digits < decimals + 1) + { + buf[digits++] = (char)('0' + n % 10); + n /= 10; + } + while (digits > 0) + { + if(digits == decimals) UNITY_OUTPUT_CHAR('.'); + UNITY_OUTPUT_CHAR(buf[--digits]); + } + + /* print exponent if needed */ + if (exponent != 0) + { + UNITY_OUTPUT_CHAR('e'); + + if(exponent < 0) + { + UNITY_OUTPUT_CHAR('-'); + exponent = -exponent; + } + else + { + UNITY_OUTPUT_CHAR('+'); + } + + digits = 0; + while (exponent != 0 || digits < 2) + { + buf[digits++] = (char)('0' + exponent % 10); + exponent /= 10; + } + while (digits > 0) + { + UNITY_OUTPUT_CHAR(buf[--digits]); + } + } + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) +{ + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(':'); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +void UnityConcludeTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + } + else if (!Unity.CurrentTestFailed) + { + UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); + UnityPrint(UnityStrPass); + } + else + { + Unity.TestFailures++; + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); +} + +/*-----------------------------------------------*/ +static void UnityAddMsgIfSpecified(const char* msg) +{ + if (msg) + { + UnityPrint(UnityStrSpacer); +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + UnityPrint(msg); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(expected); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(actual); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStringsLen(const char* expected, + const char* actual, + const UNITY_UINT32 length) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(expected, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(actual, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*----------------------------------------------- + * Assertion & Control Helpers + *-----------------------------------------------*/ + +static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_LINE_TYPE lineNumber, + const char* msg) +{ + if (expected == actual) return 0; /* Both are NULL or same pointer */ + + /* print and return true if just expected is NULL */ + if (expected == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForExpected); + UnityAddMsgIfSpecified(msg); + return 1; + } + + /* print and return true if just actual is NULL */ + if (actual == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForActual); + UnityAddMsgIfSpecified(msg); + return 1; + } + + return 0; /* return false if neither is NULL */ +} + +/*----------------------------------------------- + * Assertion Functions + *-----------------------------------------------*/ + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((mask & expected) != (mask & actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); + UnityPrint(UnityStrWas); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if (threshold == actual && compare & UNITY_EQUAL_TO) return; + if (threshold == actual) failed = 1; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > threshold && compare & UNITY_SMALLER_THAN) failed = 1; + if (actual < threshold && compare & UNITY_GREATER_THAN) failed = 1; + } + else /* UINT or HEX */ + { + if ((UNITY_UINT)actual > (UNITY_UINT)threshold && compare & UNITY_SMALLER_THAN) failed = 1; + if ((UNITY_UINT)actual < (UNITY_UINT)threshold && compare & UNITY_GREATER_THAN) failed = 1; + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) UnityPrint(UnityStrGt); + if (compare & UNITY_SMALLER_THAN) UnityPrint(UnityStrLt); + if (compare & UNITY_EQUAL_TO) UnityPrint(UnityStrOrEqual); + UnityPrintNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#define UnityPrintPointlessAndBail() \ +{ \ + UnityTestResultsFailBegin(lineNumber); \ + UnityPrint(UnityStrPointless); \ + UnityAddMsgIfSpecified(msg); \ + UNITY_FAIL_AND_BAIL; } + +/*-----------------------------------------------*/ +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while ((elements > 0) && elements--) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + break; + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + break; +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + break; +#endif + default: /* length 4 bytes */ + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; + length = 4; + break; + } + + if (expect_val != actual_val) + { + if (style & UNITY_DISPLAY_RANGE_UINT && length < sizeof(expect_val)) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)(length + (const char*)expected); + } + actual = (UNITY_INTERNAL_PTR)(length + (const char*)actual); + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT +/* Wrap this define in a function with variable types as float or double */ +#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ + if (isinf(expected) && isinf(actual) && ((expected < 0) == (actual < 0))) return 1; \ + if (UNITY_NAN_CHECK) return 1; \ + diff = actual - expected; \ + if (diff < 0) diff = -diff; \ + if (delta < 0) delta = -delta; \ + return !(isnan(diff) || isinf(diff) || (diff > delta)) + /* This first part of this condition will catch any NaN or Infinite values */ +#ifndef UNITY_NAN_NOT_EQUAL_NAN + #define UNITY_NAN_CHECK isnan(expected) && isnan(actual) +#else + #define UNITY_NAN_CHECK 0 +#endif + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + { \ + UnityPrint(UnityStrExpected); \ + UnityPrintFloat(expected); \ + UnityPrint(UnityStrWas); \ + UnityPrintFloat(actual); } +#else + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + UnityPrint(UnityStrDelta) +#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ + +static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) +{ + UNITY_FLOAT diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while (elements--) + { + if (!UnityFloatsWithin(*ptr_expected * UNITY_FLOAT_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + + if (!UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat((UNITY_DOUBLE)actual); +#else + if (should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_FLOAT */ + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DOUBLE +static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) +{ + UNITY_DOUBLE diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while (elements--) + { + if (!UnityDoublesWithin(*ptr_expected * UNITY_DOUBLE_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (!UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat(actual); +#else + if (should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_DOUBLE */ + +/*-----------------------------------------------*/ +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > expected) + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(actual - expected) > delta); + else + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(expected - actual) > delta); + } + else + { + if ((UNITY_UINT)actual > (UNITY_UINT)expected) + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(actual - expected) > delta); + else + Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(expected - actual) > delta); + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; expected[i] || actual[i]; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStrings(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; (i < length) && (expected[i] || actual[i]); i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStringsLen(expected, actual, length); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 i = 0; + UNITY_UINT32 j = 0; + const char* expd = NULL; + const char* act = NULL; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if no elements, it's an error */ + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if ((const void*)expected == (const void*)actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + if (flags != UNITY_ARRAY_TO_ARRAY) + { + expd = (const char*)expected; + } + + do + { + act = actual[j]; + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expd = ((const char* const*)expected)[j]; + } + + /* if both pointers not null compare the strings */ + if (expd && act) + { + for (i = 0; expd[i] || act[i]; i++) + { + if (expd[i] != act[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expd != act) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(j); + } + UnityPrintExpectedAndActualStrings(expd, act); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + } while (++j < num_elements); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; + UNITY_UINT32 elements = num_elements; + UNITY_UINT32 bytes; + + RETURN_IF_FAIL_OR_IGNORE; + + if ((elements == 0) || (length == 0)) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) return; /* Both are NULL or same pointer */ + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + UNITY_FAIL_AND_BAIL; + + while (elements--) + { + bytes = length; + while (bytes--) + { + if (*ptr_exp != *ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrMemory); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + } + UnityPrint(UnityStrByte); + UnityPrintNumberUnsigned(length - bytes - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp++; + ptr_act++; + } + if (flags == UNITY_ARRAY_TO_VAL) + { + ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + } + } +} + +/*-----------------------------------------------*/ + +static union +{ + UNITY_INT8 i8; + UNITY_INT16 i16; + UNITY_INT32 i32; +#ifdef UNITY_SUPPORT_64 + UNITY_INT64 i64; +#endif +#ifndef UNITY_EXCLUDE_FLOAT + float f; +#endif +#ifndef UNITY_EXCLUDE_DOUBLE + double d; +#endif +} UnityQuickCompare; + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) +{ + switch(size) + { + case 1: + UnityQuickCompare.i8 = (UNITY_INT8)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); + + case 2: + UnityQuickCompare.i16 = (UNITY_INT16)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); + +#ifdef UNITY_SUPPORT_64 + case 8: + UnityQuickCompare.i64 = (UNITY_INT64)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); +#endif + default: /* 4 bytes */ + UnityQuickCompare.i32 = (UNITY_INT32)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); + } +} + +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) +{ + UnityQuickCompare.f = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); +} +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) +{ + UnityQuickCompare.d = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); +} +#endif + +/*----------------------------------------------- + * Control Functions + *-----------------------------------------------*/ + +void UnityFail(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + if (msg[0] != ' ') + { + UNITY_OUTPUT_CHAR(' '); + } + UnityPrint(msg); + } + + UNITY_FAIL_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrIgnore); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_IGNORE_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) +{ + Unity.CurrentTestName = FuncName; + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + if (TEST_PROTECT()) + { + setUp(); + Func(); + } + if (TEST_PROTECT()) + { + tearDown(); + } + UnityConcludeTest(); +} + +/*-----------------------------------------------*/ +void UnityBegin(const char* filename) +{ + Unity.TestFile = filename; + Unity.CurrentTestName = NULL; + Unity.CurrentTestLineNumber = 0; + Unity.NumberOfTests = 0; + Unity.TestFailures = 0; + Unity.TestIgnores = 0; + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + + UNITY_CLR_DETAILS(); + UNITY_OUTPUT_START(); +} + +/*-----------------------------------------------*/ +int UnityEnd(void) +{ + UNITY_PRINT_EOL(); + UnityPrint(UnityStrBreaker); + UNITY_PRINT_EOL(); + UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); + UnityPrint(UnityStrResultsTests); + UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); + UnityPrint(UnityStrResultsFailures); + UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); + UnityPrint(UnityStrResultsIgnored); + UNITY_PRINT_EOL(); + if (Unity.TestFailures == 0U) + { + UnityPrint(UnityStrOk); + } + else + { + UnityPrint(UnityStrFail); +#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL + UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); +#endif + } + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); + UNITY_OUTPUT_COMPLETE(); + return (int)(Unity.TestFailures); +} + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ +#ifdef UNITY_USE_COMMAND_LINE_ARGS + +char* UnityOptionIncludeNamed = NULL; +char* UnityOptionExcludeNamed = NULL; +int UnityVerbosity = 1; + +int UnityParseOptions(int argc, char** argv) +{ + UnityOptionIncludeNamed = NULL; + UnityOptionExcludeNamed = NULL; + + for (int i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'l': /* list tests */ + return -1; + case 'n': /* include tests with name including this string */ + case 'f': /* an alias for -n */ + if (argv[i][2] == '=') + UnityOptionIncludeNamed = &argv[i][3]; + else if (++i < argc) + UnityOptionIncludeNamed = argv[i]; + else + { + UnityPrint("ERROR: No Test String to Include Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + case 'q': /* quiet */ + UnityVerbosity = 0; + break; + case 'v': /* verbose */ + UnityVerbosity = 2; + break; + case 'x': /* exclude tests with name including this string */ + if (argv[i][2] == '=') + UnityOptionExcludeNamed = &argv[i][3]; + else if (++i < argc) + UnityOptionExcludeNamed = argv[i]; + else + { + UnityPrint("ERROR: No Test String to Exclude Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + default: + UnityPrint("ERROR: Unknown Option "); + UNITY_OUTPUT_CHAR(argv[i][1]); + UNITY_PRINT_EOL(); + return 1; + } + } + } + + return 0; +} + +int IsStringInBiggerString(const char* longstring, const char* shortstring) +{ + const char* lptr = longstring; + const char* sptr = shortstring; + const char* lnext = lptr; + + if (*sptr == '*') + return 1; + + while (*lptr) + { + lnext = lptr + 1; + + /* If they current bytes match, go on to the next bytes */ + while (*lptr && *sptr && (*lptr == *sptr)) + { + lptr++; + sptr++; + + /* We're done if we match the entire string or up to a wildcard */ + if (*sptr == '*') + return 1; + if (*sptr == ',') + return 1; + if (*sptr == '"') + return 1; + if (*sptr == '\'') + return 1; + if (*sptr == ':') + return 2; + if (*sptr == 0) + return 1; + } + + /* Otherwise we start in the long pointer 1 character further and try again */ + lptr = lnext; + sptr = shortstring; + } + return 0; +} + +int UnityStringArgumentMatches(const char* str) +{ + int retval; + const char* ptr1; + const char* ptr2; + const char* ptrf; + + /* Go through the options and get the substrings for matching one at a time */ + ptr1 = str; + while (ptr1[0] != 0) + { + if ((ptr1[0] == '"') || (ptr1[0] == '\'')) + ptr1++; + + /* look for the start of the next partial */ + ptr2 = ptr1; + ptrf = 0; + do + { + ptr2++; + if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) + ptrf = &ptr2[1]; + } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); + while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) + ptr2++; + + /* done if complete filename match */ + retval = IsStringInBiggerString(Unity.TestFile, ptr1); + if (retval == 1) + return retval; + + /* done if testname match after filename partial match */ + if ((retval == 2) && (ptrf != 0)) + { + if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) + return 1; + } + + /* done if complete testname match */ + if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) + return 1; + + ptr1 = ptr2; + } + + /* we couldn't find a match for any substrings */ + return 0; +} + +int UnityTestMatches(void) +{ + /* Check if this test name matches the included test pattern */ + int retval; + if (UnityOptionIncludeNamed) + { + retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); + } + else + retval = 1; + + /* Check if this test name matches the excluded test pattern */ + if (UnityOptionExcludeNamed) + { + if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) + retval = 0; + } + return retval; +} + +#endif /* UNITY_USE_COMMAND_LINE_ARGS */ +/*-----------------------------------------------*/ diff --git a/3rd/libzmq/external/unity/unity.h b/3rd/libzmq/external/unity/unity.h new file mode 100644 index 00000000..32ff0e6d --- /dev/null +++ b/3rd/libzmq/external/unity/unity.h @@ -0,0 +1,503 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_FRAMEWORK_H +#define UNITY_FRAMEWORK_H +#define UNITY + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "unity_internals.h" + +/*------------------------------------------------------- + * Test Setup / Teardown + *-------------------------------------------------------*/ + +/* These functions are intended to be called before and after each test. */ +void setUp(void); +void tearDown(void); + +/* These functions are intended to be called at the beginning and end of an + * entire test suite. suiteTearDown() is passed the number of tests that + * failed, and its return value becomes the exit code of main(). */ +void suiteSetUp(void); +int suiteTearDown(int num_failures); + +/* If the compiler supports it, the following block provides stub + * implementations of the above functions as weak symbols. Note that on + * some platforms (MinGW for example), weak function implementations need + * to be in the same translation unit they are called from. This can be + * achieved by defining UNITY_INCLUDE_SETUP_STUBS before including unity.h. */ +#ifdef UNITY_INCLUDE_SETUP_STUBS + #ifdef UNITY_WEAK_ATTRIBUTE + UNITY_WEAK_ATTRIBUTE void setUp(void) { } + UNITY_WEAK_ATTRIBUTE void tearDown(void) { } + UNITY_WEAK_ATTRIBUTE void suiteSetUp(void) { } + UNITY_WEAK_ATTRIBUTE int suiteTearDown(int num_failures) { return num_failures; } + #elif defined(UNITY_WEAK_PRAGMA) + #pragma weak setUp + void setUp(void) { } + #pragma weak tearDown + void tearDown(void) { } + #pragma weak suiteSetUp + void suiteSetUp(void) { } + #pragma weak suiteTearDown + int suiteTearDown(int num_failures) { return num_failures; } + #endif +#endif + +/*------------------------------------------------------- + * Configuration Options + *------------------------------------------------------- + * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. + + * Integers/longs/pointers + * - Unity attempts to automatically discover your integer sizes + * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in + * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in + * - If you cannot use the automatic methods above, you can force Unity by using these options: + * - define UNITY_SUPPORT_64 + * - set UNITY_INT_WIDTH + * - set UNITY_LONG_WIDTH + * - set UNITY_POINTER_WIDTH + + * Floats + * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons + * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT + * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats + * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons + * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) + * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE + * - define UNITY_DOUBLE_TYPE to specify something other than double + * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors + + * Output + * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired + * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure + + * Optimization + * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge + * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. + + * Test Cases + * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script + + * Parameterized Tests + * - you'll want to create a define of TEST_CASE(...) which basically evaluates to nothing + + * Tests with Arguments + * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity + + *------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) +#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) +#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) +#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) +#define TEST_ONLY() + +/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. + * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ +#define TEST_PASS() TEST_ABORT() + +/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out + * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */ +#define TEST_FILE(a) + +/*------------------------------------------------------- + * Test Asserts (simple) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") +#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") +#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") +#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") +#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") +#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, NULL) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) + +/* Arrays Compared To Single Value */ +#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/*------------------------------------------------------- + * Test Asserts (with additional messages) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) + +/* Arrays Compared To Single Value*/ +#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* end of UNITY_FRAMEWORK_H */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/3rd/libzmq/external/unity/unity_internals.h b/3rd/libzmq/external/unity/unity_internals.h new file mode 100644 index 00000000..67e219b8 --- /dev/null +++ b/3rd/libzmq/external/unity/unity_internals.h @@ -0,0 +1,872 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_INTERNALS_H +#define UNITY_INTERNALS_H + +#ifdef UNITY_INCLUDE_CONFIG_H +#include "unity_config.h" +#endif + +#ifndef UNITY_EXCLUDE_SETJMP_H +#include +#endif + +#ifndef UNITY_EXCLUDE_MATH_H +#include +#endif + +/* Unity Attempts to Auto-Detect Integer Types + * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits + * Attempt 2: UINTPTR_MAX in , or default to same size as long + * The user may override any of these derived constants: + * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ +#ifndef UNITY_EXCLUDE_STDINT_H +#include +#endif + +#ifndef UNITY_EXCLUDE_LIMITS_H +#include +#endif + +/*------------------------------------------------------- + * Guess Widths If Not Specified + *-------------------------------------------------------*/ + +/* Determine the size of an int, if not already specified. + * We cannot use sizeof(int), because it is not yet defined + * at this stage in the translation of the C program. + * Therefore, infer it from UINT_MAX if possible. */ +#ifndef UNITY_INT_WIDTH + #ifdef UINT_MAX + #if (UINT_MAX == 0xFFFF) + #define UNITY_INT_WIDTH (16) + #elif (UINT_MAX == 0xFFFFFFFF) + #define UNITY_INT_WIDTH (32) + #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_INT_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_INT_WIDTH (32) + #endif /* UINT_MAX */ +#endif + +/* Determine the size of a long, if not already specified. */ +#ifndef UNITY_LONG_WIDTH + #ifdef ULONG_MAX + #if (ULONG_MAX == 0xFFFF) + #define UNITY_LONG_WIDTH (16) + #elif (ULONG_MAX == 0xFFFFFFFF) + #define UNITY_LONG_WIDTH (32) + #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_LONG_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_LONG_WIDTH (32) + #endif /* ULONG_MAX */ +#endif + +/* Determine the size of a pointer, if not already specified. */ +#ifndef UNITY_POINTER_WIDTH + #ifdef UINTPTR_MAX + #if (UINTPTR_MAX <= 0xFFFF) + #define UNITY_POINTER_WIDTH (16) + #elif (UINTPTR_MAX <= 0xFFFFFFFF) + #define UNITY_POINTER_WIDTH (32) + #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) + #define UNITY_POINTER_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH + #endif /* UINTPTR_MAX */ +#endif + +/*------------------------------------------------------- + * Int Support (Define types based on detected sizes) + *-------------------------------------------------------*/ + +#if (UNITY_INT_WIDTH == 32) + typedef unsigned char UNITY_UINT8; + typedef unsigned short UNITY_UINT16; + typedef unsigned int UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed short UNITY_INT16; + typedef signed int UNITY_INT32; +#elif (UNITY_INT_WIDTH == 16) + typedef unsigned char UNITY_UINT8; + typedef unsigned int UNITY_UINT16; + typedef unsigned long UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed int UNITY_INT16; + typedef signed long UNITY_INT32; +#else + #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) +#endif + +/*------------------------------------------------------- + * 64-bit Support + *-------------------------------------------------------*/ + +#ifndef UNITY_SUPPORT_64 + #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 + #define UNITY_SUPPORT_64 + #endif +#endif + +#ifndef UNITY_SUPPORT_64 + /* No 64-bit Support */ + typedef UNITY_UINT32 UNITY_UINT; + typedef UNITY_INT32 UNITY_INT; +#else + + /* 64-bit Support */ + #if (UNITY_LONG_WIDTH == 32) + typedef unsigned long long UNITY_UINT64; + typedef signed long long UNITY_INT64; + #elif (UNITY_LONG_WIDTH == 64) + typedef unsigned long UNITY_UINT64; + typedef signed long UNITY_INT64; + #else + #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) + #endif + typedef UNITY_UINT64 UNITY_UINT; + typedef UNITY_INT64 UNITY_INT; + +#endif + +/*------------------------------------------------------- + * Pointer Support + *-------------------------------------------------------*/ + +#if (UNITY_POINTER_WIDTH == 32) +#define UNITY_PTR_TO_INT UNITY_INT32 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 +#elif (UNITY_POINTER_WIDTH == 64) +#define UNITY_PTR_TO_INT UNITY_INT64 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 +#elif (UNITY_POINTER_WIDTH == 16) +#define UNITY_PTR_TO_INT UNITY_INT16 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 +#else + #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) +#endif + +#ifndef UNITY_PTR_ATTRIBUTE +#define UNITY_PTR_ATTRIBUTE +#endif + +#ifndef UNITY_INTERNAL_PTR +#define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* +#endif + +/*------------------------------------------------------- + * Float Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_FLOAT + +/* No Floating Point Support */ +#ifndef UNITY_EXCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ +#endif +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +#define UNITY_EXCLUDE_FLOAT_PRINT +#endif + +#else + +/* Floating Point Support */ +#ifndef UNITY_FLOAT_PRECISION +#define UNITY_FLOAT_PRECISION (0.00001f) +#endif +#ifndef UNITY_FLOAT_TYPE +#define UNITY_FLOAT_TYPE float +#endif +typedef UNITY_FLOAT_TYPE UNITY_FLOAT; + +/* isinf & isnan macros should be provided by math.h */ +#ifndef isinf +/* The value of Inf - Inf is NaN */ +#define isinf(n) (isnan((n) - (n)) && !isnan(n)) +#endif + +#ifndef isnan +/* NaN is the only floating point value that does NOT equal itself. + * Therefore if n != n, then it is NaN. */ +#define isnan(n) ((n != n) ? 1 : 0) +#endif + +#endif + +/*------------------------------------------------------- + * Double Float Support + *-------------------------------------------------------*/ + +/* unlike float, we DON'T include by default */ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) + + /* No Floating Point Support */ + #ifndef UNITY_EXCLUDE_DOUBLE + #define UNITY_EXCLUDE_DOUBLE + #else + #undef UNITY_INCLUDE_DOUBLE + #endif + + #ifndef UNITY_EXCLUDE_FLOAT + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_FLOAT UNITY_DOUBLE; + /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ + #endif + +#else + + /* Double Floating Point Support */ + #ifndef UNITY_DOUBLE_PRECISION + #define UNITY_DOUBLE_PRECISION (1e-12) + #endif + + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; + +#endif + +/*------------------------------------------------------- + * Output Method: stdout (DEFAULT) + *-------------------------------------------------------*/ +#ifndef UNITY_OUTPUT_CHAR +/* Default to using putchar, which is defined in stdio.h */ +#include +#define UNITY_OUTPUT_CHAR(a) (void)putchar(a) +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION +extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#ifdef UNITY_USE_FLUSH_STDOUT +/* We want to use the stdout flush utility */ +#include +#define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) +#else +/* We've specified nothing, therefore flush should just be ignored */ +#define UNITY_OUTPUT_FLUSH() +#endif +#else +/* We've defined flush as something else, so make sure we declare it here so it's ready for use */ +#ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION +extern void UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION; +#endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#define UNITY_FLUSH_CALL() +#else +#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() +#endif + +#ifndef UNITY_PRINT_EOL +#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') +#endif + +#ifndef UNITY_OUTPUT_START +#define UNITY_OUTPUT_START() +#endif + +#ifndef UNITY_OUTPUT_COMPLETE +#define UNITY_OUTPUT_COMPLETE() +#endif + +/*------------------------------------------------------- + * Footprint + *-------------------------------------------------------*/ + +#ifndef UNITY_LINE_TYPE +#define UNITY_LINE_TYPE UNITY_UINT +#endif + +#ifndef UNITY_COUNTER_TYPE +#define UNITY_COUNTER_TYPE UNITY_UINT +#endif + +/*------------------------------------------------------- + * Language Features Available + *-------------------------------------------------------*/ +#if !defined(UNITY_WEAK_ATTRIBUTE) && !defined(UNITY_WEAK_PRAGMA) +# if defined(__GNUC__) || defined(__ghs__) /* __GNUC__ includes clang */ +# if !(defined(__WIN32__) && defined(__clang__)) && !defined(__TMS470__) +# define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) +# endif +# endif +#endif + +#ifdef UNITY_NO_WEAK +# undef UNITY_WEAK_ATTRIBUTE +# undef UNITY_WEAK_PRAGMA +#endif + + +/*------------------------------------------------------- + * Internal Structs Needed + *-------------------------------------------------------*/ + +typedef void (*UnityTestFunction)(void); + +#define UNITY_DISPLAY_RANGE_INT (0x10) +#define UNITY_DISPLAY_RANGE_UINT (0x20) +#define UNITY_DISPLAY_RANGE_HEX (0x40) + +typedef enum +{ +UNITY_DISPLAY_STYLE_INT = sizeof(int)+ UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, +#endif + +UNITY_DISPLAY_STYLE_UINT = sizeof(unsigned) + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, +#endif + + UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, +#endif + + UNITY_DISPLAY_STYLE_UNKNOWN +} UNITY_DISPLAY_STYLE_T; + +typedef enum +{ + UNITY_EQUAL_TO = 1, + UNITY_GREATER_THAN = 2, + UNITY_GREATER_OR_EQUAL = 2 + UNITY_EQUAL_TO, + UNITY_SMALLER_THAN = 4, + UNITY_SMALLER_OR_EQUAL = 4 + UNITY_EQUAL_TO +} UNITY_COMPARISON_T; + +#ifndef UNITY_EXCLUDE_FLOAT +typedef enum UNITY_FLOAT_TRAIT +{ + UNITY_FLOAT_IS_NOT_INF = 0, + UNITY_FLOAT_IS_INF, + UNITY_FLOAT_IS_NOT_NEG_INF, + UNITY_FLOAT_IS_NEG_INF, + UNITY_FLOAT_IS_NOT_NAN, + UNITY_FLOAT_IS_NAN, + UNITY_FLOAT_IS_NOT_DET, + UNITY_FLOAT_IS_DET, + UNITY_FLOAT_INVALID_TRAIT +} UNITY_FLOAT_TRAIT_T; +#endif + +typedef enum +{ + UNITY_ARRAY_TO_VAL = 0, + UNITY_ARRAY_TO_ARRAY +} UNITY_FLAGS_T; + +struct UNITY_STORAGE_T +{ + const char* TestFile; + const char* CurrentTestName; +#ifndef UNITY_EXCLUDE_DETAILS + const char* CurrentDetail1; + const char* CurrentDetail2; +#endif + UNITY_LINE_TYPE CurrentTestLineNumber; + UNITY_COUNTER_TYPE NumberOfTests; + UNITY_COUNTER_TYPE TestFailures; + UNITY_COUNTER_TYPE TestIgnores; + UNITY_COUNTER_TYPE CurrentTestFailed; + UNITY_COUNTER_TYPE CurrentTestIgnored; +#ifndef UNITY_EXCLUDE_SETJMP_H + jmp_buf AbortFrame; +#endif +}; + +extern struct UNITY_STORAGE_T Unity; + +/*------------------------------------------------------- + * Test Suite Management + *-------------------------------------------------------*/ + +void UnityBegin(const char* filename); +int UnityEnd(void); +void UnityConcludeTest(void); +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); + +/*------------------------------------------------------- + * Details Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_DETAILS +#define UNITY_CLR_DETAILS() +#define UNITY_SET_DETAIL(d1) +#define UNITY_SET_DETAILS(d1,d2) +#else +#define UNITY_CLR_DETAILS() { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAIL(d1) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAILS(d1,d2) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = d2; } + +#ifndef UNITY_DETAIL1_NAME +#define UNITY_DETAIL1_NAME "Function" +#endif + +#ifndef UNITY_DETAIL2_NAME +#define UNITY_DETAIL2_NAME "Argument" +#endif +#endif + +/*------------------------------------------------------- + * Test Output + *-------------------------------------------------------*/ + +void UnityPrint(const char* string); +void UnityPrintLen(const char* string, const UNITY_UINT32 length); +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintNumber(const UNITY_INT number); +void UnityPrintNumberUnsigned(const UNITY_UINT number); +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles); + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +void UnityPrintFloat(const UNITY_DOUBLE input_number); +#endif + +/*------------------------------------------------------- + * Test Assertion Functions + *------------------------------------------------------- + * Use the macros below this section instead of calling + * these directly. The macros have a consistent naming + * convention and will pull in file and line information + * for you. */ + +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityFail(const char* message, const UNITY_LINE_TYPE line); + +void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); + +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +/*------------------------------------------------------- + * Helpers + *-------------------------------------------------------*/ + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); +#endif +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); +#endif + +/*------------------------------------------------------- + * Error Strings We Might Need + *-------------------------------------------------------*/ + +extern const char UnityStrErrFloat[]; +extern const char UnityStrErrDouble[]; +extern const char UnityStrErr64[]; + +/*------------------------------------------------------- + * Test Running Macros + *-------------------------------------------------------*/ + +#ifndef UNITY_EXCLUDE_SETJMP_H +#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) +#define TEST_ABORT() longjmp(Unity.AbortFrame, 1) +#else +#define TEST_PROTECT() 1 +#define TEST_ABORT() return +#endif + +/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ +#ifndef RUN_TEST +#ifdef __STDC_VERSION__ +#if __STDC_VERSION__ >= 199901L +#define RUN_TEST(...) UnityDefaultTestRun(RUN_TEST_FIRST(__VA_ARGS__), RUN_TEST_SECOND(__VA_ARGS__)) +#define RUN_TEST_FIRST(...) RUN_TEST_FIRST_HELPER(__VA_ARGS__, throwaway) +#define RUN_TEST_FIRST_HELPER(first, ...) (first), #first +#define RUN_TEST_SECOND(...) RUN_TEST_SECOND_HELPER(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_SECOND_HELPER(first, second, ...) (second) +#endif +#endif +#endif + +/* If we can't do the tricky version, we'll just have to require them to always include the line number */ +#ifndef RUN_TEST +#ifdef CMOCK +#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) +#else +#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) +#endif +#endif + +#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) +#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) +#define UNITY_NEW_TEST(a) \ + Unity.CurrentTestName = (a); \ + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ + Unity.NumberOfTests++; + +#ifndef UNITY_BEGIN +#define UNITY_BEGIN() UnityBegin(__FILE__) +#endif + +#ifndef UNITY_END +#define UNITY_END() UnityEnd() +#endif + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ + +#ifdef UNITY_USE_COMMAND_LINE_ARGS +int UnityParseOptions(int argc, char** argv); +int UnityTestMatches(void); +#endif + +/*------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) + +/*------------------------------------------------------- + * Test Asserts + *-------------------------------------------------------*/ + +#define UNITY_TEST_ASSERT(condition, line, message) if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));} +#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) + +#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) expected, sizeof(int)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) expected, sizeof(unsigned int)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) expected, sizeof(int*)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) + +#ifdef UNITY_SUPPORT_64 +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#else +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#endif + +#ifdef UNITY_EXCLUDE_FLOAT +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#else +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray((UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray(UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#else +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)line) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual, (UNITY_LINE_TYPE)(line), message) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray((UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)line, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray(UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)line, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +/* End of UNITY_INTERNALS_H */ +#endif diff --git a/3rd/libzmq/external/unity/version.txt b/3rd/libzmq/external/unity/version.txt new file mode 100644 index 00000000..f1b3f42a --- /dev/null +++ b/3rd/libzmq/external/unity/version.txt @@ -0,0 +1 @@ +https://github.com/ThrowTheSwitch/Unity/commit/b4aca70fd9e0ddf0afbdafb1b826f5edcfc1049b diff --git a/3rd/libzmq/external/wepoll/README.md b/3rd/libzmq/external/wepoll/README.md new file mode 100644 index 00000000..d334d083 --- /dev/null +++ b/3rd/libzmq/external/wepoll/README.md @@ -0,0 +1,202 @@ +# wepoll - epoll for windows + +[![][ci status badge]][ci status link] + +This library implements the [epoll][man epoll] API for Windows +applications. It is fast and scalable, and it closely resembles the API +and behavior of Linux' epoll. + +## Rationale + +Unlike Linux, OS X, and many other operating systems, Windows doesn't +have a good API for receiving socket state notifications. It only +supports the `select` and `WSAPoll` APIs, but they +[don't scale][select scale] and suffer from +[other issues][wsapoll broken]. + +Using I/O completion ports isn't always practical when software is +designed to be cross-platform. Wepoll offers an alternative that is +much closer to a drop-in replacement for software that was designed +to run on Linux. + +## Features + +* Can poll 100000s of sockets efficiently. +* Fully thread-safe. +* Multiple threads can poll the same epoll port. +* Sockets can be added to multiple epoll sets. +* All epoll events (`EPOLLIN`, `EPOLLOUT`, `EPOLLPRI`, `EPOLLRDHUP`) + are supported. +* Level-triggered and one-shot (`EPOLLONESTHOT`) modes are supported +* Trivial to embed: you need [only two files][dist]. + +## Limitations + +* Only works with sockets. +* Edge-triggered (`EPOLLET`) mode isn't supported. + +## How to use + +The library is [distributed][dist] as a single source file +([wepoll.c][wepoll.c]) and a single header file ([wepoll.h][wepoll.h]).
+Compile the .c file as part of your project, and include the header wherever +needed. + +## Compatibility + +* Requires Windows Vista or higher. +* Can be compiled with recent versions of MSVC, Clang, and GCC. + +## API + +### General remarks + +* The epoll port is a `HANDLE`, not a file descriptor. +* All functions set both `errno` and `GetLastError()` on failure. +* For more extensive documentation, see the [epoll(7) man page][man epoll], + and the per-function man pages that are linked below. + +### epoll_create/epoll_create1 + +```c +HANDLE epoll_create(int size); +HANDLE epoll_create1(int flags); +``` + +* Create a new epoll instance (port). +* `size` is ignored but most be greater than zero. +* `flags` must be zero as there are no supported flags. +* Returns `NULL` on failure. +* [Linux man page][man epoll_create] + +### epoll_close + +```c +int epoll_close(HANDLE ephnd); +``` + +* Close an epoll port. +* Do not attempt to close the epoll port with `close()`, + `CloseHandle()` or `closesocket()`. + +### epoll_ctl + +```c +int epoll_ctl(HANDLE ephnd, + int op, + SOCKET sock, + struct epoll_event* event); +``` + +* Control which socket events are monitored by an epoll port. +* `ephnd` must be a HANDLE created by + [`epoll_create()`](#epoll_createepoll_create1) or + [`epoll_create1()`](#epoll_createepoll_create1). +* `op` must be one of `EPOLL_CTL_ADD`, `EPOLL_CTL_MOD`, `EPOLL_CTL_DEL`. +* `sock` must be a valid socket created by [`socket()`][msdn socket], + [`WSASocket()`][msdn wsasocket], or [`accept()`][msdn accept]. +* `event` should be a pointer to a [`struct epoll_event`](#struct-epoll_event).
+ If `op` is `EPOLL_CTL_DEL` then the `event` parameter is ignored, and it + may be `NULL`. +* Returns 0 on success, -1 on failure. +* It is recommended to always explicitly remove a socket from its epoll + set using `EPOLL_CTL_DEL` *before* closing it.
+ As on Linux, closed sockets are automatically removed from the epoll set, but + wepoll may not be able to detect that a socket was closed until the next call + to [`epoll_wait()`](#epoll_wait). +* [Linux man page][man epoll_ctl] + +### epoll_wait + +```c +int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout); +``` + +* Receive socket events from an epoll port. +* `events` should point to a caller-allocated array of + [`epoll_event`](#struct-epoll_event) structs, which will receive the + reported events. +* `maxevents` is the maximum number of events that will be written to the + `events` array, and must be greater than zero. +* `timeout` specifies whether to block when no events are immediately available. + - `<0` block indefinitely + - `0` report any events that are already waiting, but don't block + - `≥1` block for at most N milliseconds +* Return value: + - `-1` an error occurred + - `0` timed out without any events to report + - `≥1` the number of events stored in the `events` buffer +* [Linux man page][man epoll_wait] + +### struct epoll_event + +```c +typedef union epoll_data { + void* ptr; + int fd; + uint32_t u32; + uint64_t u64; + SOCKET sock; /* Windows specific */ + HANDLE hnd; /* Windows specific */ +} epoll_data_t; +``` + +```c +struct epoll_event { + uint32_t events; /* Epoll events and flags */ + epoll_data_t data; /* User data variable */ +}; +``` + +* The `events` field is a bit mask containing the events being + monitored/reported, and optional flags.
+ Flags are accepted by [`epoll_ctl()`](#epoll_ctl), but they are not reported + back by [`epoll_wait()`](#epoll_wait). +* The `data` field can be used to associate application-specific information + with a socket; its value will be returned unmodified by + [`epoll_wait()`](#epoll_wait). +* [Linux man page][man epoll_ctl] + +| Event | Description | +|---------------|----------------------------------------------------------------------| +| `EPOLLIN` | incoming data available, or incoming connection ready to be accepted | +| `EPOLLOUT` | ready to send data, or outgoing connection successfully established | +| `EPOLLRDHUP` | remote peer initiated graceful socket shutdown | +| `EPOLLPRI` | out-of-band data available for reading | +| `EPOLLERR` | socket error1 | +| `EPOLLHUP` | socket hang-up1 | +| `EPOLLRDNORM` | same as `EPOLLIN` | +| `EPOLLRDBAND` | same as `EPOLLPRI` | +| `EPOLLWRNORM` | same as `EPOLLOUT` | +| `EPOLLWRBAND` | same as `EPOLLOUT` | +| `EPOLLMSG` | never reported | + +| Flag | Description | +|------------------|---------------------------| +| `EPOLLONESHOT` | report event(s) only once | +| `EPOLLET` | not supported by wepoll | +| `EPOLLEXCLUSIVE` | not supported by wepoll | +| `EPOLLWAKEUP` | not supported by wepoll | + +1: the `EPOLLERR` and `EPOLLHUP` events may always be reported by +[`epoll_wait()`](#epoll_wait), regardless of the event mask that was passed to +[`epoll_ctl()`](#epoll_ctl). + + +[ci status badge]: https://ci.appveyor.com/api/projects/status/github/piscisaureus/wepoll?branch=master&svg=true +[ci status link]: https://ci.appveyor.com/project/piscisaureus/wepoll/branch/master +[dist]: https://github.com/piscisaureus/wepoll/tree/dist +[man epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html +[man epoll_create]: http://man7.org/linux/man-pages/man2/epoll_create.2.html +[man epoll_ctl]: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html +[man epoll_wait]: http://man7.org/linux/man-pages/man2/epoll_wait.2.html +[msdn accept]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx +[msdn socket]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx +[msdn wsasocket]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx +[select scale]: https://daniel.haxx.se/docs/poll-vs-select.html +[wsapoll broken]: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/ +[wepoll.c]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.c +[wepoll.h]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.h diff --git a/3rd/libzmq/external/wepoll/license.txt b/3rd/libzmq/external/wepoll/license.txt new file mode 100644 index 00000000..31ad7c22 --- /dev/null +++ b/3rd/libzmq/external/wepoll/license.txt @@ -0,0 +1,28 @@ +wepoll - epoll for Windows +https://github.com/piscisaureus/wepoll + +Copyright 2012-2018, Bert Belder +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rd/libzmq/external/wepoll/version.txt b/3rd/libzmq/external/wepoll/version.txt new file mode 100644 index 00000000..62915844 --- /dev/null +++ b/3rd/libzmq/external/wepoll/version.txt @@ -0,0 +1 @@ +https://github.com/piscisaureus/wepoll/tree/v1.5.4 diff --git a/3rd/libzmq/external/wepoll/wepoll.c b/3rd/libzmq/external/wepoll/wepoll.c new file mode 100644 index 00000000..3d301410 --- /dev/null +++ b/3rd/libzmq/external/wepoll/wepoll.c @@ -0,0 +1,2186 @@ +/* + * wepoll - epoll for Windows + * https://github.com/piscisaureus/wepoll + * + * Copyright 2012-2018, Bert Belder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WEPOLL_EXPORT +#define WEPOLL_EXPORT +#endif + +#include + +/* clang-format off */ + +enum EPOLL_EVENTS { + EPOLLIN = (int) (1U << 0), + EPOLLPRI = (int) (1U << 1), + EPOLLOUT = (int) (1U << 2), + EPOLLERR = (int) (1U << 3), + EPOLLHUP = (int) (1U << 4), + EPOLLRDNORM = (int) (1U << 6), + EPOLLRDBAND = (int) (1U << 7), + EPOLLWRNORM = (int) (1U << 8), + EPOLLWRBAND = (int) (1U << 9), + EPOLLMSG = (int) (1U << 10), /* Never reported. */ + EPOLLRDHUP = (int) (1U << 13), + EPOLLONESHOT = (int) (1U << 31) +}; + +#define EPOLLIN (1U << 0) +#define EPOLLPRI (1U << 1) +#define EPOLLOUT (1U << 2) +#define EPOLLERR (1U << 3) +#define EPOLLHUP (1U << 4) +#define EPOLLRDNORM (1U << 6) +#define EPOLLRDBAND (1U << 7) +#define EPOLLWRNORM (1U << 8) +#define EPOLLWRBAND (1U << 9) +#define EPOLLMSG (1U << 10) +#define EPOLLRDHUP (1U << 13) +#define EPOLLONESHOT (1U << 31) + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_MOD 2 +#define EPOLL_CTL_DEL 3 + +/* clang-format on */ + +typedef void* HANDLE; +typedef uintptr_t SOCKET; + +typedef union epoll_data { + void* ptr; + int fd; + uint32_t u32; + uint64_t u64; + SOCKET sock; /* Windows specific */ + HANDLE hnd; /* Windows specific */ +} epoll_data_t; + +struct epoll_event { + uint32_t events; /* Epoll events and flags */ + epoll_data_t data; /* User data variable */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +WEPOLL_EXPORT HANDLE epoll_create(int size); +WEPOLL_EXPORT HANDLE epoll_create1(int flags); + +WEPOLL_EXPORT int epoll_close(HANDLE ephnd); + +WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd, + int op, + SOCKET sock, + struct epoll_event* event); + +WEPOLL_EXPORT int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include +#include + +#define WEPOLL_INTERNAL static +#define WEPOLL_INTERNAL_VAR static + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif + +#define _WIN32_WINNT 0x0600 + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifndef __GNUC__ +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifndef __GNUC__ +#pragma warning(pop) +#endif + +WEPOLL_INTERNAL int nt_global_init(void); + +typedef LONG NTSTATUS; +typedef NTSTATUS* PNTSTATUS; + +#ifndef NT_SUCCESS +#define NT_SUCCESS(status) (((NTSTATUS)(status)) >= 0) +#endif + +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) +#endif + +#ifndef STATUS_PENDING +#define STATUS_PENDING ((NTSTATUS) 0x00000103L) +#endif + +#ifndef STATUS_CANCELLED +#define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) +#endif + +typedef struct _IO_STATUS_BLOCK { + NTSTATUS Status; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef VOID(NTAPI* PIO_APC_ROUTINE)(PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG Reserved); + +typedef struct _LSA_UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; + +#define RTL_CONSTANT_STRING(s) \ + { sizeof(s) - sizeof((s)[0]), sizeof(s), s } + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +#define RTL_CONSTANT_OBJECT_ATTRIBUTES(ObjectName, Attributes) \ + { sizeof(OBJECT_ATTRIBUTES), NULL, ObjectName, Attributes, NULL, NULL } + +#ifndef FILE_OPEN +#define FILE_OPEN 0x00000001UL +#endif + +#define NT_NTDLL_IMPORT_LIST(X) \ + X(NTSTATUS, \ + NTAPI, \ + NtCreateFile, \ + (PHANDLE FileHandle, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_ATTRIBUTES ObjectAttributes, \ + PIO_STATUS_BLOCK IoStatusBlock, \ + PLARGE_INTEGER AllocationSize, \ + ULONG FileAttributes, \ + ULONG ShareAccess, \ + ULONG CreateDisposition, \ + ULONG CreateOptions, \ + PVOID EaBuffer, \ + ULONG EaLength)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtDeviceIoControlFile, \ + (HANDLE FileHandle, \ + HANDLE Event, \ + PIO_APC_ROUTINE ApcRoutine, \ + PVOID ApcContext, \ + PIO_STATUS_BLOCK IoStatusBlock, \ + ULONG IoControlCode, \ + PVOID InputBuffer, \ + ULONG InputBufferLength, \ + PVOID OutputBuffer, \ + ULONG OutputBufferLength)) \ + \ + X(ULONG, WINAPI, RtlNtStatusToDosError, (NTSTATUS Status)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtCreateKeyedEvent, \ + (PHANDLE handle, \ + ACCESS_MASK access, \ + POBJECT_ATTRIBUTES attr, \ + ULONG flags)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtWaitForKeyedEvent, \ + (HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtReleaseKeyedEvent, \ + (HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout)) + +#define X(return_type, attributes, name, parameters) \ + WEPOLL_INTERNAL_VAR return_type(attributes* name) parameters; +NT_NTDLL_IMPORT_LIST(X) +#undef X + +#include +#include + +#ifndef _SSIZE_T_DEFINED +typedef intptr_t ssize_t; +#endif + +#define array_count(a) (sizeof(a) / (sizeof((a)[0]))) + +/* clang-format off */ +#define container_of(ptr, type, member) \ + ((type*) ((uintptr_t) (ptr) - offsetof(type, member))) +/* clang-format on */ + +#define unused_var(v) ((void) (v)) + +/* Polyfill `inline` for older versions of msvc (up to Visual Studio 2013) */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define inline __inline +#endif + +/* clang-format off */ +#define AFD_POLL_RECEIVE 0x0001 +#define AFD_POLL_RECEIVE_EXPEDITED 0x0002 +#define AFD_POLL_SEND 0x0004 +#define AFD_POLL_DISCONNECT 0x0008 +#define AFD_POLL_ABORT 0x0010 +#define AFD_POLL_LOCAL_CLOSE 0x0020 +#define AFD_POLL_ACCEPT 0x0080 +#define AFD_POLL_CONNECT_FAIL 0x0100 +/* clang-format on */ + +typedef struct _AFD_POLL_HANDLE_INFO { + HANDLE Handle; + ULONG Events; + NTSTATUS Status; +} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; + +typedef struct _AFD_POLL_INFO { + LARGE_INTEGER Timeout; + ULONG NumberOfHandles; + ULONG Exclusive; + AFD_POLL_HANDLE_INFO Handles[1]; +} AFD_POLL_INFO, *PAFD_POLL_INFO; + +WEPOLL_INTERNAL int afd_create_helper_handle(HANDLE iocp, + HANDLE* afd_helper_handle_out); + +WEPOLL_INTERNAL int afd_poll(HANDLE afd_helper_handle, + AFD_POLL_INFO* poll_info, + OVERLAPPED* overlapped); + +#define return_map_error(value) \ + do { \ + err_map_win_error(); \ + return (value); \ + } while (0) + +#define return_set_error(value, error) \ + do { \ + err_set_win_error(error); \ + return (value); \ + } while (0) + +WEPOLL_INTERNAL void err_map_win_error(void); +WEPOLL_INTERNAL void err_set_win_error(DWORD error); +WEPOLL_INTERNAL int err_check_handle(HANDLE handle); + +WEPOLL_INTERNAL int ws_global_init(void); +WEPOLL_INTERNAL SOCKET ws_get_base_socket(SOCKET socket); + +#define IOCTL_AFD_POLL 0x00012024 + +static UNICODE_STRING afd__helper_name = + RTL_CONSTANT_STRING(L"\\Device\\Afd\\Wepoll"); + +static OBJECT_ATTRIBUTES afd__helper_attributes = + RTL_CONSTANT_OBJECT_ATTRIBUTES(&afd__helper_name, 0); + +int afd_create_helper_handle(HANDLE iocp, HANDLE* afd_helper_handle_out) { + HANDLE afd_helper_handle; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + /* By opening \Device\Afd without specifying any extended attributes, we'll + * get a handle that lets us talk to the AFD driver, but that doesn't have an + * associated endpoint (so it's not a socket). */ + status = NtCreateFile(&afd_helper_handle, + SYNCHRONIZE, + &afd__helper_attributes, + &iosb, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + NULL, + 0); + if (status != STATUS_SUCCESS) + return_set_error(-1, RtlNtStatusToDosError(status)); + + if (CreateIoCompletionPort(afd_helper_handle, iocp, 0, 0) == NULL) + goto error; + + if (!SetFileCompletionNotificationModes(afd_helper_handle, + FILE_SKIP_SET_EVENT_ON_HANDLE)) + goto error; + + *afd_helper_handle_out = afd_helper_handle; + return 0; + +error: + CloseHandle(afd_helper_handle); + return_map_error(-1); +} + +int afd_poll(HANDLE afd_helper_handle, + AFD_POLL_INFO* poll_info, + OVERLAPPED* overlapped) { + IO_STATUS_BLOCK* iosb; + HANDLE event; + void* apc_context; + NTSTATUS status; + + /* Blocking operation is not supported. */ + assert(overlapped != NULL); + + iosb = (IO_STATUS_BLOCK*) &overlapped->Internal; + event = overlapped->hEvent; + + /* Do what other windows APIs would do: if hEvent has it's lowest bit set, + * don't post a completion to the completion port. */ + if ((uintptr_t) event & 1) { + event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1); + apc_context = NULL; + } else { + apc_context = overlapped; + } + + iosb->Status = STATUS_PENDING; + status = NtDeviceIoControlFile(afd_helper_handle, + event, + NULL, + apc_context, + iosb, + IOCTL_AFD_POLL, + poll_info, + sizeof *poll_info, + poll_info, + sizeof *poll_info); + + if (status == STATUS_SUCCESS) + return 0; + else if (status == STATUS_PENDING) + return_set_error(-1, ERROR_IO_PENDING); + else + return_set_error(-1, RtlNtStatusToDosError(status)); +} + +WEPOLL_INTERNAL int epoll_global_init(void); + +WEPOLL_INTERNAL int init(void); + +#include + +typedef struct queue_node queue_node_t; + +typedef struct queue_node { + queue_node_t* prev; + queue_node_t* next; +} queue_node_t; + +typedef struct queue { + queue_node_t head; +} queue_t; + +WEPOLL_INTERNAL void queue_init(queue_t* queue); +WEPOLL_INTERNAL void queue_node_init(queue_node_t* node); + +WEPOLL_INTERNAL queue_node_t* queue_first(const queue_t* queue); +WEPOLL_INTERNAL queue_node_t* queue_last(const queue_t* queue); + +WEPOLL_INTERNAL void queue_prepend(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_append(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_move_first(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_move_last(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_remove(queue_node_t* node); + +WEPOLL_INTERNAL bool queue_empty(const queue_t* queue); +WEPOLL_INTERNAL bool queue_enqueued(const queue_node_t* node); + +typedef struct port_state port_state_t; +typedef struct poll_group poll_group_t; + +WEPOLL_INTERNAL poll_group_t* poll_group_acquire(port_state_t* port); +WEPOLL_INTERNAL void poll_group_release(poll_group_t* poll_group); + +WEPOLL_INTERNAL void poll_group_delete(poll_group_t* poll_group); + +WEPOLL_INTERNAL poll_group_t* poll_group_from_queue_node( + queue_node_t* queue_node); +WEPOLL_INTERNAL HANDLE + poll_group_get_afd_helper_handle(poll_group_t* poll_group); + +/* N.b.: the tree functions do not set errno or LastError when they fail. Each + * of the API functions has at most one failure mode. It is up to the caller to + * set an appropriate error code when necessary. */ + +typedef struct tree tree_t; +typedef struct tree_node tree_node_t; + +typedef struct tree { + tree_node_t* root; +} tree_t; + +typedef struct tree_node { + tree_node_t* left; + tree_node_t* right; + tree_node_t* parent; + uintptr_t key; + bool red; +} tree_node_t; + +WEPOLL_INTERNAL void tree_init(tree_t* tree); +WEPOLL_INTERNAL void tree_node_init(tree_node_t* node); + +WEPOLL_INTERNAL int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key); +WEPOLL_INTERNAL void tree_del(tree_t* tree, tree_node_t* node); + +WEPOLL_INTERNAL tree_node_t* tree_find(const tree_t* tree, uintptr_t key); +WEPOLL_INTERNAL tree_node_t* tree_root(const tree_t* tree); + +typedef struct port_state port_state_t; +typedef struct sock_state sock_state_t; + +WEPOLL_INTERNAL sock_state_t* sock_new(port_state_t* port_state, + SOCKET socket); +WEPOLL_INTERNAL void sock_delete(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void sock_force_delete(port_state_t* port_state, + sock_state_t* sock_state); + +WEPOLL_INTERNAL int sock_set_event(port_state_t* port_state, + sock_state_t* sock_state, + const struct epoll_event* ev); + +WEPOLL_INTERNAL int sock_update(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL int sock_feed_event(port_state_t* port_state, + OVERLAPPED* overlapped, + struct epoll_event* ev); + +WEPOLL_INTERNAL sock_state_t* sock_state_from_queue_node( + queue_node_t* queue_node); +WEPOLL_INTERNAL queue_node_t* sock_state_to_queue_node( + sock_state_t* sock_state); +WEPOLL_INTERNAL sock_state_t* sock_state_from_tree_node( + tree_node_t* tree_node); +WEPOLL_INTERNAL tree_node_t* sock_state_to_tree_node(sock_state_t* sock_state); + +/* The reflock is a special kind of lock that normally prevents a chunk of + * memory from being freed, but does allow the chunk of memory to eventually be + * released in a coordinated fashion. + * + * Under normal operation, threads increase and decrease the reference count, + * which are wait-free operations. + * + * Exactly once during the reflock's lifecycle, a thread holding a reference to + * the lock may "destroy" the lock; this operation blocks until all other + * threads holding a reference to the lock have dereferenced it. After + * "destroy" returns, the calling thread may assume that no other threads have + * a reference to the lock. + * + * Attemmpting to lock or destroy a lock after reflock_unref_and_destroy() has + * been called is invalid and results in undefined behavior. Therefore the user + * should use another lock to guarantee that this can't happen. + */ + +typedef struct reflock { + volatile long state; /* 32-bit Interlocked APIs operate on `long` values. */ +} reflock_t; + +WEPOLL_INTERNAL int reflock_global_init(void); + +WEPOLL_INTERNAL void reflock_init(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_ref(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_unref(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_unref_and_destroy(reflock_t* reflock); + +typedef struct ts_tree { + tree_t tree; + SRWLOCK lock; +} ts_tree_t; + +typedef struct ts_tree_node { + tree_node_t tree_node; + reflock_t reflock; +} ts_tree_node_t; + +WEPOLL_INTERNAL void ts_tree_init(ts_tree_t* rtl); +WEPOLL_INTERNAL void ts_tree_node_init(ts_tree_node_t* node); + +WEPOLL_INTERNAL int ts_tree_add(ts_tree_t* ts_tree, + ts_tree_node_t* node, + uintptr_t key); + +WEPOLL_INTERNAL ts_tree_node_t* ts_tree_del_and_ref(ts_tree_t* ts_tree, + uintptr_t key); +WEPOLL_INTERNAL ts_tree_node_t* ts_tree_find_and_ref(ts_tree_t* ts_tree, + uintptr_t key); + +WEPOLL_INTERNAL void ts_tree_node_unref(ts_tree_node_t* node); +WEPOLL_INTERNAL void ts_tree_node_unref_and_destroy(ts_tree_node_t* node); + +typedef struct port_state port_state_t; +typedef struct sock_state sock_state_t; + +typedef struct port_state { + HANDLE iocp; + tree_t sock_tree; + queue_t sock_update_queue; + queue_t sock_deleted_queue; + queue_t poll_group_queue; + ts_tree_node_t handle_tree_node; + CRITICAL_SECTION lock; + size_t active_poll_count; +} port_state_t; + +WEPOLL_INTERNAL port_state_t* port_new(HANDLE* iocp_out); +WEPOLL_INTERNAL int port_close(port_state_t* port_state); +WEPOLL_INTERNAL int port_delete(port_state_t* port_state); + +WEPOLL_INTERNAL int port_wait(port_state_t* port_state, + struct epoll_event* events, + int maxevents, + int timeout); + +WEPOLL_INTERNAL int port_ctl(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev); + +WEPOLL_INTERNAL int port_register_socket_handle(port_state_t* port_state, + sock_state_t* sock_state, + SOCKET socket); +WEPOLL_INTERNAL void port_unregister_socket_handle(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL sock_state_t* port_find_socket(port_state_t* port_state, + SOCKET socket); + +WEPOLL_INTERNAL void port_request_socket_update(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void port_cancel_socket_update(port_state_t* port_state, + sock_state_t* sock_state); + +WEPOLL_INTERNAL void port_add_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void port_remove_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state); + +static ts_tree_t epoll__handle_tree; + +static inline port_state_t* epoll__handle_tree_node_to_port( + ts_tree_node_t* tree_node) { + return container_of(tree_node, port_state_t, handle_tree_node); +} + +int epoll_global_init(void) { + ts_tree_init(&epoll__handle_tree); + return 0; +} + +static HANDLE epoll__create(void) { + port_state_t* port_state; + HANDLE ephnd; + + if (init() < 0) + return NULL; + + port_state = port_new(&ephnd); + if (port_state == NULL) + return NULL; + + if (ts_tree_add(&epoll__handle_tree, + &port_state->handle_tree_node, + (uintptr_t) ephnd) < 0) { + /* This should never happen. */ + port_delete(port_state); + return_set_error(NULL, ERROR_ALREADY_EXISTS); + } + + return ephnd; +} + +HANDLE epoll_create(int size) { + if (size <= 0) + return_set_error(NULL, ERROR_INVALID_PARAMETER); + + return epoll__create(); +} + +HANDLE epoll_create1(int flags) { + if (flags != 0) + return_set_error(NULL, ERROR_INVALID_PARAMETER); + + return epoll__create(); +} + +int epoll_close(HANDLE ephnd) { + ts_tree_node_t* tree_node; + port_state_t* port_state; + + if (init() < 0) + return -1; + + tree_node = ts_tree_del_and_ref(&epoll__handle_tree, (uintptr_t) ephnd); + if (tree_node == NULL) { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = epoll__handle_tree_node_to_port(tree_node); + port_close(port_state); + + ts_tree_node_unref_and_destroy(tree_node); + + return port_delete(port_state); + +err: + err_check_handle(ephnd); + return -1; +} + +int epoll_ctl(HANDLE ephnd, int op, SOCKET sock, struct epoll_event* ev) { + ts_tree_node_t* tree_node; + port_state_t* port_state; + int r; + + if (init() < 0) + return -1; + + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, (uintptr_t) ephnd); + if (tree_node == NULL) { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = epoll__handle_tree_node_to_port(tree_node); + r = port_ctl(port_state, op, sock, ev); + + ts_tree_node_unref(tree_node); + + if (r < 0) + goto err; + + return 0; + +err: + /* On Linux, in the case of epoll_ctl_mod(), EBADF takes priority over other + * errors. Wepoll mimics this behavior. */ + err_check_handle(ephnd); + err_check_handle((HANDLE) sock); + return -1; +} + +int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout) { + ts_tree_node_t* tree_node; + port_state_t* port_state; + int num_events; + + if (maxevents <= 0) + return_set_error(-1, ERROR_INVALID_PARAMETER); + + if (init() < 0) + return -1; + + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, (uintptr_t) ephnd); + if (tree_node == NULL) { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = epoll__handle_tree_node_to_port(tree_node); + num_events = port_wait(port_state, events, maxevents, timeout); + + ts_tree_node_unref(tree_node); + + if (num_events < 0) + goto err; + + return num_events; + +err: + err_check_handle(ephnd); + return -1; +} + +#include + +#define ERR__ERRNO_MAPPINGS(X) \ + X(ERROR_ACCESS_DENIED, EACCES) \ + X(ERROR_ALREADY_EXISTS, EEXIST) \ + X(ERROR_BAD_COMMAND, EACCES) \ + X(ERROR_BAD_EXE_FORMAT, ENOEXEC) \ + X(ERROR_BAD_LENGTH, EACCES) \ + X(ERROR_BAD_NETPATH, ENOENT) \ + X(ERROR_BAD_NET_NAME, ENOENT) \ + X(ERROR_BAD_NET_RESP, ENETDOWN) \ + X(ERROR_BAD_PATHNAME, ENOENT) \ + X(ERROR_BROKEN_PIPE, EPIPE) \ + X(ERROR_CANNOT_MAKE, EACCES) \ + X(ERROR_COMMITMENT_LIMIT, ENOMEM) \ + X(ERROR_CONNECTION_ABORTED, ECONNABORTED) \ + X(ERROR_CONNECTION_ACTIVE, EISCONN) \ + X(ERROR_CONNECTION_REFUSED, ECONNREFUSED) \ + X(ERROR_CRC, EACCES) \ + X(ERROR_DIR_NOT_EMPTY, ENOTEMPTY) \ + X(ERROR_DISK_FULL, ENOSPC) \ + X(ERROR_DUP_NAME, EADDRINUSE) \ + X(ERROR_FILENAME_EXCED_RANGE, ENOENT) \ + X(ERROR_FILE_NOT_FOUND, ENOENT) \ + X(ERROR_GEN_FAILURE, EACCES) \ + X(ERROR_GRACEFUL_DISCONNECT, EPIPE) \ + X(ERROR_HOST_DOWN, EHOSTUNREACH) \ + X(ERROR_HOST_UNREACHABLE, EHOSTUNREACH) \ + X(ERROR_INSUFFICIENT_BUFFER, EFAULT) \ + X(ERROR_INVALID_ADDRESS, EADDRNOTAVAIL) \ + X(ERROR_INVALID_FUNCTION, EINVAL) \ + X(ERROR_INVALID_HANDLE, EBADF) \ + X(ERROR_INVALID_NETNAME, EADDRNOTAVAIL) \ + X(ERROR_INVALID_PARAMETER, EINVAL) \ + X(ERROR_INVALID_USER_BUFFER, EMSGSIZE) \ + X(ERROR_IO_PENDING, EINPROGRESS) \ + X(ERROR_LOCK_VIOLATION, EACCES) \ + X(ERROR_MORE_DATA, EMSGSIZE) \ + X(ERROR_NETNAME_DELETED, ECONNABORTED) \ + X(ERROR_NETWORK_ACCESS_DENIED, EACCES) \ + X(ERROR_NETWORK_BUSY, ENETDOWN) \ + X(ERROR_NETWORK_UNREACHABLE, ENETUNREACH) \ + X(ERROR_NOACCESS, EFAULT) \ + X(ERROR_NONPAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_NOT_ENOUGH_MEMORY, ENOMEM) \ + X(ERROR_NOT_ENOUGH_QUOTA, ENOMEM) \ + X(ERROR_NOT_FOUND, ENOENT) \ + X(ERROR_NOT_LOCKED, EACCES) \ + X(ERROR_NOT_READY, EACCES) \ + X(ERROR_NOT_SAME_DEVICE, EXDEV) \ + X(ERROR_NOT_SUPPORTED, ENOTSUP) \ + X(ERROR_NO_MORE_FILES, ENOENT) \ + X(ERROR_NO_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_OPERATION_ABORTED, EINTR) \ + X(ERROR_OUT_OF_PAPER, EACCES) \ + X(ERROR_PAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_PAGEFILE_QUOTA, ENOMEM) \ + X(ERROR_PATH_NOT_FOUND, ENOENT) \ + X(ERROR_PIPE_NOT_CONNECTED, EPIPE) \ + X(ERROR_PORT_UNREACHABLE, ECONNRESET) \ + X(ERROR_PROTOCOL_UNREACHABLE, ENETUNREACH) \ + X(ERROR_REM_NOT_LIST, ECONNREFUSED) \ + X(ERROR_REQUEST_ABORTED, EINTR) \ + X(ERROR_REQ_NOT_ACCEP, EWOULDBLOCK) \ + X(ERROR_SECTOR_NOT_FOUND, EACCES) \ + X(ERROR_SEM_TIMEOUT, ETIMEDOUT) \ + X(ERROR_SHARING_VIOLATION, EACCES) \ + X(ERROR_TOO_MANY_NAMES, ENOMEM) \ + X(ERROR_TOO_MANY_OPEN_FILES, EMFILE) \ + X(ERROR_UNEXP_NET_ERR, ECONNABORTED) \ + X(ERROR_WAIT_NO_CHILDREN, ECHILD) \ + X(ERROR_WORKING_SET_QUOTA, ENOMEM) \ + X(ERROR_WRITE_PROTECT, EACCES) \ + X(ERROR_WRONG_DISK, EACCES) \ + X(WSAEACCES, EACCES) \ + X(WSAEADDRINUSE, EADDRINUSE) \ + X(WSAEADDRNOTAVAIL, EADDRNOTAVAIL) \ + X(WSAEAFNOSUPPORT, EAFNOSUPPORT) \ + X(WSAECONNABORTED, ECONNABORTED) \ + X(WSAECONNREFUSED, ECONNREFUSED) \ + X(WSAECONNRESET, ECONNRESET) \ + X(WSAEDISCON, EPIPE) \ + X(WSAEFAULT, EFAULT) \ + X(WSAEHOSTDOWN, EHOSTUNREACH) \ + X(WSAEHOSTUNREACH, EHOSTUNREACH) \ + X(WSAEINPROGRESS, EBUSY) \ + X(WSAEINTR, EINTR) \ + X(WSAEINVAL, EINVAL) \ + X(WSAEISCONN, EISCONN) \ + X(WSAEMSGSIZE, EMSGSIZE) \ + X(WSAENETDOWN, ENETDOWN) \ + X(WSAENETRESET, EHOSTUNREACH) \ + X(WSAENETUNREACH, ENETUNREACH) \ + X(WSAENOBUFS, ENOMEM) \ + X(WSAENOTCONN, ENOTCONN) \ + X(WSAENOTSOCK, ENOTSOCK) \ + X(WSAEOPNOTSUPP, EOPNOTSUPP) \ + X(WSAEPROCLIM, ENOMEM) \ + X(WSAESHUTDOWN, EPIPE) \ + X(WSAETIMEDOUT, ETIMEDOUT) \ + X(WSAEWOULDBLOCK, EWOULDBLOCK) \ + X(WSANOTINITIALISED, ENETDOWN) \ + X(WSASYSNOTREADY, ENETDOWN) \ + X(WSAVERNOTSUPPORTED, ENOSYS) + +static errno_t err__map_win_error_to_errno(DWORD error) { + switch (error) { +#define X(error_sym, errno_sym) \ + case error_sym: \ + return errno_sym; + ERR__ERRNO_MAPPINGS(X) +#undef X + } + return EINVAL; +} + +void err_map_win_error(void) { + errno = err__map_win_error_to_errno(GetLastError()); +} + +void err_set_win_error(DWORD error) { + SetLastError(error); + errno = err__map_win_error_to_errno(error); +} + +int err_check_handle(HANDLE handle) { + DWORD flags; + + /* GetHandleInformation() succeeds when passed INVALID_HANDLE_VALUE, so check + * for this condition explicitly. */ + if (handle == INVALID_HANDLE_VALUE) + return_set_error(-1, ERROR_INVALID_HANDLE); + + if (!GetHandleInformation(handle, &flags)) + return_map_error(-1); + + return 0; +} + +static bool init__done = false; +static INIT_ONCE init__once = INIT_ONCE_STATIC_INIT; + +static BOOL CALLBACK init__once_callback(INIT_ONCE* once, + void* parameter, + void** context) { + unused_var(once); + unused_var(parameter); + unused_var(context); + + /* N.b. that initialization order matters here. */ + if (ws_global_init() < 0 || nt_global_init() < 0 || + reflock_global_init() < 0 || epoll_global_init() < 0) + return FALSE; + + init__done = true; + return TRUE; +} + +int init(void) { + if (!init__done && + !InitOnceExecuteOnce(&init__once, init__once_callback, NULL, NULL)) + return -1; /* LastError and errno aren't touched InitOnceExecuteOnce. */ + + return 0; +} + +/* Set up a workaround for the following problem: + * FARPROC addr = GetProcAddress(...); + * MY_FUNC func = (MY_FUNC) addr; <-- GCC 8 warning/error. + * MY_FUNC func = (MY_FUNC) (void*) addr; <-- MSVC warning/error. + * To compile cleanly with either compiler, do casts with this "bridge" type: + * MY_FUNC func = (MY_FUNC) (nt__fn_ptr_cast_t) addr; */ +#ifdef __GNUC__ +typedef void* nt__fn_ptr_cast_t; +#else +typedef FARPROC nt__fn_ptr_cast_t; +#endif + +#define X(return_type, attributes, name, parameters) \ + WEPOLL_INTERNAL return_type(attributes* name) parameters = NULL; +NT_NTDLL_IMPORT_LIST(X) +#undef X + +int nt_global_init(void) { + HMODULE ntdll; + FARPROC fn_ptr; + + ntdll = GetModuleHandleW(L"ntdll.dll"); + if (ntdll == NULL) + return -1; + +#define X(return_type, attributes, name, parameters) \ + fn_ptr = GetProcAddress(ntdll, #name); \ + if (fn_ptr == NULL) \ + return -1; \ + name = (return_type(attributes*) parameters)(nt__fn_ptr_cast_t) fn_ptr; + NT_NTDLL_IMPORT_LIST(X) +#undef X + + return 0; +} + +#include + +static const size_t POLL_GROUP__MAX_GROUP_SIZE = 32; + +typedef struct poll_group { + port_state_t* port_state; + queue_node_t queue_node; + HANDLE afd_helper_handle; + size_t group_size; +} poll_group_t; + +static poll_group_t* poll_group__new(port_state_t* port_state) { + poll_group_t* poll_group = malloc(sizeof *poll_group); + if (poll_group == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + + memset(poll_group, 0, sizeof *poll_group); + + queue_node_init(&poll_group->queue_node); + poll_group->port_state = port_state; + + if (afd_create_helper_handle(port_state->iocp, + &poll_group->afd_helper_handle) < 0) { + free(poll_group); + return NULL; + } + + queue_append(&port_state->poll_group_queue, &poll_group->queue_node); + + return poll_group; +} + +void poll_group_delete(poll_group_t* poll_group) { + assert(poll_group->group_size == 0); + CloseHandle(poll_group->afd_helper_handle); + queue_remove(&poll_group->queue_node); + free(poll_group); +} + +poll_group_t* poll_group_from_queue_node(queue_node_t* queue_node) { + return container_of(queue_node, poll_group_t, queue_node); +} + +HANDLE poll_group_get_afd_helper_handle(poll_group_t* poll_group) { + return poll_group->afd_helper_handle; +} + +poll_group_t* poll_group_acquire(port_state_t* port_state) { + queue_t* queue = &port_state->poll_group_queue; + poll_group_t* poll_group = + !queue_empty(queue) + ? container_of(queue_last(queue), poll_group_t, queue_node) + : NULL; + + if (poll_group == NULL || + poll_group->group_size >= POLL_GROUP__MAX_GROUP_SIZE) + poll_group = poll_group__new(port_state); + if (poll_group == NULL) + return NULL; + + if (++poll_group->group_size == POLL_GROUP__MAX_GROUP_SIZE) + queue_move_first(&port_state->poll_group_queue, &poll_group->queue_node); + + return poll_group; +} + +void poll_group_release(poll_group_t* poll_group) { + port_state_t* port_state = poll_group->port_state; + + poll_group->group_size--; + assert(poll_group->group_size < POLL_GROUP__MAX_GROUP_SIZE); + + queue_move_last(&port_state->poll_group_queue, &poll_group->queue_node); + + /* Poll groups are currently only freed when the epoll port is closed. */ +} + +#define PORT__MAX_ON_STACK_COMPLETIONS 256 + +static port_state_t* port__alloc(void) { + port_state_t* port_state = malloc(sizeof *port_state); + if (port_state == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + + return port_state; +} + +static void port__free(port_state_t* port) { + assert(port != NULL); + free(port); +} + +static HANDLE port__create_iocp(void) { + HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (iocp == NULL) + return_map_error(NULL); + + return iocp; +} + +port_state_t* port_new(HANDLE* iocp_out) { + port_state_t* port_state; + HANDLE iocp; + + port_state = port__alloc(); + if (port_state == NULL) + goto err1; + + iocp = port__create_iocp(); + if (iocp == NULL) + goto err2; + + memset(port_state, 0, sizeof *port_state); + + port_state->iocp = iocp; + tree_init(&port_state->sock_tree); + queue_init(&port_state->sock_update_queue); + queue_init(&port_state->sock_deleted_queue); + queue_init(&port_state->poll_group_queue); + ts_tree_node_init(&port_state->handle_tree_node); + InitializeCriticalSection(&port_state->lock); + + *iocp_out = iocp; + return port_state; + +err2: + port__free(port_state); +err1: + return NULL; +} + +static int port__close_iocp(port_state_t* port_state) { + HANDLE iocp = port_state->iocp; + port_state->iocp = NULL; + + if (!CloseHandle(iocp)) + return_map_error(-1); + + return 0; +} + +int port_close(port_state_t* port_state) { + int result; + + EnterCriticalSection(&port_state->lock); + result = port__close_iocp(port_state); + LeaveCriticalSection(&port_state->lock); + + return result; +} + +int port_delete(port_state_t* port_state) { + tree_node_t* tree_node; + queue_node_t* queue_node; + + /* At this point the IOCP port should have been closed. */ + assert(port_state->iocp == NULL); + + while ((tree_node = tree_root(&port_state->sock_tree)) != NULL) { + sock_state_t* sock_state = sock_state_from_tree_node(tree_node); + sock_force_delete(port_state, sock_state); + } + + while ((queue_node = queue_first(&port_state->sock_deleted_queue)) != NULL) { + sock_state_t* sock_state = sock_state_from_queue_node(queue_node); + sock_force_delete(port_state, sock_state); + } + + while ((queue_node = queue_first(&port_state->poll_group_queue)) != NULL) { + poll_group_t* poll_group = poll_group_from_queue_node(queue_node); + poll_group_delete(poll_group); + } + + assert(queue_empty(&port_state->sock_update_queue)); + + DeleteCriticalSection(&port_state->lock); + + port__free(port_state); + + return 0; +} + +static int port__update_events(port_state_t* port_state) { + queue_t* sock_update_queue = &port_state->sock_update_queue; + + /* Walk the queue, submitting new poll requests for every socket that needs + * it. */ + while (!queue_empty(sock_update_queue)) { + queue_node_t* queue_node = queue_first(sock_update_queue); + sock_state_t* sock_state = sock_state_from_queue_node(queue_node); + + if (sock_update(port_state, sock_state) < 0) + return -1; + + /* sock_update() removes the socket from the update queue. */ + } + + return 0; +} + +static void port__update_events_if_polling(port_state_t* port_state) { + if (port_state->active_poll_count > 0) + port__update_events(port_state); +} + +static int port__feed_events(port_state_t* port_state, + struct epoll_event* epoll_events, + OVERLAPPED_ENTRY* iocp_events, + DWORD iocp_event_count) { + int epoll_event_count = 0; + DWORD i; + + for (i = 0; i < iocp_event_count; i++) { + OVERLAPPED* overlapped = iocp_events[i].lpOverlapped; + struct epoll_event* ev = &epoll_events[epoll_event_count]; + + epoll_event_count += sock_feed_event(port_state, overlapped, ev); + } + + return epoll_event_count; +} + +static int port__poll(port_state_t* port_state, + struct epoll_event* epoll_events, + OVERLAPPED_ENTRY* iocp_events, + DWORD maxevents, + DWORD timeout) { + DWORD completion_count; + + if (port__update_events(port_state) < 0) + return -1; + + port_state->active_poll_count++; + + LeaveCriticalSection(&port_state->lock); + + BOOL r = GetQueuedCompletionStatusEx(port_state->iocp, + iocp_events, + maxevents, + &completion_count, + timeout, + FALSE); + + EnterCriticalSection(&port_state->lock); + + port_state->active_poll_count--; + + if (!r) + return_map_error(-1); + + return port__feed_events( + port_state, epoll_events, iocp_events, completion_count); +} + +int port_wait(port_state_t* port_state, + struct epoll_event* events, + int maxevents, + int timeout) { + OVERLAPPED_ENTRY stack_iocp_events[PORT__MAX_ON_STACK_COMPLETIONS]; + OVERLAPPED_ENTRY* iocp_events; + uint64_t due = 0; + DWORD gqcs_timeout; + int result; + + /* Check whether `maxevents` is in range. */ + if (maxevents <= 0) + return_set_error(-1, ERROR_INVALID_PARAMETER); + + /* Decide whether the IOCP completion list can live on the stack, or allocate + * memory for it on the heap. */ + if ((size_t) maxevents <= array_count(stack_iocp_events)) { + iocp_events = stack_iocp_events; + } else if ((iocp_events = + malloc((size_t) maxevents * sizeof *iocp_events)) == NULL) { + iocp_events = stack_iocp_events; + maxevents = array_count(stack_iocp_events); + } + + /* Compute the timeout for GetQueuedCompletionStatus, and the wait end + * time, if the user specified a timeout other than zero or infinite. */ + if (timeout > 0) { + due = GetTickCount64() + (uint64_t) timeout; + gqcs_timeout = (DWORD) timeout; + } else if (timeout == 0) { + gqcs_timeout = 0; + } else { + gqcs_timeout = INFINITE; + } + + EnterCriticalSection(&port_state->lock); + + /* Dequeue completion packets until either at least one interesting event + * has been discovered, or the timeout is reached. */ + for (;;) { + uint64_t now; + + result = port__poll( + port_state, events, iocp_events, (DWORD) maxevents, gqcs_timeout); + if (result < 0 || result > 0) + break; /* Result, error, or time-out. */ + + if (timeout < 0) + continue; /* When timeout is negative, never time out. */ + + /* Update time. */ + now = GetTickCount64(); + + /* Do not allow the due time to be in the past. */ + if (now >= due) { + SetLastError(WAIT_TIMEOUT); + break; + } + + /* Recompute time-out argument for GetQueuedCompletionStatus. */ + gqcs_timeout = (DWORD)(due - now); + } + + port__update_events_if_polling(port_state); + + LeaveCriticalSection(&port_state->lock); + + if (iocp_events != stack_iocp_events) + free(iocp_events); + + if (result >= 0) + return result; + else if (GetLastError() == WAIT_TIMEOUT) + return 0; + else + return -1; +} + +static int port__ctl_add(port_state_t* port_state, + SOCKET sock, + struct epoll_event* ev) { + sock_state_t* sock_state = sock_new(port_state, sock); + if (sock_state == NULL) + return -1; + + if (sock_set_event(port_state, sock_state, ev) < 0) { + sock_delete(port_state, sock_state); + return -1; + } + + port__update_events_if_polling(port_state); + + return 0; +} + +static int port__ctl_mod(port_state_t* port_state, + SOCKET sock, + struct epoll_event* ev) { + sock_state_t* sock_state = port_find_socket(port_state, sock); + if (sock_state == NULL) + return -1; + + if (sock_set_event(port_state, sock_state, ev) < 0) + return -1; + + port__update_events_if_polling(port_state); + + return 0; +} + +static int port__ctl_del(port_state_t* port_state, SOCKET sock) { + sock_state_t* sock_state = port_find_socket(port_state, sock); + if (sock_state == NULL) + return -1; + + sock_delete(port_state, sock_state); + + return 0; +} + +static int port__ctl_op(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev) { + switch (op) { + case EPOLL_CTL_ADD: + return port__ctl_add(port_state, sock, ev); + case EPOLL_CTL_MOD: + return port__ctl_mod(port_state, sock, ev); + case EPOLL_CTL_DEL: + return port__ctl_del(port_state, sock); + default: + return_set_error(-1, ERROR_INVALID_PARAMETER); + } +} + +int port_ctl(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev) { + int result; + + EnterCriticalSection(&port_state->lock); + result = port__ctl_op(port_state, op, sock, ev); + LeaveCriticalSection(&port_state->lock); + + return result; +} + +int port_register_socket_handle(port_state_t* port_state, + sock_state_t* sock_state, + SOCKET socket) { + if (tree_add(&port_state->sock_tree, + sock_state_to_tree_node(sock_state), + socket) < 0) + return_set_error(-1, ERROR_ALREADY_EXISTS); + return 0; +} + +void port_unregister_socket_handle(port_state_t* port_state, + sock_state_t* sock_state) { + tree_del(&port_state->sock_tree, sock_state_to_tree_node(sock_state)); +} + +sock_state_t* port_find_socket(port_state_t* port_state, SOCKET socket) { + tree_node_t* tree_node = tree_find(&port_state->sock_tree, socket); + if (tree_node == NULL) + return_set_error(NULL, ERROR_NOT_FOUND); + return sock_state_from_tree_node(tree_node); +} + +void port_request_socket_update(port_state_t* port_state, + sock_state_t* sock_state) { + if (queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_append(&port_state->sock_update_queue, + sock_state_to_queue_node(sock_state)); +} + +void port_cancel_socket_update(port_state_t* port_state, + sock_state_t* sock_state) { + unused_var(port_state); + if (!queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +void port_add_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state) { + if (queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_append(&port_state->sock_deleted_queue, + sock_state_to_queue_node(sock_state)); +} + +void port_remove_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state) { + unused_var(port_state); + if (!queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +void queue_init(queue_t* queue) { + queue_node_init(&queue->head); +} + +void queue_node_init(queue_node_t* node) { + node->prev = node; + node->next = node; +} + +static inline void queue__detach_node(queue_node_t* node) { + node->prev->next = node->next; + node->next->prev = node->prev; +} + +queue_node_t* queue_first(const queue_t* queue) { + return !queue_empty(queue) ? queue->head.next : NULL; +} + +queue_node_t* queue_last(const queue_t* queue) { + return !queue_empty(queue) ? queue->head.prev : NULL; +} + +void queue_prepend(queue_t* queue, queue_node_t* node) { + node->next = queue->head.next; + node->prev = &queue->head; + node->next->prev = node; + queue->head.next = node; +} + +void queue_append(queue_t* queue, queue_node_t* node) { + node->next = &queue->head; + node->prev = queue->head.prev; + node->prev->next = node; + queue->head.prev = node; +} + +void queue_move_first(queue_t* queue, queue_node_t* node) { + queue__detach_node(node); + queue_prepend(queue, node); +} + +void queue_move_last(queue_t* queue, queue_node_t* node) { + queue__detach_node(node); + queue_append(queue, node); +} + +void queue_remove(queue_node_t* node) { + queue__detach_node(node); + queue_node_init(node); +} + +bool queue_empty(const queue_t* queue) { + return !queue_enqueued(&queue->head); +} + +bool queue_enqueued(const queue_node_t* node) { + return node->prev != node; +} + +/* clang-format off */ +static const long REFLOCK__REF = (long) 0x00000001; +static const long REFLOCK__REF_MASK = (long) 0x0fffffff; +static const long REFLOCK__DESTROY = (long) 0x10000000; +static const long REFLOCK__DESTROY_MASK = (long) 0xf0000000; +static const long REFLOCK__POISON = (long) 0x300DEAD0; +/* clang-format on */ + +static HANDLE reflock__keyed_event = NULL; + +int reflock_global_init(void) { + NTSTATUS status = + NtCreateKeyedEvent(&reflock__keyed_event, ~(ACCESS_MASK) 0, NULL, 0); + if (status != STATUS_SUCCESS) + return_set_error(-1, RtlNtStatusToDosError(status)); + return 0; +} + +void reflock_init(reflock_t* reflock) { + reflock->state = 0; +} + +static void reflock__signal_event(void* address) { + NTSTATUS status = + NtReleaseKeyedEvent(reflock__keyed_event, address, FALSE, NULL); + if (status != STATUS_SUCCESS) + abort(); +} + +static void reflock__await_event(void* address) { + NTSTATUS status = + NtWaitForKeyedEvent(reflock__keyed_event, address, FALSE, NULL); + if (status != STATUS_SUCCESS) + abort(); +} + +void reflock_ref(reflock_t* reflock) { + long state = InterlockedAdd(&reflock->state, REFLOCK__REF); + unused_var(state); + assert((state & REFLOCK__DESTROY_MASK) == 0); /* Overflow or destroyed. */ +} + +void reflock_unref(reflock_t* reflock) { + long state = InterlockedAdd(&reflock->state, -REFLOCK__REF); + long ref_count = state & REFLOCK__REF_MASK; + long destroy = state & REFLOCK__DESTROY_MASK; + + unused_var(ref_count); + unused_var(destroy); + + if (state == REFLOCK__DESTROY) + reflock__signal_event(reflock); + else + assert(destroy == 0 || ref_count > 0); +} + +void reflock_unref_and_destroy(reflock_t* reflock) { + long state = + InterlockedAdd(&reflock->state, REFLOCK__DESTROY - REFLOCK__REF); + long ref_count = state & REFLOCK__REF_MASK; + + assert((state & REFLOCK__DESTROY_MASK) == + REFLOCK__DESTROY); /* Underflow or already destroyed. */ + + if (ref_count != 0) + reflock__await_event(reflock); + + state = InterlockedExchange(&reflock->state, REFLOCK__POISON); + assert(state == REFLOCK__DESTROY); +} + +static const uint32_t SOCK__KNOWN_EPOLL_EVENTS = + EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | + EPOLLRDBAND | EPOLLWRNORM | EPOLLWRBAND | EPOLLMSG | EPOLLRDHUP; + +typedef enum sock__poll_status { + SOCK__POLL_IDLE = 0, + SOCK__POLL_PENDING, + SOCK__POLL_CANCELLED +} sock__poll_status_t; + +typedef struct sock_state { + OVERLAPPED overlapped; + AFD_POLL_INFO poll_info; + queue_node_t queue_node; + tree_node_t tree_node; + poll_group_t* poll_group; + SOCKET base_socket; + epoll_data_t user_data; + uint32_t user_events; + uint32_t pending_events; + sock__poll_status_t poll_status; + bool delete_pending; +} sock_state_t; + +static inline sock_state_t* sock__alloc(void) { + sock_state_t* sock_state = malloc(sizeof *sock_state); + if (sock_state == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + return sock_state; +} + +static inline void sock__free(sock_state_t* sock_state) { + free(sock_state); +} + +static int sock__cancel_poll(sock_state_t* sock_state) { + HANDLE afd_helper_handle = + poll_group_get_afd_helper_handle(sock_state->poll_group); + assert(sock_state->poll_status == SOCK__POLL_PENDING); + + /* CancelIoEx() may fail with ERROR_NOT_FOUND if the overlapped operation has + * already completed. This is not a problem and we proceed normally. */ + if (!HasOverlappedIoCompleted(&sock_state->overlapped) && + !CancelIoEx(afd_helper_handle, &sock_state->overlapped) && + GetLastError() != ERROR_NOT_FOUND) + return_map_error(-1); + + sock_state->poll_status = SOCK__POLL_CANCELLED; + sock_state->pending_events = 0; + return 0; +} + +sock_state_t* sock_new(port_state_t* port_state, SOCKET socket) { + SOCKET base_socket; + poll_group_t* poll_group; + sock_state_t* sock_state; + + if (socket == 0 || socket == INVALID_SOCKET) + return_set_error(NULL, ERROR_INVALID_HANDLE); + + base_socket = ws_get_base_socket(socket); + if (base_socket == INVALID_SOCKET) + return NULL; + + poll_group = poll_group_acquire(port_state); + if (poll_group == NULL) + return NULL; + + sock_state = sock__alloc(); + if (sock_state == NULL) + goto err1; + + memset(sock_state, 0, sizeof *sock_state); + + sock_state->base_socket = base_socket; + sock_state->poll_group = poll_group; + + tree_node_init(&sock_state->tree_node); + queue_node_init(&sock_state->queue_node); + + if (port_register_socket_handle(port_state, sock_state, socket) < 0) + goto err2; + + return sock_state; + +err2: + sock__free(sock_state); +err1: + poll_group_release(poll_group); + + return NULL; +} + +static int sock__delete(port_state_t* port_state, + sock_state_t* sock_state, + bool force) { + if (!sock_state->delete_pending) { + if (sock_state->poll_status == SOCK__POLL_PENDING) + sock__cancel_poll(sock_state); + + port_cancel_socket_update(port_state, sock_state); + port_unregister_socket_handle(port_state, sock_state); + + sock_state->delete_pending = true; + } + + /* If the poll request still needs to complete, the sock_state object can't + * be free()d yet. `sock_feed_event()` or `port_close()` will take care + * of this later. */ + if (force || sock_state->poll_status == SOCK__POLL_IDLE) { + /* Free the sock_state now. */ + port_remove_deleted_socket(port_state, sock_state); + poll_group_release(sock_state->poll_group); + sock__free(sock_state); + } else { + /* Free the socket later. */ + port_add_deleted_socket(port_state, sock_state); + } + + return 0; +} + +void sock_delete(port_state_t* port_state, sock_state_t* sock_state) { + sock__delete(port_state, sock_state, false); +} + +void sock_force_delete(port_state_t* port_state, sock_state_t* sock_state) { + sock__delete(port_state, sock_state, true); +} + +int sock_set_event(port_state_t* port_state, + sock_state_t* sock_state, + const struct epoll_event* ev) { + /* EPOLLERR and EPOLLHUP are always reported, even when not requested by the + * caller. However they are disabled after a event has been reported for a + * socket for which the EPOLLONESHOT flag as set. */ + uint32_t events = ev->events | EPOLLERR | EPOLLHUP; + + sock_state->user_events = events; + sock_state->user_data = ev->data; + + if ((events & SOCK__KNOWN_EPOLL_EVENTS & ~sock_state->pending_events) != 0) + port_request_socket_update(port_state, sock_state); + + return 0; +} + +static inline DWORD sock__epoll_events_to_afd_events(uint32_t epoll_events) { + /* Always monitor for AFD_POLL_LOCAL_CLOSE, which is triggered when the + * socket is closed with closesocket() or CloseHandle(). */ + DWORD afd_events = AFD_POLL_LOCAL_CLOSE; + + if (epoll_events & (EPOLLIN | EPOLLRDNORM)) + afd_events |= AFD_POLL_RECEIVE | AFD_POLL_ACCEPT; + if (epoll_events & (EPOLLPRI | EPOLLRDBAND)) + afd_events |= AFD_POLL_RECEIVE_EXPEDITED; + if (epoll_events & (EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND)) + afd_events |= AFD_POLL_SEND; + if (epoll_events & (EPOLLIN | EPOLLRDNORM | EPOLLRDHUP)) + afd_events |= AFD_POLL_DISCONNECT; + if (epoll_events & EPOLLHUP) + afd_events |= AFD_POLL_ABORT; + if (epoll_events & EPOLLERR) + afd_events |= AFD_POLL_CONNECT_FAIL; + + return afd_events; +} + +static inline uint32_t sock__afd_events_to_epoll_events(DWORD afd_events) { + uint32_t epoll_events = 0; + + if (afd_events & (AFD_POLL_RECEIVE | AFD_POLL_ACCEPT)) + epoll_events |= EPOLLIN | EPOLLRDNORM; + if (afd_events & AFD_POLL_RECEIVE_EXPEDITED) + epoll_events |= EPOLLPRI | EPOLLRDBAND; + if (afd_events & AFD_POLL_SEND) + epoll_events |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + if (afd_events & AFD_POLL_DISCONNECT) + epoll_events |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; + if (afd_events & AFD_POLL_ABORT) + epoll_events |= EPOLLHUP; + if (afd_events & AFD_POLL_CONNECT_FAIL) + /* Linux reports all these events after connect() has failed. */ + epoll_events |= + EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLRDNORM | EPOLLWRNORM | EPOLLRDHUP; + + return epoll_events; +} + +int sock_update(port_state_t* port_state, sock_state_t* sock_state) { + assert(!sock_state->delete_pending); + + if ((sock_state->poll_status == SOCK__POLL_PENDING) && + (sock_state->user_events & SOCK__KNOWN_EPOLL_EVENTS & + ~sock_state->pending_events) == 0) { + /* All the events the user is interested in are already being monitored by + * the pending poll operation. It might spuriously complete because of an + * event that we're no longer interested in; when that happens we'll submit + * a new poll operation with the updated event mask. */ + + } else if (sock_state->poll_status == SOCK__POLL_PENDING) { + /* A poll operation is already pending, but it's not monitoring for all the + * events that the user is interested in. Therefore, cancel the pending + * poll operation; when we receive it's completion package, a new poll + * operation will be submitted with the correct event mask. */ + if (sock__cancel_poll(sock_state) < 0) + return -1; + + } else if (sock_state->poll_status == SOCK__POLL_CANCELLED) { + /* The poll operation has already been cancelled, we're still waiting for + * it to return. For now, there's nothing that needs to be done. */ + + } else if (sock_state->poll_status == SOCK__POLL_IDLE) { + /* No poll operation is pending; start one. */ + sock_state->poll_info.Exclusive = FALSE; + sock_state->poll_info.NumberOfHandles = 1; + sock_state->poll_info.Timeout.QuadPart = INT64_MAX; + sock_state->poll_info.Handles[0].Handle = (HANDLE) sock_state->base_socket; + sock_state->poll_info.Handles[0].Status = 0; + sock_state->poll_info.Handles[0].Events = + sock__epoll_events_to_afd_events(sock_state->user_events); + + memset(&sock_state->overlapped, 0, sizeof sock_state->overlapped); + + if (afd_poll(poll_group_get_afd_helper_handle(sock_state->poll_group), + &sock_state->poll_info, + &sock_state->overlapped) < 0) { + switch (GetLastError()) { + case ERROR_IO_PENDING: + /* Overlapped poll operation in progress; this is expected. */ + break; + case ERROR_INVALID_HANDLE: + /* Socket closed; it'll be dropped from the epoll set. */ + return sock__delete(port_state, sock_state, false); + default: + /* Other errors are propagated to the caller. */ + return_map_error(-1); + } + } + + /* The poll request was successfully submitted. */ + sock_state->poll_status = SOCK__POLL_PENDING; + sock_state->pending_events = sock_state->user_events; + + } else { + /* Unreachable. */ + assert(false); + } + + port_cancel_socket_update(port_state, sock_state); + return 0; +} + +int sock_feed_event(port_state_t* port_state, + OVERLAPPED* overlapped, + struct epoll_event* ev) { + sock_state_t* sock_state = + container_of(overlapped, sock_state_t, overlapped); + AFD_POLL_INFO* poll_info = &sock_state->poll_info; + uint32_t epoll_events = 0; + + sock_state->poll_status = SOCK__POLL_IDLE; + sock_state->pending_events = 0; + + if (sock_state->delete_pending) { + /* Socket has been deleted earlier and can now be freed. */ + return sock__delete(port_state, sock_state, false); + + } else if ((NTSTATUS) overlapped->Internal == STATUS_CANCELLED) { + /* The poll request was cancelled by CancelIoEx. */ + + } else if (!NT_SUCCESS(overlapped->Internal)) { + /* The overlapped request itself failed in an unexpected way. */ + epoll_events = EPOLLERR; + + } else if (poll_info->NumberOfHandles < 1) { + /* This poll operation succeeded but didn't report any socket events. */ + + } else if (poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) { + /* The poll operation reported that the socket was closed. */ + return sock__delete(port_state, sock_state, false); + + } else { + /* Events related to our socket were reported. */ + epoll_events = + sock__afd_events_to_epoll_events(poll_info->Handles[0].Events); + } + + /* Requeue the socket so a new poll request will be submitted. */ + port_request_socket_update(port_state, sock_state); + + /* Filter out events that the user didn't ask for. */ + epoll_events &= sock_state->user_events; + + /* Return if there are no epoll events to report. */ + if (epoll_events == 0) + return 0; + + /* If the the socket has the EPOLLONESHOT flag set, unmonitor all events, + * even EPOLLERR and EPOLLHUP. But always keep looking for closed sockets. */ + if (sock_state->user_events & EPOLLONESHOT) + sock_state->user_events = 0; + + ev->data = sock_state->user_data; + ev->events = epoll_events; + return 1; +} + +queue_node_t* sock_state_to_queue_node(sock_state_t* sock_state) { + return &sock_state->queue_node; +} + +sock_state_t* sock_state_from_tree_node(tree_node_t* tree_node) { + return container_of(tree_node, sock_state_t, tree_node); +} + +tree_node_t* sock_state_to_tree_node(sock_state_t* sock_state) { + return &sock_state->tree_node; +} + +sock_state_t* sock_state_from_queue_node(queue_node_t* queue_node) { + return container_of(queue_node, sock_state_t, queue_node); +} + +void ts_tree_init(ts_tree_t* ts_tree) { + tree_init(&ts_tree->tree); + InitializeSRWLock(&ts_tree->lock); +} + +void ts_tree_node_init(ts_tree_node_t* node) { + tree_node_init(&node->tree_node); + reflock_init(&node->reflock); +} + +int ts_tree_add(ts_tree_t* ts_tree, ts_tree_node_t* node, uintptr_t key) { + int r; + + AcquireSRWLockExclusive(&ts_tree->lock); + r = tree_add(&ts_tree->tree, &node->tree_node, key); + ReleaseSRWLockExclusive(&ts_tree->lock); + + return r; +} + +static inline ts_tree_node_t* ts_tree__find_node(ts_tree_t* ts_tree, + uintptr_t key) { + tree_node_t* tree_node = tree_find(&ts_tree->tree, key); + if (tree_node == NULL) + return NULL; + + return container_of(tree_node, ts_tree_node_t, tree_node); +} + +ts_tree_node_t* ts_tree_del_and_ref(ts_tree_t* ts_tree, uintptr_t key) { + ts_tree_node_t* ts_tree_node; + + AcquireSRWLockExclusive(&ts_tree->lock); + + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) { + tree_del(&ts_tree->tree, &ts_tree_node->tree_node); + reflock_ref(&ts_tree_node->reflock); + } + + ReleaseSRWLockExclusive(&ts_tree->lock); + + return ts_tree_node; +} + +ts_tree_node_t* ts_tree_find_and_ref(ts_tree_t* ts_tree, uintptr_t key) { + ts_tree_node_t* ts_tree_node; + + AcquireSRWLockShared(&ts_tree->lock); + + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) + reflock_ref(&ts_tree_node->reflock); + + ReleaseSRWLockShared(&ts_tree->lock); + + return ts_tree_node; +} + +void ts_tree_node_unref(ts_tree_node_t* node) { + reflock_unref(&node->reflock); +} + +void ts_tree_node_unref_and_destroy(ts_tree_node_t* node) { + reflock_unref_and_destroy(&node->reflock); +} + +void tree_init(tree_t* tree) { + memset(tree, 0, sizeof *tree); +} + +void tree_node_init(tree_node_t* node) { + memset(node, 0, sizeof *node); +} + +#define TREE__ROTATE(cis, trans) \ + tree_node_t* p = node; \ + tree_node_t* q = node->trans; \ + tree_node_t* parent = p->parent; \ + \ + if (parent) { \ + if (parent->left == p) \ + parent->left = q; \ + else \ + parent->right = q; \ + } else { \ + tree->root = q; \ + } \ + \ + q->parent = parent; \ + p->parent = q; \ + p->trans = q->cis; \ + if (p->trans) \ + p->trans->parent = p; \ + q->cis = p; + +static inline void tree__rotate_left(tree_t* tree, tree_node_t* node) { + TREE__ROTATE(left, right) +} + +static inline void tree__rotate_right(tree_t* tree, tree_node_t* node) { + TREE__ROTATE(right, left) +} + +#define TREE__INSERT_OR_DESCEND(side) \ + if (parent->side) { \ + parent = parent->side; \ + } else { \ + parent->side = node; \ + break; \ + } + +#define TREE__FIXUP_AFTER_INSERT(cis, trans) \ + tree_node_t* grandparent = parent->parent; \ + tree_node_t* uncle = grandparent->trans; \ + \ + if (uncle && uncle->red) { \ + parent->red = uncle->red = false; \ + grandparent->red = true; \ + node = grandparent; \ + } else { \ + if (node == parent->trans) { \ + tree__rotate_##cis(tree, parent); \ + node = parent; \ + parent = node->parent; \ + } \ + parent->red = false; \ + grandparent->red = true; \ + tree__rotate_##trans(tree, grandparent); \ + } + +int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) { + tree_node_t* parent; + + parent = tree->root; + if (parent) { + for (;;) { + if (key < parent->key) { + TREE__INSERT_OR_DESCEND(left) + } else if (key > parent->key) { + TREE__INSERT_OR_DESCEND(right) + } else { + return -1; + } + } + } else { + tree->root = node; + } + + node->key = key; + node->left = node->right = NULL; + node->parent = parent; + node->red = true; + + for (; parent && parent->red; parent = node->parent) { + if (parent == parent->parent->left) { + TREE__FIXUP_AFTER_INSERT(left, right) + } else { + TREE__FIXUP_AFTER_INSERT(right, left) + } + } + tree->root->red = false; + + return 0; +} + +#define TREE__FIXUP_AFTER_REMOVE(cis, trans) \ + tree_node_t* sibling = parent->trans; \ + \ + if (sibling->red) { \ + sibling->red = false; \ + parent->red = true; \ + tree__rotate_##cis(tree, parent); \ + sibling = parent->trans; \ + } \ + if ((sibling->left && sibling->left->red) || \ + (sibling->right && sibling->right->red)) { \ + if (!sibling->trans || !sibling->trans->red) { \ + sibling->cis->red = false; \ + sibling->red = true; \ + tree__rotate_##trans(tree, sibling); \ + sibling = parent->trans; \ + } \ + sibling->red = parent->red; \ + parent->red = sibling->trans->red = false; \ + tree__rotate_##cis(tree, parent); \ + node = tree->root; \ + break; \ + } \ + sibling->red = true; + +void tree_del(tree_t* tree, tree_node_t* node) { + tree_node_t* parent = node->parent; + tree_node_t* left = node->left; + tree_node_t* right = node->right; + tree_node_t* next; + bool red; + + if (!left) { + next = right; + } else if (!right) { + next = left; + } else { + next = right; + while (next->left) + next = next->left; + } + + if (parent) { + if (parent->left == node) + parent->left = next; + else + parent->right = next; + } else { + tree->root = next; + } + + if (left && right) { + red = next->red; + next->red = node->red; + next->left = left; + left->parent = next; + if (next != right) { + parent = next->parent; + next->parent = node->parent; + node = next->right; + parent->left = node; + next->right = right; + right->parent = next; + } else { + next->parent = parent; + parent = next; + node = next->right; + } + } else { + red = node->red; + node = next; + } + + if (node) + node->parent = parent; + if (red) + return; + if (node && node->red) { + node->red = false; + return; + } + + do { + if (node == tree->root) + break; + if (node == parent->left) { + TREE__FIXUP_AFTER_REMOVE(left, right) + } else { + TREE__FIXUP_AFTER_REMOVE(right, left) + } + node = parent; + parent = parent->parent; + } while (!node->red); + + if (node) + node->red = false; +} + +tree_node_t* tree_find(const tree_t* tree, uintptr_t key) { + tree_node_t* node = tree->root; + while (node) { + if (key < node->key) + node = node->left; + else if (key > node->key) + node = node->right; + else + return node; + } + return NULL; +} + +tree_node_t* tree_root(const tree_t* tree) { + return tree->root; +} + +#ifndef SIO_BASE_HANDLE +#define SIO_BASE_HANDLE 0x48000022 +#endif + +int ws_global_init(void) { + int r; + WSADATA wsa_data; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (r != 0) + return_set_error(-1, (DWORD) r); + + return 0; +} + +SOCKET ws_get_base_socket(SOCKET socket) { + SOCKET base_socket; + DWORD bytes; + + if (WSAIoctl(socket, + SIO_BASE_HANDLE, + NULL, + 0, + &base_socket, + sizeof base_socket, + &bytes, + NULL, + NULL) == SOCKET_ERROR) + return_map_error(INVALID_SOCKET); + + return base_socket; +} diff --git a/3rd/libzmq/external/wepoll/wepoll.h b/3rd/libzmq/external/wepoll/wepoll.h new file mode 100644 index 00000000..7b522984 --- /dev/null +++ b/3rd/libzmq/external/wepoll/wepoll.h @@ -0,0 +1,117 @@ +/* + * wepoll - epoll for Windows + * https://github.com/piscisaureus/wepoll + * + * Copyright 2012-2018, Bert Belder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WEPOLL_H_ +#define WEPOLL_H_ + +#ifndef WEPOLL_EXPORT +#define WEPOLL_EXPORT +#endif + +#include + +/* clang-format off */ + +enum EPOLL_EVENTS { + EPOLLIN = (int) (1U << 0), + EPOLLPRI = (int) (1U << 1), + EPOLLOUT = (int) (1U << 2), + EPOLLERR = (int) (1U << 3), + EPOLLHUP = (int) (1U << 4), + EPOLLRDNORM = (int) (1U << 6), + EPOLLRDBAND = (int) (1U << 7), + EPOLLWRNORM = (int) (1U << 8), + EPOLLWRBAND = (int) (1U << 9), + EPOLLMSG = (int) (1U << 10), /* Never reported. */ + EPOLLRDHUP = (int) (1U << 13), + EPOLLONESHOT = (int) (1U << 31) +}; + +#define EPOLLIN (1U << 0) +#define EPOLLPRI (1U << 1) +#define EPOLLOUT (1U << 2) +#define EPOLLERR (1U << 3) +#define EPOLLHUP (1U << 4) +#define EPOLLRDNORM (1U << 6) +#define EPOLLRDBAND (1U << 7) +#define EPOLLWRNORM (1U << 8) +#define EPOLLWRBAND (1U << 9) +#define EPOLLMSG (1U << 10) +#define EPOLLRDHUP (1U << 13) +#define EPOLLONESHOT (1U << 31) + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_MOD 2 +#define EPOLL_CTL_DEL 3 + +/* clang-format on */ + +typedef void* HANDLE; +typedef uintptr_t SOCKET; + +typedef union epoll_data { + void* ptr; + int fd; + uint32_t u32; + uint64_t u64; + SOCKET sock; /* Windows specific */ + HANDLE hnd; /* Windows specific */ +} epoll_data_t; + +struct epoll_event { + uint32_t events; /* Epoll events and flags */ + epoll_data_t data; /* User data variable */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +WEPOLL_EXPORT HANDLE epoll_create(int size); +WEPOLL_EXPORT HANDLE epoll_create1(int flags); + +WEPOLL_EXPORT int epoll_close(HANDLE ephnd); + +WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd, + int op, + SOCKET sock, + struct epoll_event* event); + +WEPOLL_EXPORT int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* WEPOLL_H_ */ diff --git a/3rd/libzmq/include/zmq.h b/3rd/libzmq/include/zmq.h new file mode 100644 index 00000000..d05659f7 --- /dev/null +++ b/3rd/libzmq/include/zmq.h @@ -0,0 +1,788 @@ +/* + 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 . + + ************************************************************************* + NOTE to contributors. This file comprises the principal public contract + for ZeroMQ API users. Any change to this file supplied in a stable + release SHOULD not break existing applications. + In practice this means that the value of constants must not change, and + that old values may not be reused for new constants. + ************************************************************************* +*/ + +#ifndef __ZMQ_H_INCLUDED__ +#define __ZMQ_H_INCLUDED__ + +/* Version macros for compile-time API version detection */ +#define ZMQ_VERSION_MAJOR 4 +#define ZMQ_VERSION_MINOR 3 +#define ZMQ_VERSION_PATCH 4 + +#define ZMQ_MAKE_VERSION(major, minor, patch) \ + ((major) *10000 + (minor) *100 + (patch)) +#define ZMQ_VERSION \ + ZMQ_MAKE_VERSION (ZMQ_VERSION_MAJOR, ZMQ_VERSION_MINOR, ZMQ_VERSION_PATCH) + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined _WIN32_WCE +#include +#endif +#include +#include +#if defined _WIN32 +// Set target version to Windows Server 2008, Windows Vista or higher. +// Windows XP (0x0501) is supported but without client & server socket types. +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#endif + +#ifdef __MINGW32__ +// Require Windows XP or higher with MinGW for getaddrinfo(). +#if (_WIN32_WINNT >= 0x0501) +#else +#error You need at least Windows XP target +#endif +#endif +#endif + +/* Handle DSO symbol visibility */ +#if defined _WIN32 +#if defined ZMQ_STATIC +#define ZMQ_EXPORT +#elif defined DLL_EXPORT +#define ZMQ_EXPORT __declspec(dllexport) +#else +#define ZMQ_EXPORT __declspec(dllimport) +#endif +#else +#if defined __SUNPRO_C || defined __SUNPRO_CC +#define ZMQ_EXPORT __global +#elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER +#define ZMQ_EXPORT __attribute__ ((visibility ("default"))) +#else +#define ZMQ_EXPORT +#endif +#endif + +/* Define integer types needed for event interface */ +#define ZMQ_DEFINED_STDINT 1 +#if defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_OPENVMS +#include +#elif defined _MSC_VER && _MSC_VER < 1600 +#ifndef uint64_t +typedef unsigned __int64 uint64_t; +#endif +#ifndef int32_t +typedef __int32 int32_t; +#endif +#ifndef uint32_t +typedef unsigned __int32 uint32_t; +#endif +#ifndef uint16_t +typedef unsigned __int16 uint16_t; +#endif +#ifndef uint8_t +typedef unsigned __int8 uint8_t; +#endif +#else +#include +#endif + +// 32-bit AIX's pollfd struct members are called reqevents and rtnevents so it +// defines compatibility macros for them. Need to include that header first to +// stop build failures since zmq_pollset_t defines them as events and revents. +#ifdef ZMQ_HAVE_AIX +#include +#endif + + +/******************************************************************************/ +/* 0MQ errors. */ +/******************************************************************************/ + +/* A number random enough not to collide with different errno ranges on */ +/* different OSes. The assumption is that error_t is at least 32-bit type. */ +#define ZMQ_HAUSNUMERO 156384712 + +/* On Windows platform some of the standard POSIX errnos are not defined. */ +#ifndef ENOTSUP +#define ENOTSUP (ZMQ_HAUSNUMERO + 1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (ZMQ_HAUSNUMERO + 2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (ZMQ_HAUSNUMERO + 3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (ZMQ_HAUSNUMERO + 4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (ZMQ_HAUSNUMERO + 5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (ZMQ_HAUSNUMERO + 6) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (ZMQ_HAUSNUMERO + 7) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (ZMQ_HAUSNUMERO + 8) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (ZMQ_HAUSNUMERO + 9) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (ZMQ_HAUSNUMERO + 10) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (ZMQ_HAUSNUMERO + 11) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (ZMQ_HAUSNUMERO + 12) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (ZMQ_HAUSNUMERO + 13) +#endif +#ifndef ECONNRESET +#define ECONNRESET (ZMQ_HAUSNUMERO + 14) +#endif +#ifndef ENOTCONN +#define ENOTCONN (ZMQ_HAUSNUMERO + 15) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (ZMQ_HAUSNUMERO + 16) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (ZMQ_HAUSNUMERO + 17) +#endif +#ifndef ENETRESET +#define ENETRESET (ZMQ_HAUSNUMERO + 18) +#endif + +/* Native 0MQ error codes. */ +#define EFSM (ZMQ_HAUSNUMERO + 51) +#define ENOCOMPATPROTO (ZMQ_HAUSNUMERO + 52) +#define ETERM (ZMQ_HAUSNUMERO + 53) +#define EMTHREAD (ZMQ_HAUSNUMERO + 54) + +/* This function retrieves the errno as it is known to 0MQ library. The goal */ +/* of this function is to make the code 100% portable, including where 0MQ */ +/* compiled with certain CRT library (on Windows) is linked to an */ +/* application that uses different CRT library. */ +ZMQ_EXPORT int zmq_errno (void); + +/* Resolves system errors and 0MQ errors to human-readable string. */ +ZMQ_EXPORT const char *zmq_strerror (int errnum_); + +/* Run-time API version detection */ +ZMQ_EXPORT void zmq_version (int *major_, int *minor_, int *patch_); + +/******************************************************************************/ +/* 0MQ infrastructure (a.k.a. context) initialisation & termination. */ +/******************************************************************************/ + +/* Context options */ +#define ZMQ_IO_THREADS 1 +#define ZMQ_MAX_SOCKETS 2 +#define ZMQ_SOCKET_LIMIT 3 +#define ZMQ_THREAD_PRIORITY 3 +#define ZMQ_THREAD_SCHED_POLICY 4 +#define ZMQ_MAX_MSGSZ 5 +#define ZMQ_MSG_T_SIZE 6 +#define ZMQ_THREAD_AFFINITY_CPU_ADD 7 +#define ZMQ_THREAD_AFFINITY_CPU_REMOVE 8 +#define ZMQ_THREAD_NAME_PREFIX 9 + +/* Default for new contexts */ +#define ZMQ_IO_THREADS_DFLT 1 +#define ZMQ_MAX_SOCKETS_DFLT 1023 +#define ZMQ_THREAD_PRIORITY_DFLT -1 +#define ZMQ_THREAD_SCHED_POLICY_DFLT -1 + +ZMQ_EXPORT void *zmq_ctx_new (void); +ZMQ_EXPORT int zmq_ctx_term (void *context_); +ZMQ_EXPORT int zmq_ctx_shutdown (void *context_); +ZMQ_EXPORT int zmq_ctx_set (void *context_, int option_, int optval_); +ZMQ_EXPORT int zmq_ctx_get (void *context_, int option_); + +/* Old (legacy) API */ +ZMQ_EXPORT void *zmq_init (int io_threads_); +ZMQ_EXPORT int zmq_term (void *context_); +ZMQ_EXPORT int zmq_ctx_destroy (void *context_); + + +/******************************************************************************/ +/* 0MQ message definition. */ +/******************************************************************************/ + +/* Some architectures, like sparc64 and some variants of aarch64, enforce pointer + * alignment and raise sigbus on violations. Make sure applications allocate + * zmq_msg_t on addresses aligned on a pointer-size boundary to avoid this issue. + */ +typedef struct zmq_msg_t +{ +#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) + __declspec(align (8)) unsigned char _[64]; +#elif defined(_MSC_VER) \ + && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE) || defined(_M_ARM)) + __declspec(align (4)) unsigned char _[64]; +#elif defined(__GNUC__) || defined(__INTEL_COMPILER) \ + || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590) \ + || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590) + unsigned char _[64] __attribute__ ((aligned (sizeof (void *)))); +#else + unsigned char _[64]; +#endif +} zmq_msg_t; + +typedef void(zmq_free_fn) (void *data_, void *hint_); + +ZMQ_EXPORT int zmq_msg_init (zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_init_size (zmq_msg_t *msg_, size_t size_); +ZMQ_EXPORT int zmq_msg_init_data ( + zmq_msg_t *msg_, void *data_, size_t size_, zmq_free_fn *ffn_, void *hint_); +ZMQ_EXPORT int zmq_msg_send (zmq_msg_t *msg_, void *s_, int flags_); +ZMQ_EXPORT int zmq_msg_recv (zmq_msg_t *msg_, void *s_, int flags_); +ZMQ_EXPORT int zmq_msg_close (zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_move (zmq_msg_t *dest_, zmq_msg_t *src_); +ZMQ_EXPORT int zmq_msg_copy (zmq_msg_t *dest_, zmq_msg_t *src_); +ZMQ_EXPORT void *zmq_msg_data (zmq_msg_t *msg_); +ZMQ_EXPORT size_t zmq_msg_size (const zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_more (const zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_get (const zmq_msg_t *msg_, int property_); +ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg_, int property_, int optval_); +ZMQ_EXPORT const char *zmq_msg_gets (const zmq_msg_t *msg_, + const char *property_); + +/******************************************************************************/ +/* 0MQ socket definition. */ +/******************************************************************************/ + +/* Socket types. */ +#define ZMQ_PAIR 0 +#define ZMQ_PUB 1 +#define ZMQ_SUB 2 +#define ZMQ_REQ 3 +#define ZMQ_REP 4 +#define ZMQ_DEALER 5 +#define ZMQ_ROUTER 6 +#define ZMQ_PULL 7 +#define ZMQ_PUSH 8 +#define ZMQ_XPUB 9 +#define ZMQ_XSUB 10 +#define ZMQ_STREAM 11 + +/* Deprecated aliases */ +#define ZMQ_XREQ ZMQ_DEALER +#define ZMQ_XREP ZMQ_ROUTER + +/* Socket options. */ +#define ZMQ_AFFINITY 4 +#define ZMQ_ROUTING_ID 5 +#define ZMQ_SUBSCRIBE 6 +#define ZMQ_UNSUBSCRIBE 7 +#define ZMQ_RATE 8 +#define ZMQ_RECOVERY_IVL 9 +#define ZMQ_SNDBUF 11 +#define ZMQ_RCVBUF 12 +#define ZMQ_RCVMORE 13 +#define ZMQ_FD 14 +#define ZMQ_EVENTS 15 +#define ZMQ_TYPE 16 +#define ZMQ_LINGER 17 +#define ZMQ_RECONNECT_IVL 18 +#define ZMQ_BACKLOG 19 +#define ZMQ_RECONNECT_IVL_MAX 21 +#define ZMQ_MAXMSGSIZE 22 +#define ZMQ_SNDHWM 23 +#define ZMQ_RCVHWM 24 +#define ZMQ_MULTICAST_HOPS 25 +#define ZMQ_RCVTIMEO 27 +#define ZMQ_SNDTIMEO 28 +#define ZMQ_LAST_ENDPOINT 32 +#define ZMQ_ROUTER_MANDATORY 33 +#define ZMQ_TCP_KEEPALIVE 34 +#define ZMQ_TCP_KEEPALIVE_CNT 35 +#define ZMQ_TCP_KEEPALIVE_IDLE 36 +#define ZMQ_TCP_KEEPALIVE_INTVL 37 +#define ZMQ_IMMEDIATE 39 +#define ZMQ_XPUB_VERBOSE 40 +#define ZMQ_ROUTER_RAW 41 +#define ZMQ_IPV6 42 +#define ZMQ_MECHANISM 43 +#define ZMQ_PLAIN_SERVER 44 +#define ZMQ_PLAIN_USERNAME 45 +#define ZMQ_PLAIN_PASSWORD 46 +#define ZMQ_CURVE_SERVER 47 +#define ZMQ_CURVE_PUBLICKEY 48 +#define ZMQ_CURVE_SECRETKEY 49 +#define ZMQ_CURVE_SERVERKEY 50 +#define ZMQ_PROBE_ROUTER 51 +#define ZMQ_REQ_CORRELATE 52 +#define ZMQ_REQ_RELAXED 53 +#define ZMQ_CONFLATE 54 +#define ZMQ_ZAP_DOMAIN 55 +#define ZMQ_ROUTER_HANDOVER 56 +#define ZMQ_TOS 57 +#define ZMQ_CONNECT_ROUTING_ID 61 +#define ZMQ_GSSAPI_SERVER 62 +#define ZMQ_GSSAPI_PRINCIPAL 63 +#define ZMQ_GSSAPI_SERVICE_PRINCIPAL 64 +#define ZMQ_GSSAPI_PLAINTEXT 65 +#define ZMQ_HANDSHAKE_IVL 66 +#define ZMQ_SOCKS_PROXY 68 +#define ZMQ_XPUB_NODROP 69 +#define ZMQ_BLOCKY 70 +#define ZMQ_XPUB_MANUAL 71 +#define ZMQ_XPUB_WELCOME_MSG 72 +#define ZMQ_STREAM_NOTIFY 73 +#define ZMQ_INVERT_MATCHING 74 +#define ZMQ_HEARTBEAT_IVL 75 +#define ZMQ_HEARTBEAT_TTL 76 +#define ZMQ_HEARTBEAT_TIMEOUT 77 +#define ZMQ_XPUB_VERBOSER 78 +#define ZMQ_CONNECT_TIMEOUT 79 +#define ZMQ_TCP_MAXRT 80 +#define ZMQ_THREAD_SAFE 81 +#define ZMQ_MULTICAST_MAXTPDU 84 +#define ZMQ_VMCI_BUFFER_SIZE 85 +#define ZMQ_VMCI_BUFFER_MIN_SIZE 86 +#define ZMQ_VMCI_BUFFER_MAX_SIZE 87 +#define ZMQ_VMCI_CONNECT_TIMEOUT 88 +#define ZMQ_USE_FD 89 +#define ZMQ_GSSAPI_PRINCIPAL_NAMETYPE 90 +#define ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE 91 +#define ZMQ_BINDTODEVICE 92 + +/* Message options */ +#define ZMQ_MORE 1 +#define ZMQ_SHARED 3 + +/* Send/recv options. */ +#define ZMQ_DONTWAIT 1 +#define ZMQ_SNDMORE 2 + +/* Security mechanisms */ +#define ZMQ_NULL 0 +#define ZMQ_PLAIN 1 +#define ZMQ_CURVE 2 +#define ZMQ_GSSAPI 3 + +/* RADIO-DISH protocol */ +#define ZMQ_GROUP_MAX_LENGTH 255 + +/* Deprecated options and aliases */ +#define ZMQ_IDENTITY ZMQ_ROUTING_ID +#define ZMQ_CONNECT_RID ZMQ_CONNECT_ROUTING_ID +#define ZMQ_TCP_ACCEPT_FILTER 38 +#define ZMQ_IPC_FILTER_PID 58 +#define ZMQ_IPC_FILTER_UID 59 +#define ZMQ_IPC_FILTER_GID 60 +#define ZMQ_IPV4ONLY 31 +#define ZMQ_DELAY_ATTACH_ON_CONNECT ZMQ_IMMEDIATE +#define ZMQ_NOBLOCK ZMQ_DONTWAIT +#define ZMQ_FAIL_UNROUTABLE ZMQ_ROUTER_MANDATORY +#define ZMQ_ROUTER_BEHAVIOR ZMQ_ROUTER_MANDATORY + +/* Deprecated Message options */ +#define ZMQ_SRCFD 2 + +/******************************************************************************/ +/* GSSAPI definitions */ +/******************************************************************************/ + +/* GSSAPI principal name types */ +#define ZMQ_GSSAPI_NT_HOSTBASED 0 +#define ZMQ_GSSAPI_NT_USER_NAME 1 +#define ZMQ_GSSAPI_NT_KRB5_PRINCIPAL 2 + +/******************************************************************************/ +/* 0MQ socket events and monitoring */ +/******************************************************************************/ + +/* Socket transport events (TCP, IPC and TIPC only) */ + +#define ZMQ_EVENT_CONNECTED 0x0001 +#define ZMQ_EVENT_CONNECT_DELAYED 0x0002 +#define ZMQ_EVENT_CONNECT_RETRIED 0x0004 +#define ZMQ_EVENT_LISTENING 0x0008 +#define ZMQ_EVENT_BIND_FAILED 0x0010 +#define ZMQ_EVENT_ACCEPTED 0x0020 +#define ZMQ_EVENT_ACCEPT_FAILED 0x0040 +#define ZMQ_EVENT_CLOSED 0x0080 +#define ZMQ_EVENT_CLOSE_FAILED 0x0100 +#define ZMQ_EVENT_DISCONNECTED 0x0200 +#define ZMQ_EVENT_MONITOR_STOPPED 0x0400 +#define ZMQ_EVENT_ALL 0xFFFF +/* Unspecified system errors during handshake. Event value is an errno. */ +#define ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL 0x0800 +/* Handshake complete successfully with successful authentication (if * + * enabled). Event value is unused. */ +#define ZMQ_EVENT_HANDSHAKE_SUCCEEDED 0x1000 +/* Protocol errors between ZMTP peers or between server and ZAP handler. * + * Event value is one of ZMQ_PROTOCOL_ERROR_* */ +#define ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL 0x2000 +/* Failed authentication requests. Event value is the numeric ZAP status * + * code, i.e. 300, 400 or 500. */ +#define ZMQ_EVENT_HANDSHAKE_FAILED_AUTH 0x4000 +#define ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED 0x10000000 +#define ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND 0x10000001 +#define ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE 0x10000002 +#define ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE 0x10000003 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED 0x10000011 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE 0x10000012 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO 0x10000013 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE 0x10000014 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR 0x10000015 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY 0x10000016 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME 0x10000017 +#define ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA 0x10000018 +// the following two may be due to erroneous configuration of a peer +#define ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC 0x11000001 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH 0x11000002 +#define ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED 0x20000000 +#define ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY 0x20000001 +#define ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID 0x20000002 +#define ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION 0x20000003 +#define ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE 0x20000004 +#define ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA 0x20000005 +#define ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED 0x30000000 + +ZMQ_EXPORT void *zmq_socket (void *, int type_); +ZMQ_EXPORT int zmq_close (void *s_); +ZMQ_EXPORT int +zmq_setsockopt (void *s_, int option_, const void *optval_, size_t optvallen_); +ZMQ_EXPORT int +zmq_getsockopt (void *s_, int option_, void *optval_, size_t *optvallen_); +ZMQ_EXPORT int zmq_bind (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_connect (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_unbind (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_disconnect (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_send (void *s_, const void *buf_, size_t len_, int flags_); +ZMQ_EXPORT int +zmq_send_const (void *s_, const void *buf_, size_t len_, int flags_); +ZMQ_EXPORT int zmq_recv (void *s_, void *buf_, size_t len_, int flags_); +ZMQ_EXPORT int zmq_socket_monitor (void *s_, const char *addr_, int events_); + +/******************************************************************************/ +/* Hide socket fd type; this was before zmq_poller_event_t typedef below */ +/******************************************************************************/ + +#if defined _WIN32 +// Windows uses a pointer-sized unsigned integer to store the socket fd. +#if defined _WIN64 +typedef unsigned __int64 zmq_fd_t; +#else +typedef unsigned int zmq_fd_t; +#endif +#else +typedef int zmq_fd_t; +#endif + +/******************************************************************************/ +/* Deprecated I/O multiplexing. Prefer using zmq_poller API */ +/******************************************************************************/ + +#define ZMQ_POLLIN 1 +#define ZMQ_POLLOUT 2 +#define ZMQ_POLLERR 4 +#define ZMQ_POLLPRI 8 + +typedef struct zmq_pollitem_t +{ + void *socket; + zmq_fd_t fd; + short events; + short revents; +} zmq_pollitem_t; + +#define ZMQ_POLLITEMS_DFLT 16 + +ZMQ_EXPORT int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_); + +/******************************************************************************/ +/* Message proxying */ +/******************************************************************************/ + +ZMQ_EXPORT int zmq_proxy (void *frontend_, void *backend_, void *capture_); +ZMQ_EXPORT int zmq_proxy_steerable (void *frontend_, + void *backend_, + void *capture_, + void *control_); + +/******************************************************************************/ +/* Probe library capabilities */ +/******************************************************************************/ + +#define ZMQ_HAS_CAPABILITIES 1 +ZMQ_EXPORT int zmq_has (const char *capability_); + +/* Deprecated aliases */ +#define ZMQ_STREAMER 1 +#define ZMQ_FORWARDER 2 +#define ZMQ_QUEUE 3 + +/* Deprecated methods */ +ZMQ_EXPORT int zmq_device (int type_, void *frontend_, void *backend_); +ZMQ_EXPORT int zmq_sendmsg (void *s_, zmq_msg_t *msg_, int flags_); +ZMQ_EXPORT int zmq_recvmsg (void *s_, zmq_msg_t *msg_, int flags_); +struct iovec; +ZMQ_EXPORT int +zmq_sendiov (void *s_, struct iovec *iov_, size_t count_, int flags_); +ZMQ_EXPORT int +zmq_recviov (void *s_, struct iovec *iov_, size_t *count_, int flags_); + +/******************************************************************************/ +/* Encryption functions */ +/******************************************************************************/ + +/* Encode data with Z85 encoding. Returns encoded data */ +ZMQ_EXPORT char * +zmq_z85_encode (char *dest_, const uint8_t *data_, size_t size_); + +/* Decode data with Z85 encoding. Returns decoded data */ +ZMQ_EXPORT uint8_t *zmq_z85_decode (uint8_t *dest_, const char *string_); + +/* Generate z85-encoded public and private keypair with tweetnacl/libsodium. */ +/* Returns 0 on success. */ +ZMQ_EXPORT int zmq_curve_keypair (char *z85_public_key_, char *z85_secret_key_); + +/* Derive the z85-encoded public key from the z85-encoded secret key. */ +/* Returns 0 on success. */ +ZMQ_EXPORT int zmq_curve_public (char *z85_public_key_, + const char *z85_secret_key_); + +/******************************************************************************/ +/* Atomic utility methods */ +/******************************************************************************/ + +ZMQ_EXPORT void *zmq_atomic_counter_new (void); +ZMQ_EXPORT void zmq_atomic_counter_set (void *counter_, int value_); +ZMQ_EXPORT int zmq_atomic_counter_inc (void *counter_); +ZMQ_EXPORT int zmq_atomic_counter_dec (void *counter_); +ZMQ_EXPORT int zmq_atomic_counter_value (void *counter_); +ZMQ_EXPORT void zmq_atomic_counter_destroy (void **counter_p_); + +/******************************************************************************/ +/* Scheduling timers */ +/******************************************************************************/ + +#define ZMQ_HAVE_TIMERS + +typedef void(zmq_timer_fn) (int timer_id, void *arg); + +ZMQ_EXPORT void *zmq_timers_new (void); +ZMQ_EXPORT int zmq_timers_destroy (void **timers_p); +ZMQ_EXPORT int +zmq_timers_add (void *timers, size_t interval, zmq_timer_fn handler, void *arg); +ZMQ_EXPORT int zmq_timers_cancel (void *timers, int timer_id); +ZMQ_EXPORT int +zmq_timers_set_interval (void *timers, int timer_id, size_t interval); +ZMQ_EXPORT int zmq_timers_reset (void *timers, int timer_id); +ZMQ_EXPORT long zmq_timers_timeout (void *timers); +ZMQ_EXPORT int zmq_timers_execute (void *timers); + + +/******************************************************************************/ +/* These functions are not documented by man pages -- use at your own risk. */ +/* If you need these to be part of the formal ZMQ API, then (a) write a man */ +/* page, and (b) write a test case in tests. */ +/******************************************************************************/ + +/* Helper functions are used by perf tests so that they don't have to care */ +/* about minutiae of time-related functions on different OS platforms. */ + +/* Starts the stopwatch. Returns the handle to the watch. */ +ZMQ_EXPORT void *zmq_stopwatch_start (void); + +/* Returns the number of microseconds elapsed since the stopwatch was */ +/* started, but does not stop or deallocate the stopwatch. */ +ZMQ_EXPORT unsigned long zmq_stopwatch_intermediate (void *watch_); + +/* Stops the stopwatch. Returns the number of microseconds elapsed since */ +/* the stopwatch was started, and deallocates that watch. */ +ZMQ_EXPORT unsigned long zmq_stopwatch_stop (void *watch_); + +/* Sleeps for specified number of seconds. */ +ZMQ_EXPORT void zmq_sleep (int seconds_); + +typedef void(zmq_thread_fn) (void *); + +/* Start a thread. Returns a handle to the thread. */ +ZMQ_EXPORT void *zmq_threadstart (zmq_thread_fn *func_, void *arg_); + +/* Wait for thread to complete then free up resources. */ +ZMQ_EXPORT void zmq_threadclose (void *thread_); + + +/******************************************************************************/ +/* These functions are DRAFT and disabled in stable releases, and subject to */ +/* change at ANY time until declared stable. */ +/******************************************************************************/ + +#ifdef ZMQ_BUILD_DRAFT_API + +/* DRAFT Socket types. */ +#define ZMQ_SERVER 12 +#define ZMQ_CLIENT 13 +#define ZMQ_RADIO 14 +#define ZMQ_DISH 15 +#define ZMQ_GATHER 16 +#define ZMQ_SCATTER 17 +#define ZMQ_DGRAM 18 +#define ZMQ_PEER 19 +#define ZMQ_CHANNEL 20 + +/* DRAFT Socket options. */ +#define ZMQ_ZAP_ENFORCE_DOMAIN 93 +#define ZMQ_LOOPBACK_FASTPATH 94 +#define ZMQ_METADATA 95 +#define ZMQ_MULTICAST_LOOP 96 +#define ZMQ_ROUTER_NOTIFY 97 +#define ZMQ_XPUB_MANUAL_LAST_VALUE 98 +#define ZMQ_SOCKS_USERNAME 99 +#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 +#define ZMQ_ONLY_FIRST_SUBSCRIBE 108 +#define ZMQ_RECONNECT_STOP 109 +#define ZMQ_HELLO_MSG 110 +#define ZMQ_DISCONNECT_MSG 111 +#define ZMQ_PRIORITY 112 + +/* DRAFT ZMQ_RECONNECT_STOP options */ +#define ZMQ_RECONNECT_STOP_CONN_REFUSED 0x1 +#define ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED 0x2 +#define ZMQ_RECONNECT_STOP_AFTER_DISCONNECT 0x3 + +/* DRAFT Context options */ +#define ZMQ_ZERO_COPY_RECV 10 + +/* DRAFT Context methods. */ +ZMQ_EXPORT int zmq_ctx_set_ext (void *context_, + int option_, + const void *optval_, + size_t optvallen_); +ZMQ_EXPORT int zmq_ctx_get_ext (void *context_, + int option_, + void *optval_, + size_t *optvallen_); + +/* DRAFT Socket methods. */ +ZMQ_EXPORT int zmq_join (void *s, const char *group); +ZMQ_EXPORT int zmq_leave (void *s, const char *group); +ZMQ_EXPORT uint32_t zmq_connect_peer (void *s_, const char *addr_); + +/* DRAFT Msg methods. */ +ZMQ_EXPORT int zmq_msg_set_routing_id (zmq_msg_t *msg, uint32_t routing_id); +ZMQ_EXPORT uint32_t zmq_msg_routing_id (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_set_group (zmq_msg_t *msg, const char *group); +ZMQ_EXPORT const char *zmq_msg_group (zmq_msg_t *msg); +ZMQ_EXPORT int +zmq_msg_init_buffer (zmq_msg_t *msg_, const void *buf_, size_t size_); + +/* DRAFT Msg property names. */ +#define ZMQ_MSG_PROPERTY_ROUTING_ID "Routing-Id" +#define ZMQ_MSG_PROPERTY_SOCKET_TYPE "Socket-Type" +#define ZMQ_MSG_PROPERTY_USER_ID "User-Id" +#define ZMQ_MSG_PROPERTY_PEER_ADDRESS "Peer-Address" + +/* Router notify options */ +#define ZMQ_NOTIFY_CONNECT 1 +#define ZMQ_NOTIFY_DISCONNECT 2 + +/******************************************************************************/ +/* Poller polling on sockets,fd and thread-safe sockets */ +/******************************************************************************/ + +#define ZMQ_HAVE_POLLER + +typedef struct zmq_poller_event_t +{ + void *socket; + zmq_fd_t fd; + void *user_data; + short events; +} zmq_poller_event_t; + +ZMQ_EXPORT void *zmq_poller_new (void); +ZMQ_EXPORT int zmq_poller_destroy (void **poller_p); +ZMQ_EXPORT int zmq_poller_size (void *poller); +ZMQ_EXPORT int +zmq_poller_add (void *poller, void *socket, void *user_data, short events); +ZMQ_EXPORT int zmq_poller_modify (void *poller, void *socket, short events); +ZMQ_EXPORT int zmq_poller_remove (void *poller, void *socket); +ZMQ_EXPORT int +zmq_poller_wait (void *poller, zmq_poller_event_t *event, long timeout); +ZMQ_EXPORT int zmq_poller_wait_all (void *poller, + zmq_poller_event_t *events, + int n_events, + long timeout); +ZMQ_EXPORT int zmq_poller_fd (void *poller, zmq_fd_t *fd); + +ZMQ_EXPORT int +zmq_poller_add_fd (void *poller, zmq_fd_t fd, void *user_data, short events); +ZMQ_EXPORT int zmq_poller_modify_fd (void *poller, zmq_fd_t fd, short events); +ZMQ_EXPORT int zmq_poller_remove_fd (void *poller, zmq_fd_t fd); + +ZMQ_EXPORT int zmq_socket_get_peer_state (void *socket, + const void *routing_id, + size_t routing_id_size); + +/* DRAFT Socket monitoring events */ +#define ZMQ_EVENT_PIPES_STATS 0x10000 + +#define ZMQ_CURRENT_EVENT_VERSION 1 +#define ZMQ_CURRENT_EVENT_VERSION_DRAFT 2 + +#define ZMQ_EVENT_ALL_V1 ZMQ_EVENT_ALL +#define ZMQ_EVENT_ALL_V2 ZMQ_EVENT_ALL_V1 | ZMQ_EVENT_PIPES_STATS + +ZMQ_EXPORT int zmq_socket_monitor_versioned ( + void *s_, const char *addr_, uint64_t events_, int event_version_, int type_); +ZMQ_EXPORT int zmq_socket_monitor_pipes_stats (void *s); + +#endif // ZMQ_BUILD_DRAFT_API + + +#undef ZMQ_EXPORT + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd/libzmq/include/zmq_utils.h b/3rd/libzmq/include/zmq_utils.h new file mode 100644 index 00000000..b9398d57 --- /dev/null +++ b/3rd/libzmq/include/zmq_utils.h @@ -0,0 +1,50 @@ +/* + 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 . +*/ + +/* This file is deprecated, and all its functionality provided by zmq.h */ +/* Note that -Wpedantic compilation requires GCC to avoid using its custom + extensions such as #warning, hence the trick below. Also, pragmas for + warnings or other messages are not standard, not portable, and not all + compilers even have an equivalent concept. + So in the worst case, this include file is treated as silently empty. */ + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) \ + || defined(_MSC_VER) +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wcpp" +#pragma GCC diagnostic ignored "-Werror" +#pragma GCC diagnostic ignored "-Wall" +#endif +#pragma message( \ + "Warning: zmq_utils.h is deprecated. All its functionality is provided by zmq.h.") +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif +#endif diff --git a/3rd/libzmq/installer.ico b/3rd/libzmq/installer.ico new file mode 100644 index 00000000..ba2709c5 Binary files /dev/null and b/3rd/libzmq/installer.ico differ diff --git a/3rd/libzmq/src/address.cpp b/3rd/libzmq/src/address.cpp new file mode 100644 index 00000000..9ec83c2f --- /dev/null +++ b/3rd/libzmq/src/address.cpp @@ -0,0 +1,143 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "address.hpp" +#include "ctx.hpp" +#include "err.hpp" +#include "tcp_address.hpp" +#include "udp_address.hpp" +#include "ipc_address.hpp" +#include "tipc_address.hpp" +#include "ws_address.hpp" + +#if defined ZMQ_HAVE_VMCI +#include "vmci_address.hpp" +#endif + +#include +#include + +zmq::address_t::address_t (const std::string &protocol_, + const std::string &address_, + ctx_t *parent_) : + protocol (protocol_), + address (address_), + parent (parent_) +{ + resolved.dummy = NULL; +} + +zmq::address_t::~address_t () +{ + if (protocol == protocol_name::tcp) { + LIBZMQ_DELETE (resolved.tcp_addr); + } else if (protocol == protocol_name::udp) { + LIBZMQ_DELETE (resolved.udp_addr); + } +#ifdef ZMQ_HAVE_WS + else if (protocol == protocol_name::ws) { + LIBZMQ_DELETE (resolved.ws_addr); + } +#endif + +#ifdef ZMQ_HAVE_WSS + else if (protocol == protocol_name::wss) { + LIBZMQ_DELETE (resolved.ws_addr); + } +#endif + +#if defined ZMQ_HAVE_IPC + else if (protocol == protocol_name::ipc) { + LIBZMQ_DELETE (resolved.ipc_addr); + } +#endif +#if defined ZMQ_HAVE_TIPC + else if (protocol == protocol_name::tipc) { + LIBZMQ_DELETE (resolved.tipc_addr); + } +#endif +#if defined ZMQ_HAVE_VMCI + else if (protocol == protocol_name::vmci) { + LIBZMQ_DELETE (resolved.vmci_addr); + } +#endif +} + +int zmq::address_t::to_string (std::string &addr_) const +{ + if (protocol == protocol_name::tcp && resolved.tcp_addr) + return resolved.tcp_addr->to_string (addr_); + if (protocol == protocol_name::udp && resolved.udp_addr) + return resolved.udp_addr->to_string (addr_); +#ifdef ZMQ_HAVE_WS + 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_IPC + if (protocol == protocol_name::ipc && resolved.ipc_addr) + return resolved.ipc_addr->to_string (addr_); +#endif +#if defined ZMQ_HAVE_TIPC + if (protocol == protocol_name::tipc && resolved.tipc_addr) + return resolved.tipc_addr->to_string (addr_); +#endif +#if defined ZMQ_HAVE_VMCI + if (protocol == protocol_name::vmci && resolved.vmci_addr) + return resolved.vmci_addr->to_string (addr_); +#endif + + if (!protocol.empty () && !address.empty ()) { + std::stringstream s; + s << protocol << "://" << address; + addr_ = s.str (); + return 0; + } + addr_.clear (); + return -1; +} + +zmq::zmq_socklen_t zmq::get_socket_address (fd_t fd_, + socket_end_t socket_end_, + sockaddr_storage *ss_) +{ + zmq_socklen_t sl = static_cast (sizeof (*ss_)); + + const int rc = + socket_end_ == socket_end_local + ? getsockname (fd_, reinterpret_cast (ss_), &sl) + : getpeername (fd_, reinterpret_cast (ss_), &sl); + + return rc != 0 ? 0 : sl; +} diff --git a/3rd/libzmq/src/address.hpp b/3rd/libzmq/src/address.hpp new file mode 100644 index 00000000..3c0a51de --- /dev/null +++ b/3rd/libzmq/src/address.hpp @@ -0,0 +1,162 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_ADDRESS_HPP_INCLUDED__ + +#include "fd.hpp" + +#include + +#ifndef ZMQ_HAVE_WINDOWS +#include +#else +#include +#endif + +namespace zmq +{ +class ctx_t; +class tcp_address_t; +class udp_address_t; +class ws_address_t; +#ifdef ZMQ_HAVE_WSS +class wss_address_t; +#endif +#if defined ZMQ_HAVE_IPC +class ipc_address_t; +#endif +#if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_VXWORKS +class tipc_address_t; +#endif +#if defined ZMQ_HAVE_VMCI +class vmci_address_t; +#endif + +namespace protocol_name +{ +static const char inproc[] = "inproc"; +static const char tcp[] = "tcp"; +static const char udp[] = "udp"; +#ifdef ZMQ_HAVE_OPENPGM +static const char pgm[] = "pgm"; +static const char epgm[] = "epgm"; +#endif +#ifdef ZMQ_HAVE_NORM +static const char norm[] = "norm"; +#endif +#ifdef ZMQ_HAVE_WS +static const char ws[] = "ws"; +#endif +#ifdef ZMQ_HAVE_WSS +static const char wss[] = "wss"; +#endif +#if defined ZMQ_HAVE_IPC +static const char ipc[] = "ipc"; +#endif +#if defined ZMQ_HAVE_TIPC +static const char tipc[] = "tipc"; +#endif +#if defined ZMQ_HAVE_VMCI +static const char vmci[] = "vmci"; +#endif +} + +struct address_t +{ + address_t (const std::string &protocol_, + const std::string &address_, + ctx_t *parent_); + + ~address_t (); + + const std::string protocol; + const std::string address; + ctx_t *const parent; + + // Protocol specific resolved address + // All members must be pointers to allow for consistent initialization + union + { + void *dummy; + tcp_address_t *tcp_addr; + udp_address_t *udp_addr; +#ifdef ZMQ_HAVE_WS + ws_address_t *ws_addr; +#endif +#ifdef ZMQ_HAVE_WSS + wss_address_t *wss_addr; +#endif +#if defined ZMQ_HAVE_IPC + ipc_address_t *ipc_addr; +#endif +#if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_VXWORKS + tipc_address_t *tipc_addr; +#endif +#if defined ZMQ_HAVE_VMCI + vmci_address_t *vmci_addr; +#endif + } resolved; + + int to_string (std::string &addr_) const; +}; + +#if defined(ZMQ_HAVE_HPUX) || defined(ZMQ_HAVE_VXWORKS) \ + || defined(ZMQ_HAVE_WINDOWS) +typedef int zmq_socklen_t; +#else +typedef socklen_t zmq_socklen_t; +#endif + +enum socket_end_t +{ + socket_end_local, + socket_end_remote +}; + +zmq_socklen_t +get_socket_address (fd_t fd_, socket_end_t socket_end_, sockaddr_storage *ss_); + +template +std::string get_socket_name (fd_t fd_, socket_end_t socket_end_) +{ + struct sockaddr_storage ss; + const zmq_socklen_t sl = get_socket_address (fd_, socket_end_, &ss); + if (sl == 0) { + return std::string (); + } + + const T addr (reinterpret_cast (&ss), sl); + std::string address_string; + addr.to_string (address_string); + return address_string; +} +} + +#endif diff --git a/3rd/libzmq/src/array.hpp b/3rd/libzmq/src/array.hpp new file mode 100644 index 00000000..8c813824 --- /dev/null +++ b/3rd/libzmq/src/array.hpp @@ -0,0 +1,140 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ARRAY_INCLUDED__ +#define __ZMQ_ARRAY_INCLUDED__ + +#include +#include + +#include "macros.hpp" + +namespace zmq +{ +// Implementation of fast arrays with O(1) access, insertion and +// removal. The array stores pointers rather than objects. +// O(1) is achieved by making items inheriting from +// array_item_t class which internally stores the position +// in the array. +// The ID template argument is used to differentiate among arrays +// and thus let an object be stored in different arrays. + +// Base class for objects stored in the array. If you want to store +// same object in multiple arrays, each of those arrays has to have +// different ID. The item itself has to be derived from instantiations of +// array_item_t template for all relevant IDs. + +template class array_item_t +{ + public: + array_item_t () : _array_index (-1) {} + + // The destructor doesn't have to be virtual. It is made virtual + // just to keep ICC and code checking tools from complaining. + virtual ~array_item_t () ZMQ_DEFAULT; + + void set_array_index (int index_) { _array_index = index_; } + + int get_array_index () const { return _array_index; } + + private: + int _array_index; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (array_item_t) +}; + + +template class array_t +{ + private: + typedef array_item_t item_t; + + public: + typedef typename std::vector::size_type size_type; + + array_t () ZMQ_DEFAULT; + + size_type size () { return _items.size (); } + + bool empty () { return _items.empty (); } + + T *&operator[] (size_type index_) { return _items[index_]; } + + void push_back (T *item_) + { + if (item_) + static_cast (item_)->set_array_index ( + static_cast (_items.size ())); + _items.push_back (item_); + } + + void erase (T *item_) + { + erase (static_cast (item_)->get_array_index ()); + } + + void erase (size_type index_) + { + if (_items.empty ()) + return; + static_cast (_items.back ()) + ->set_array_index (static_cast (index_)); + + _items[index_] = _items.back (); + _items.pop_back (); + } + + void swap (size_type index1_, size_type index2_) + { + if (_items[index1_]) + static_cast (_items[index1_]) + ->set_array_index (static_cast (index2_)); + if (_items[index2_]) + static_cast (_items[index2_]) + ->set_array_index (static_cast (index1_)); + std::swap (_items[index1_], _items[index2_]); + } + + void clear () { _items.clear (); } + + static size_type index (T *item_) + { + return static_cast ( + static_cast (item_)->get_array_index ()); + } + + private: + typedef std::vector items_t; + items_t _items; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (array_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/atomic_counter.hpp b/3rd/libzmq/src/atomic_counter.hpp new file mode 100644 index 00000000..d05837a1 --- /dev/null +++ b/3rd/libzmq/src/atomic_counter.hpp @@ -0,0 +1,235 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__ +#define __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__ + +#include "stdint.hpp" +#include "macros.hpp" + +#if defined ZMQ_FORCE_MUTEXES +#define ZMQ_ATOMIC_COUNTER_MUTEX +#elif (defined __cplusplus && __cplusplus >= 201103L) \ + || (defined _MSC_VER && _MSC_VER >= 1900) +#define ZMQ_ATOMIC_COUNTER_CXX11 +#elif defined ZMQ_HAVE_ATOMIC_INTRINSICS +#define ZMQ_ATOMIC_COUNTER_INTRINSIC +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZMQ_ATOMIC_COUNTER_X86 +#elif defined __ARM_ARCH_7A__ && defined __GNUC__ +#define ZMQ_ATOMIC_COUNTER_ARM +#elif defined ZMQ_HAVE_WINDOWS +#define ZMQ_ATOMIC_COUNTER_WINDOWS +#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD \ + || defined ZMQ_HAVE_GNU) +#define ZMQ_ATOMIC_COUNTER_ATOMIC_H +#elif defined __tile__ +#define ZMQ_ATOMIC_COUNTER_TILE +#else +#define ZMQ_ATOMIC_COUNTER_MUTEX +#endif + +#if defined ZMQ_ATOMIC_COUNTER_MUTEX +#include "mutex.hpp" +#elif defined ZMQ_ATOMIC_COUNTER_CXX11 +#include +#elif defined ZMQ_ATOMIC_COUNTER_WINDOWS +#include "windows.hpp" +#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H +#include +#elif defined ZMQ_ATOMIC_COUNTER_TILE +#include +#endif + +namespace zmq +{ +// This class represents an integer that can be incremented/decremented +// in atomic fashion. +// +// In zmq::shared_message_memory_allocator a buffer with an atomic_counter_t +// at the start is allocated. If the class does not align to pointer size, +// access to pointers in structures in the buffer will cause SIGBUS on +// architectures that do not allow mis-aligned pointers (eg: SPARC). +// Force the compiler to align to pointer size, which will cause the object +// to grow from 4 bytes to 8 bytes on 64 bit architectures (when not using +// mutexes). + +#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) +class __declspec(align (8)) atomic_counter_t +#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE)) +class __declspec(align (4)) atomic_counter_t +#else +class atomic_counter_t +#endif +{ + public: + typedef uint32_t integer_t; + + atomic_counter_t (integer_t value_ = 0) ZMQ_NOEXCEPT : _value (value_) {} + + // Set counter _value (not thread-safe). + void set (integer_t value_) ZMQ_NOEXCEPT { _value = value_; } + + // Atomic addition. Returns the old _value. + integer_t add (integer_t increment_) ZMQ_NOEXCEPT + { + integer_t old_value; + +#if defined ZMQ_ATOMIC_COUNTER_WINDOWS + old_value = InterlockedExchangeAdd ((LONG *) &_value, increment_); +#elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC + old_value = __atomic_fetch_add (&_value, increment_, __ATOMIC_ACQ_REL); +#elif defined ZMQ_ATOMIC_COUNTER_CXX11 + old_value = _value.fetch_add (increment_, std::memory_order_acq_rel); +#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H + integer_t new_value = atomic_add_32_nv (&_value, increment_); + old_value = new_value - increment_; +#elif defined ZMQ_ATOMIC_COUNTER_TILE + old_value = arch_atomic_add (&_value, increment_); +#elif defined ZMQ_ATOMIC_COUNTER_X86 + __asm__ volatile("lock; xadd %0, %1 \n\t" + : "=r"(old_value), "=m"(_value) + : "0"(increment_), "m"(_value) + : "cc", "memory"); +#elif defined ZMQ_ATOMIC_COUNTER_ARM + integer_t flag, tmp; + __asm__ volatile(" dmb sy\n\t" + "1: ldrex %0, [%5]\n\t" + " add %2, %0, %4\n\t" + " strex %1, %2, [%5]\n\t" + " teq %1, #0\n\t" + " bne 1b\n\t" + " dmb sy\n\t" + : "=&r"(old_value), "=&r"(flag), "=&r"(tmp), + "+Qo"(_value) + : "Ir"(increment_), "r"(&_value) + : "cc"); +#elif defined ZMQ_ATOMIC_COUNTER_MUTEX + sync.lock (); + old_value = _value; + _value += increment_; + sync.unlock (); +#else +#error atomic_counter is not implemented for this platform +#endif + return old_value; + } + + // Atomic subtraction. Returns false if the counter drops to zero. + bool sub (integer_t decrement_) ZMQ_NOEXCEPT + { +#if defined ZMQ_ATOMIC_COUNTER_WINDOWS + LONG delta = -((LONG) decrement_); + integer_t old = InterlockedExchangeAdd ((LONG *) &_value, delta); + return old - decrement_ != 0; +#elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC + integer_t nv = + __atomic_sub_fetch (&_value, decrement_, __ATOMIC_ACQ_REL); + return nv != 0; +#elif defined ZMQ_ATOMIC_COUNTER_CXX11 + const integer_t old = + _value.fetch_sub (decrement_, std::memory_order_acq_rel); + return old - decrement_ != 0; +#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H + int32_t delta = -((int32_t) decrement_); + integer_t nv = atomic_add_32_nv (&_value, delta); + return nv != 0; +#elif defined ZMQ_ATOMIC_COUNTER_TILE + int32_t delta = -((int32_t) decrement_); + integer_t nv = arch_atomic_add (&_value, delta); + return nv != 0; +#elif defined ZMQ_ATOMIC_COUNTER_X86 + integer_t oldval = -decrement_; + volatile integer_t *val = &_value; + __asm__ volatile("lock; xaddl %0,%1" + : "=r"(oldval), "=m"(*val) + : "0"(oldval), "m"(*val) + : "cc", "memory"); + return oldval != decrement_; +#elif defined ZMQ_ATOMIC_COUNTER_ARM + integer_t old_value, flag, tmp; + __asm__ volatile(" dmb sy\n\t" + "1: ldrex %0, [%5]\n\t" + " sub %2, %0, %4\n\t" + " strex %1, %2, [%5]\n\t" + " teq %1, #0\n\t" + " bne 1b\n\t" + " dmb sy\n\t" + : "=&r"(old_value), "=&r"(flag), "=&r"(tmp), + "+Qo"(_value) + : "Ir"(decrement_), "r"(&_value) + : "cc"); + return old_value - decrement_ != 0; +#elif defined ZMQ_ATOMIC_COUNTER_MUTEX + sync.lock (); + _value -= decrement_; + bool result = _value ? true : false; + sync.unlock (); + return result; +#else +#error atomic_counter is not implemented for this platform +#endif + } + + integer_t get () const ZMQ_NOEXCEPT { return _value; } + + private: +#if defined ZMQ_ATOMIC_COUNTER_CXX11 + std::atomic _value; +#else + volatile integer_t _value; +#endif + +#if defined ZMQ_ATOMIC_COUNTER_MUTEX + mutex_t sync; +#endif + +#if !defined ZMQ_ATOMIC_COUNTER_CXX11 + ZMQ_NON_COPYABLE_NOR_MOVABLE (atomic_counter_t) +#endif +#if defined(__GNUC__) || defined(__INTEL_COMPILER) \ + || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590) \ + || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590) +} __attribute__ ((aligned (sizeof (void *)))); +#else +}; +#endif +} + +// Remove macros local to this file. +#undef ZMQ_ATOMIC_COUNTER_MUTEX +#undef ZMQ_ATOMIC_COUNTER_INTRINSIC +#undef ZMQ_ATOMIC_COUNTER_CXX11 +#undef ZMQ_ATOMIC_COUNTER_X86 +#undef ZMQ_ATOMIC_COUNTER_ARM +#undef ZMQ_ATOMIC_COUNTER_WINDOWS +#undef ZMQ_ATOMIC_COUNTER_ATOMIC_H +#undef ZMQ_ATOMIC_COUNTER_TILE + +#endif diff --git a/3rd/libzmq/src/atomic_ptr.hpp b/3rd/libzmq/src/atomic_ptr.hpp new file mode 100644 index 00000000..fa8bc7c3 --- /dev/null +++ b/3rd/libzmq/src/atomic_ptr.hpp @@ -0,0 +1,306 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ATOMIC_PTR_HPP_INCLUDED__ +#define __ZMQ_ATOMIC_PTR_HPP_INCLUDED__ + +#include "macros.hpp" + +#if defined ZMQ_FORCE_MUTEXES +#define ZMQ_ATOMIC_PTR_MUTEX +#elif (defined __cplusplus && __cplusplus >= 201103L) \ + || (defined _MSC_VER && _MSC_VER >= 1900) +#define ZMQ_ATOMIC_PTR_CXX11 +#elif defined ZMQ_HAVE_ATOMIC_INTRINSICS +#define ZMQ_ATOMIC_PTR_INTRINSIC +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZMQ_ATOMIC_PTR_X86 +#elif defined __ARM_ARCH_7A__ && defined __GNUC__ +#define ZMQ_ATOMIC_PTR_ARM +#elif defined __tile__ +#define ZMQ_ATOMIC_PTR_TILE +#elif defined ZMQ_HAVE_WINDOWS +#define ZMQ_ATOMIC_PTR_WINDOWS +#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD \ + || defined ZMQ_HAVE_GNU) +#define ZMQ_ATOMIC_PTR_ATOMIC_H +#else +#define ZMQ_ATOMIC_PTR_MUTEX +#endif + +#if defined ZMQ_ATOMIC_PTR_MUTEX +#include "mutex.hpp" +#elif defined ZMQ_ATOMIC_PTR_CXX11 +#include +#elif defined ZMQ_ATOMIC_PTR_WINDOWS +#include "windows.hpp" +#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H +#include +#elif defined ZMQ_ATOMIC_PTR_TILE +#include +#endif + +namespace zmq +{ +#if !defined ZMQ_ATOMIC_PTR_CXX11 +inline void *atomic_xchg_ptr (void **ptr_, + void *const val_ +#if defined ZMQ_ATOMIC_PTR_MUTEX + , + mutex_t &_sync +#endif + ) ZMQ_NOEXCEPT +{ +#if defined ZMQ_ATOMIC_PTR_WINDOWS + return InterlockedExchangePointer ((PVOID *) ptr_, val_); +#elif defined ZMQ_ATOMIC_PTR_INTRINSIC + return __atomic_exchange_n (ptr_, val_, __ATOMIC_ACQ_REL); +#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H + return atomic_swap_ptr (ptr_, val_); +#elif defined ZMQ_ATOMIC_PTR_TILE + return arch_atomic_exchange (ptr_, val_); +#elif defined ZMQ_ATOMIC_PTR_X86 + void *old; + __asm__ volatile("lock; xchg %0, %2" + : "=r"(old), "=m"(*ptr_) + : "m"(*ptr_), "0"(val_)); + return old; +#elif defined ZMQ_ATOMIC_PTR_ARM + void *old; + unsigned int flag; + __asm__ volatile(" dmb sy\n\t" + "1: ldrex %1, [%3]\n\t" + " strex %0, %4, [%3]\n\t" + " teq %0, #0\n\t" + " bne 1b\n\t" + " dmb sy\n\t" + : "=&r"(flag), "=&r"(old), "+Qo"(*ptr_) + : "r"(ptr_), "r"(val_) + : "cc"); + return old; +#elif defined ZMQ_ATOMIC_PTR_MUTEX + _sync.lock (); + void *old = *ptr_; + *ptr_ = val_; + _sync.unlock (); + return old; +#else +#error atomic_ptr is not implemented for this platform +#endif +} + +inline void *atomic_cas (void *volatile *ptr_, + void *cmp_, + void *val_ +#if defined ZMQ_ATOMIC_PTR_MUTEX + , + mutex_t &_sync +#endif + ) ZMQ_NOEXCEPT +{ +#if defined ZMQ_ATOMIC_PTR_WINDOWS + return InterlockedCompareExchangePointer ((volatile PVOID *) ptr_, val_, + cmp_); +#elif defined ZMQ_ATOMIC_PTR_INTRINSIC + void *old = cmp_; + __atomic_compare_exchange_n (ptr_, &old, val_, false, __ATOMIC_RELEASE, + __ATOMIC_ACQUIRE); + return old; +#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H + return atomic_cas_ptr (ptr_, cmp_, val_); +#elif defined ZMQ_ATOMIC_PTR_TILE + return arch_atomic_val_compare_and_exchange (ptr_, cmp_, val_); +#elif defined ZMQ_ATOMIC_PTR_X86 + void *old; + __asm__ volatile("lock; cmpxchg %2, %3" + : "=a"(old), "=m"(*ptr_) + : "r"(val_), "m"(*ptr_), "0"(cmp_) + : "cc"); + return old; +#elif defined ZMQ_ATOMIC_PTR_ARM + void *old; + unsigned int flag; + __asm__ volatile(" dmb sy\n\t" + "1: ldrex %1, [%3]\n\t" + " mov %0, #0\n\t" + " teq %1, %4\n\t" + " it eq\n\t" + " strexeq %0, %5, [%3]\n\t" + " teq %0, #0\n\t" + " bne 1b\n\t" + " dmb sy\n\t" + : "=&r"(flag), "=&r"(old), "+Qo"(*ptr_) + : "r"(ptr_), "r"(cmp_), "r"(val_) + : "cc"); + return old; +#elif defined ZMQ_ATOMIC_PTR_MUTEX + _sync.lock (); + void *old = *ptr_; + if (*ptr_ == cmp_) + *ptr_ = val_; + _sync.unlock (); + return old; +#else +#error atomic_ptr is not implemented for this platform +#endif +} +#endif + +// This class encapsulates several atomic operations on pointers. + +template class atomic_ptr_t +{ + public: + // Initialise atomic pointer + atomic_ptr_t () ZMQ_NOEXCEPT { _ptr = NULL; } + + // Set value of atomic pointer in a non-threadsafe way + // Use this function only when you are sure that at most one + // thread is accessing the pointer at the moment. + void set (T *ptr_) ZMQ_NOEXCEPT { _ptr = ptr_; } + + // Perform atomic 'exchange pointers' operation. Pointer is set + // to the 'val_' value. Old value is returned. + T *xchg (T *val_) ZMQ_NOEXCEPT + { +#if defined ZMQ_ATOMIC_PTR_CXX11 + return _ptr.exchange (val_, std::memory_order_acq_rel); +#else + return (T *) atomic_xchg_ptr ((void **) &_ptr, val_ +#if defined ZMQ_ATOMIC_PTR_MUTEX + , + _sync +#endif + ); +#endif + } + + // Perform atomic 'compare and swap' operation on the pointer. + // The pointer is compared to 'cmp' argument and if they are + // equal, its value is set to 'val_'. Old value of the pointer + // is returned. + T *cas (T *cmp_, T *val_) ZMQ_NOEXCEPT + { +#if defined ZMQ_ATOMIC_PTR_CXX11 + _ptr.compare_exchange_strong (cmp_, val_, std::memory_order_acq_rel); + return cmp_; +#else + return (T *) atomic_cas ((void **) &_ptr, cmp_, val_ +#if defined ZMQ_ATOMIC_PTR_MUTEX + , + _sync +#endif + ); +#endif + } + + private: +#if defined ZMQ_ATOMIC_PTR_CXX11 + std::atomic _ptr; +#else + volatile T *_ptr; +#endif + +#if defined ZMQ_ATOMIC_PTR_MUTEX + mutex_t _sync; +#endif + +#if !defined ZMQ_ATOMIC_PTR_CXX11 + ZMQ_NON_COPYABLE_NOR_MOVABLE (atomic_ptr_t) +#endif +}; + +struct atomic_value_t +{ + atomic_value_t (const int value_) ZMQ_NOEXCEPT : _value (value_) {} + + atomic_value_t (const atomic_value_t &src_) ZMQ_NOEXCEPT + : _value (src_.load ()) + { + } + + void store (const int value_) ZMQ_NOEXCEPT + { +#if defined ZMQ_ATOMIC_PTR_CXX11 + _value.store (value_, std::memory_order_release); +#else + atomic_xchg_ptr ((void **) &_value, (void *) (ptrdiff_t) value_ +#if defined ZMQ_ATOMIC_PTR_MUTEX + , + _sync +#endif + ); +#endif + } + + int load () const ZMQ_NOEXCEPT + { +#if defined ZMQ_ATOMIC_PTR_CXX11 + return _value.load (std::memory_order_acquire); +#else + return (int) (ptrdiff_t) atomic_cas ((void **) &_value, 0, 0 +#if defined ZMQ_ATOMIC_PTR_MUTEX + , +#if defined __SUNPRO_CC + const_cast (_sync) +#else + _sync +#endif +#endif + ); +#endif + } + + private: +#if defined ZMQ_ATOMIC_PTR_CXX11 + std::atomic _value; +#else + volatile ptrdiff_t _value; +#endif + +#if defined ZMQ_ATOMIC_PTR_MUTEX + mutable mutex_t _sync; +#endif + + private: + atomic_value_t &operator= (const atomic_value_t &src_); +}; +} + +// Remove macros local to this file. +#undef ZMQ_ATOMIC_PTR_MUTEX +#undef ZMQ_ATOMIC_PTR_INTRINSIC +#undef ZMQ_ATOMIC_PTR_CXX11 +#undef ZMQ_ATOMIC_PTR_X86 +#undef ZMQ_ATOMIC_PTR_ARM +#undef ZMQ_ATOMIC_PTR_TILE +#undef ZMQ_ATOMIC_PTR_WINDOWS +#undef ZMQ_ATOMIC_PTR_ATOMIC_H + +#endif diff --git a/3rd/libzmq/src/blob.hpp b/3rd/libzmq/src/blob.hpp new file mode 100644 index 00000000..54221618 --- /dev/null +++ b/3rd/libzmq/src/blob.hpp @@ -0,0 +1,205 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_BLOB_HPP_INCLUDED__ +#define __ZMQ_BLOB_HPP_INCLUDED__ + +#include "macros.hpp" +#include "err.hpp" + +#include +#include +#include +#include + +#if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER > 1700 +#define ZMQ_HAS_MOVE_SEMANTICS +#define ZMQ_MAP_INSERT_OR_EMPLACE(k, v) emplace (k, v) +#define ZMQ_PUSH_OR_EMPLACE_BACK emplace_back +#define ZMQ_MOVE(x) std::move (x) +#else +#if defined __SUNPRO_CC +template +std::pair make_pair_fix_const (const K &k, const V &v) +{ + return std::pair (k, v); +} + +#define ZMQ_MAP_INSERT_OR_EMPLACE(k, v) insert (make_pair_fix_const (k, v)) +#else +#define ZMQ_MAP_INSERT_OR_EMPLACE(k, v) insert (std::make_pair (k, v)) +#endif + +#define ZMQ_PUSH_OR_EMPLACE_BACK push_back +#define ZMQ_MOVE(x) (x) +#endif + +namespace zmq +{ +struct reference_tag_t +{ +}; + +// Object to hold dynamically allocated opaque binary data. +// On modern compilers, it will be movable but not copyable. Copies +// must be explicitly created by set_deep_copy. +// On older compilers, it is copyable for syntactical reasons. +struct blob_t +{ + // Creates an empty blob_t. + blob_t () : _data (0), _size (0), _owned (true) {} + + // Creates a blob_t of a given size, with uninitialized content. + explicit blob_t (const size_t size_) : + _data (static_cast (malloc (size_))), + _size (size_), + _owned (true) + { + alloc_assert (_data); + } + + // Creates a blob_t of a given size, an initializes content by copying + // from another buffer. + blob_t (const unsigned char *const data_, const size_t size_) : + _data (static_cast (malloc (size_))), + _size (size_), + _owned (true) + { + alloc_assert (_data); + memcpy (_data, data_, size_); + } + + // Creates a blob_t for temporary use that only references a + // pre-allocated block of data. + // Use with caution and ensure that the blob_t will not outlive + // the referenced data. + blob_t (unsigned char *const data_, const size_t size_, reference_tag_t) : + _data (data_), + _size (size_), + _owned (false) + { + } + + // Returns the size of the blob_t. + size_t size () const { return _size; } + + // Returns a pointer to the data of the blob_t. + const unsigned char *data () const { return _data; } + + // Returns a pointer to the data of the blob_t. + unsigned char *data () { return _data; } + + // Defines an order relationship on blob_t. + bool operator< (blob_t const &other_) const + { + const int cmpres = + memcmp (_data, other_._data, std::min (_size, other_._size)); + return cmpres < 0 || (cmpres == 0 && _size < other_._size); + } + + // Sets a blob_t to a deep copy of another blob_t. + void set_deep_copy (blob_t const &other_) + { + clear (); + _data = static_cast (malloc (other_._size)); + alloc_assert (_data); + _size = other_._size; + _owned = true; + memcpy (_data, other_._data, _size); + } + + // Sets a blob_t to a copy of a given buffer. + void set (const unsigned char *const data_, const size_t size_) + { + clear (); + _data = static_cast (malloc (size_)); + alloc_assert (_data); + _size = size_; + _owned = true; + memcpy (_data, data_, size_); + } + + // Empties a blob_t. + void clear () + { + if (_owned) { + free (_data); + } + _data = 0; + _size = 0; + } + + ~blob_t () + { + if (_owned) { + free (_data); + } + } + +#ifdef ZMQ_HAS_MOVE_SEMANTICS + blob_t (const blob_t &) = delete; + blob_t &operator= (const blob_t &) = delete; + + blob_t (blob_t &&other_) ZMQ_NOEXCEPT : _data (other_._data), + _size (other_._size), + _owned (other_._owned) + { + other_._owned = false; + } + blob_t &operator= (blob_t &&other_) ZMQ_NOEXCEPT + { + if (this != &other_) { + clear (); + _data = other_._data; + _size = other_._size; + _owned = other_._owned; + other_._owned = false; + } + return *this; + } +#else + blob_t (const blob_t &other) : _owned (false) { set_deep_copy (other); } + blob_t &operator= (const blob_t &other) + { + if (this != &other) { + clear (); + set_deep_copy (other); + } + return *this; + } +#endif + + private: + unsigned char *_data; + size_t _size; + bool _owned; +}; +} + +#endif diff --git a/3rd/libzmq/src/channel.cpp b/3rd/libzmq/src/channel.cpp new file mode 100644 index 00000000..6a396228 --- /dev/null +++ b/3rd/libzmq/src/channel.cpp @@ -0,0 +1,160 @@ +/* + Copyright (c) 2007-2020 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "channel.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "msg.hpp" + +zmq::channel_t::channel_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_, true), + _pipe (NULL) +{ + options.type = ZMQ_CHANNEL; +} + +zmq::channel_t::~channel_t () +{ + zmq_assert (!_pipe); +} + +void zmq::channel_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_ != NULL); + + // ZMQ_PAIR socket can only be connected to a single peer. + // The socket rejects any further connection requests. + if (_pipe == NULL) + _pipe = pipe_; + else + pipe_->terminate (false); +} + +void zmq::channel_t::xpipe_terminated (pipe_t *pipe_) +{ + if (pipe_ == _pipe) + _pipe = NULL; +} + +void zmq::channel_t::xread_activated (pipe_t *) +{ + // There's just one pipe. No lists of active and inactive pipes. + // There's nothing to do here. +} + +void zmq::channel_t::xwrite_activated (pipe_t *) +{ + // There's just one pipe. No lists of active and inactive pipes. + // There's nothing to do here. +} + +int zmq::channel_t::xsend (msg_t *msg_) +{ + // CHANNEL sockets do not allow multipart data (ZMQ_SNDMORE) + if (msg_->flags () & msg_t::more) { + errno = EINVAL; + return -1; + } + + if (!_pipe || !_pipe->write (msg_)) { + errno = EAGAIN; + return -1; + } + + _pipe->flush (); + + // Detach the original message from the data buffer. + const int rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +int zmq::channel_t::xrecv (msg_t *msg_) +{ + // Deallocate old content of the message. + int rc = msg_->close (); + errno_assert (rc == 0); + + if (!_pipe) { + // Initialise the output parameter to be a 0-byte message. + rc = msg_->init (); + errno_assert (rc == 0); + + errno = EAGAIN; + return -1; + } + + // Drop any messages with more flag + bool read = _pipe->read (msg_); + while (read && msg_->flags () & msg_t::more) { + // drop all frames of the current multi-frame message + read = _pipe->read (msg_); + while (read && msg_->flags () & msg_t::more) + read = _pipe->read (msg_); + + // get the new message + if (read) + read = _pipe->read (msg_); + } + + if (!read) { + // Initialise the output parameter to be a 0-byte message. + rc = msg_->init (); + errno_assert (rc == 0); + + errno = EAGAIN; + return -1; + } + + return 0; +} + +bool zmq::channel_t::xhas_in () +{ + if (!_pipe) + return false; + + return _pipe->check_read (); +} + +bool zmq::channel_t::xhas_out () +{ + if (!_pipe) + return false; + + return _pipe->check_write (); +} diff --git a/3rd/libzmq/src/channel.hpp b/3rd/libzmq/src/channel.hpp new file mode 100644 index 00000000..0eb360a0 --- /dev/null +++ b/3rd/libzmq/src/channel.hpp @@ -0,0 +1,69 @@ +/* + Copyright (c) 2007-2020 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 . +*/ + +#ifndef __ZMQ_CHANNEL_HPP_INCLUDED__ +#define __ZMQ_CHANNEL_HPP_INCLUDED__ + +#include "blob.hpp" +#include "socket_base.hpp" +#include "session_base.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; +class io_thread_t; + +class channel_t ZMQ_FINAL : public socket_base_t +{ + public: + channel_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~channel_t (); + + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + void xread_activated (zmq::pipe_t *pipe_); + void xwrite_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + zmq::pipe_t *_pipe; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (channel_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/client.cpp b/3rd/libzmq/src/client.cpp new file mode 100644 index 00000000..f26b1386 --- /dev/null +++ b/3rd/libzmq/src/client.cpp @@ -0,0 +1,114 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "client.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::client_t::client_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_, true) +{ + options.type = ZMQ_CLIENT; + options.can_send_hello_msg = true; +} + +zmq::client_t::~client_t () +{ +} + +void zmq::client_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + + _fq.attach (pipe_); + _lb.attach (pipe_); +} + +int zmq::client_t::xsend (msg_t *msg_) +{ + // CLIENT sockets do not allow multipart data (ZMQ_SNDMORE) + if (msg_->flags () & msg_t::more) { + errno = EINVAL; + return -1; + } + return _lb.sendpipe (msg_, NULL); +} + +int zmq::client_t::xrecv (msg_t *msg_) +{ + int rc = _fq.recvpipe (msg_, NULL); + + // Drop any messages with more flag + while (rc == 0 && msg_->flags () & msg_t::more) { + // drop all frames of the current multi-frame message + rc = _fq.recvpipe (msg_, NULL); + + while (rc == 0 && msg_->flags () & msg_t::more) + rc = _fq.recvpipe (msg_, NULL); + + // get the new message + if (rc == 0) + rc = _fq.recvpipe (msg_, NULL); + } + + return rc; +} + +bool zmq::client_t::xhas_in () +{ + return _fq.has_in (); +} + +bool zmq::client_t::xhas_out () +{ + return _lb.has_out (); +} + +void zmq::client_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +void zmq::client_t::xwrite_activated (pipe_t *pipe_) +{ + _lb.activated (pipe_); +} + +void zmq::client_t::xpipe_terminated (pipe_t *pipe_) +{ + _fq.pipe_terminated (pipe_); + _lb.pipe_terminated (pipe_); +} diff --git a/3rd/libzmq/src/client.hpp b/3rd/libzmq/src/client.hpp new file mode 100644 index 00000000..c166f0e8 --- /dev/null +++ b/3rd/libzmq/src/client.hpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CLIENT_HPP_INCLUDED__ +#define __ZMQ_CLIENT_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "fq.hpp" +#include "lb.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; +class io_thread_t; + +class client_t ZMQ_FINAL : public socket_base_t +{ + public: + client_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~client_t (); + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + void xread_activated (zmq::pipe_t *pipe_); + void xwrite_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + // Messages are fair-queued from inbound pipes. And load-balanced to + // the outbound pipes. + fq_t _fq; + lb_t _lb; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (client_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/clock.cpp b/3rd/libzmq/src/clock.cpp new file mode 100644 index 00000000..79522ad3 --- /dev/null +++ b/3rd/libzmq/src/clock.cpp @@ -0,0 +1,277 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "clock.hpp" +#include "likely.hpp" +#include "config.hpp" +#include "err.hpp" +#include "mutex.hpp" + +#include + +#if defined _MSC_VER +#if defined _WIN32_WCE +#include +#else +#include +#if defined(_M_ARM) || defined(_M_ARM64) +#include +#endif +#endif +#endif + +#if !defined ZMQ_HAVE_WINDOWS +#include +#endif + +#if defined HAVE_CLOCK_GETTIME || defined HAVE_GETHRTIME +#include +#endif + +#if defined ZMQ_HAVE_VXWORKS +#include "timers.h" +#endif + +#if defined ZMQ_HAVE_OSX +int alt_clock_gettime (int clock_id, timespec *ts) +{ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service (mach_host_self (), clock_id, &cclock); + clock_get_time (cclock, &mts); + mach_port_deallocate (mach_task_self (), cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; + return 0; +} +#endif + +#ifdef ZMQ_HAVE_WINDOWS +typedef ULONGLONG (*f_compatible_get_tick_count64) (); + +static zmq::mutex_t compatible_get_tick_count64_mutex; + +ULONGLONG compatible_get_tick_count64 () +{ +#ifdef ZMQ_HAVE_WINDOWS_UWP + const ULONGLONG result = ::GetTickCount64 (); + return result; +#else + zmq::scoped_lock_t locker (compatible_get_tick_count64_mutex); + + static DWORD s_wrap = 0; + static DWORD s_last_tick = 0; + const DWORD current_tick = ::GetTickCount (); + + if (current_tick < s_last_tick) + ++s_wrap; + + s_last_tick = current_tick; + const ULONGLONG result = (static_cast (s_wrap) << 32) + + static_cast (current_tick); + + return result; +#endif +} + +f_compatible_get_tick_count64 init_compatible_get_tick_count64 () +{ + f_compatible_get_tick_count64 func = NULL; +#if !defined ZMQ_HAVE_WINDOWS_UWP + + const HMODULE module = ::LoadLibraryA ("Kernel32.dll"); + if (module != NULL) + func = reinterpret_cast ( + ::GetProcAddress (module, "GetTickCount64")); +#endif + if (func == NULL) + func = compatible_get_tick_count64; + +#if !defined ZMQ_HAVE_WINDOWS_UWP + if (module != NULL) + ::FreeLibrary (module); +#endif + + return func; +} + +static f_compatible_get_tick_count64 my_get_tick_count64 = + init_compatible_get_tick_count64 (); +#endif + +const uint64_t usecs_per_msec = 1000; +const uint64_t usecs_per_sec = 1000000; +const uint64_t nsecs_per_usec = 1000; + +zmq::clock_t::clock_t () : + _last_tsc (rdtsc ()), +#ifdef ZMQ_HAVE_WINDOWS + _last_time (static_cast ((*my_get_tick_count64) ())) +#else + _last_time (now_us () / usecs_per_msec) +#endif +{ +} + +uint64_t zmq::clock_t::now_us () +{ +#if defined ZMQ_HAVE_WINDOWS + + // Get the high resolution counter's accuracy. + // While QueryPerformanceFrequency only needs to be called once, since its + // value does not change during runtime, we query it here since this is a + // static function. It might make sense to cache it, though. + LARGE_INTEGER ticks_per_second; + QueryPerformanceFrequency (&ticks_per_second); + + // What time is it? + LARGE_INTEGER tick; + QueryPerformanceCounter (&tick); + + // Convert the tick number into the number of seconds + // since the system was started. + const double ticks_div = + static_cast (ticks_per_second.QuadPart) / usecs_per_sec; + return static_cast (tick.QuadPart / ticks_div); + +#elif defined HAVE_CLOCK_GETTIME \ + && (defined CLOCK_MONOTONIC || defined ZMQ_HAVE_VXWORKS) + + // Use POSIX clock_gettime function to get precise monotonic time. + struct timespec tv; + +#if defined ZMQ_HAVE_OSX \ + && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 // less than macOS 10.12 + int rc = alt_clock_gettime (SYSTEM_CLOCK, &tv); +#else + int rc = clock_gettime (CLOCK_MONOTONIC, &tv); +#endif + // Fix case where system has clock_gettime but CLOCK_MONOTONIC is not supported. + // This should be a configuration check, but I looked into it and writing an + // AC_FUNC_CLOCK_MONOTONIC seems beyond my powers. + if (rc != 0) { +#ifndef ZMQ_HAVE_VXWORKS + // Use POSIX gettimeofday function to get precise time. + struct timeval tv; + int rc = gettimeofday (&tv, NULL); + errno_assert (rc == 0); + return tv.tv_sec * usecs_per_sec + tv.tv_usec; +#endif + } + return tv.tv_sec * usecs_per_sec + tv.tv_nsec / nsecs_per_usec; + +#elif defined HAVE_GETHRTIME + + return gethrtime () / nsecs_per_usec; + +#else + + // Use POSIX gettimeofday function to get precise time. + struct timeval tv; + int rc = gettimeofday (&tv, NULL); + errno_assert (rc == 0); + return tv.tv_sec * usecs_per_sec + tv.tv_usec; + +#endif +} + +uint64_t zmq::clock_t::now_ms () +{ + const uint64_t tsc = rdtsc (); + + // If TSC is not supported, get precise time and chop off the microseconds. + if (!tsc) { +#ifdef ZMQ_HAVE_WINDOWS + // Under Windows, now_us is not so reliable since QueryPerformanceCounter + // does not guarantee that it will use a hardware that offers a monotonic timer. + // So, lets use GetTickCount when GetTickCount64 is not available with an workaround + // to its 32 bit limitation. + return static_cast ((*my_get_tick_count64) ()); +#else + return now_us () / usecs_per_msec; +#endif + } + + // If TSC haven't jumped back (in case of migration to a different + // CPU core) and if not too much time elapsed since last measurement, + // we can return cached time value. + if (likely (tsc - _last_tsc <= (clock_precision / 2) && tsc >= _last_tsc)) + return _last_time; + + _last_tsc = tsc; +#ifdef ZMQ_HAVE_WINDOWS + _last_time = static_cast ((*my_get_tick_count64) ()); +#else + _last_time = now_us () / usecs_per_msec; +#endif + return _last_time; +} + +uint64_t zmq::clock_t::rdtsc () +{ +#if (defined _MSC_VER && (defined _M_IX86 || defined _M_X64)) + return __rdtsc (); +#elif defined(_MSC_VER) && defined(_M_ARM) // NC => added for windows ARM + return __rdpmccntr64 (); +#elif defined(_MSC_VER) && defined(_M_ARM64) // NC => added for windows ARM64 + //return __rdpmccntr64 (); + //return __rdtscp (nullptr); + // todo: find proper implementation for ARM64 + static uint64_t snCounter = 0; + return ++snCounter; +#elif (defined __GNUC__ && (defined __i386__ || defined __x86_64__)) + uint32_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return static_cast (high) << 32 | low; +#elif (defined __SUNPRO_CC && (__SUNPRO_CC >= 0x5100) \ + && (defined __i386 || defined __amd64 || defined __x86_64)) + union + { + uint64_t u64val; + uint32_t u32val[2]; + } tsc; + asm("rdtsc" : "=a"(tsc.u32val[0]), "=d"(tsc.u32val[1])); + return tsc.u64val; +#elif defined(__s390__) + uint64_t tsc; + asm("\tstck\t%0\n" : "=Q"(tsc) : : "cc"); + return tsc; +#else + struct timespec ts; +#if defined ZMQ_HAVE_OSX \ + && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 // less than macOS 10.12 + alt_clock_gettime (SYSTEM_CLOCK, &ts); +#else + clock_gettime (CLOCK_MONOTONIC, &ts); +#endif + return static_cast (ts.tv_sec) * nsecs_per_usec * usecs_per_sec + + ts.tv_nsec; +#endif +} diff --git a/3rd/libzmq/src/clock.hpp b/3rd/libzmq/src/clock.hpp new file mode 100644 index 00000000..300d1801 --- /dev/null +++ b/3rd/libzmq/src/clock.hpp @@ -0,0 +1,80 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CLOCK_HPP_INCLUDED__ +#define __ZMQ_CLOCK_HPP_INCLUDED__ + +#include "macros.hpp" +#include "stdint.hpp" + +#if defined ZMQ_HAVE_OSX +// TODO this is not required in this file, but condition_variable.hpp includes +// clock.hpp to get these definitions +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif +#ifndef HAVE_CLOCK_GETTIME +#define HAVE_CLOCK_GETTIME +#endif + +#include +#include +#include +#include +#endif + +namespace zmq +{ +class clock_t +{ + public: + clock_t (); + + // CPU's timestamp counter. Returns 0 if it's not available. + static uint64_t rdtsc (); + + // High precision timestamp. + static uint64_t now_us (); + + // Low precision timestamp. In tight loops generating it can be + // 10 to 100 times faster than the high precision timestamp. + uint64_t now_ms (); + + private: + // TSC timestamp of when last time measurement was made. + uint64_t _last_tsc; + + // Physical time corresponding to the TSC above (in milliseconds). + uint64_t _last_time; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (clock_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/command.hpp b/3rd/libzmq/src/command.hpp new file mode 100644 index 00000000..c92f7a7c --- /dev/null +++ b/3rd/libzmq/src/command.hpp @@ -0,0 +1,225 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_COMMAND_HPP_INCLUDED__ +#define __ZMQ_COMMAND_HPP_INCLUDED__ + +#include +#include "stdint.hpp" +#include "endpoint.hpp" + +namespace zmq +{ +class object_t; +class own_t; +struct i_engine; +class pipe_t; +class socket_base_t; + +// This structure defines the commands that can be sent between threads. + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4324) // C4324: alignment padding warnings +__declspec(align (64)) +#endif + struct command_t +{ + // Object to process the command. + zmq::object_t *destination; + + enum type_t + { + stop, + plug, + own, + attach, + bind, + activate_read, + activate_write, + hiccup, + pipe_term, + pipe_term_ack, + pipe_hwm, + term_req, + term, + term_ack, + term_endpoint, + reap, + reaped, + inproc_connected, + conn_failed, + pipe_peer_stats, + pipe_stats_publish, + done + } type; + + union args_t + { + // Sent to I/O thread to let it know that it should + // terminate itself. + struct + { + } stop; + + // Sent to I/O object to make it register with its I/O thread. + struct + { + } plug; + + // Sent to socket to let it know about the newly created object. + struct + { + zmq::own_t *object; + } own; + + // Attach the engine to the session. If engine is NULL, it informs + // session that the connection have failed. + struct + { + struct i_engine *engine; + } attach; + + // Sent from session to socket to establish pipe(s) between them. + // Caller have used inc_seqnum beforehand sending the command. + struct + { + zmq::pipe_t *pipe; + } bind; + + // Sent by pipe writer to inform dormant pipe reader that there + // are messages in the pipe. + struct + { + } activate_read; + + // Sent by pipe reader to inform pipe writer about how many + // messages it has read so far. + struct + { + uint64_t msgs_read; + } activate_write; + + // Sent by pipe reader to writer after creating a new inpipe. + // The parameter is actually of type pipe_t::upipe_t, however, + // its definition is private so we'll have to do with void*. + struct + { + void *pipe; + } hiccup; + + // Sent by pipe reader to pipe writer to ask it to terminate + // its end of the pipe. + struct + { + } pipe_term; + + // Pipe writer acknowledges pipe_term command. + struct + { + } pipe_term_ack; + + // Sent by one of pipe to another part for modify hwm + struct + { + int inhwm; + int outhwm; + } pipe_hwm; + + // Sent by I/O object ot the socket to request the shutdown of + // the I/O object. + struct + { + zmq::own_t *object; + } term_req; + + // Sent by socket to I/O object to start its shutdown. + struct + { + int linger; + } term; + + // Sent by I/O object to the socket to acknowledge it has + // shut down. + struct + { + } term_ack; + + // Sent by session_base (I/O thread) to socket (application thread) + // to ask to disconnect the endpoint. + struct + { + std::string *endpoint; + } term_endpoint; + + // Transfers the ownership of the closed socket + // to the reaper thread. + struct + { + zmq::socket_base_t *socket; + } reap; + + // Closed socket notifies the reaper that it's already deallocated. + struct + { + } reaped; + + // Send application-side pipe count and ask to send monitor event + struct + { + uint64_t queue_count; + zmq::own_t *socket_base; + endpoint_uri_pair_t *endpoint_pair; + } pipe_peer_stats; + + // Collate application thread and I/O thread pipe counts and endpoints + // and send as event + struct + { + uint64_t outbound_queue_count; + uint64_t inbound_queue_count; + endpoint_uri_pair_t *endpoint_pair; + } pipe_stats_publish; + + // Sent by reaper thread to the term thread when all the sockets + // are successfully deallocated. + struct + { + } done; + + } args; +#ifdef _MSC_VER +}; +#pragma warning(pop) +#else +} __attribute__ ((aligned (64))); +#endif +} + +#endif diff --git a/3rd/libzmq/src/compat.hpp b/3rd/libzmq/src/compat.hpp new file mode 100644 index 00000000..47744b98 --- /dev/null +++ b/3rd/libzmq/src/compat.hpp @@ -0,0 +1,75 @@ +/* + Copyright (c) 2020 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 . +*/ + +#ifndef __ZMQ_COMPAT_HPP_INCLUDED__ +#define __ZMQ_COMPAT_HPP_INCLUDED__ + +#include "precompiled.hpp" +#include + +#ifdef ZMQ_HAVE_WINDOWS +#define strcasecmp _stricmp +#define strtok_r strtok_s +#else +#ifndef ZMQ_HAVE_STRLCPY +#ifdef ZMQ_HAVE_LIBBSD +#include +#else +static inline size_t +strlcpy (char *dest_, const char *src_, const size_t dest_size_) +{ + size_t remain = dest_size_; + for (; remain && *src_; --remain, ++src_, ++dest_) { + *dest_ = *src_; + } + return dest_size_ - remain; +} +#endif +#endif +template +static inline int strcpy_s (char (&dest_)[size], const char *const src_) +{ + const size_t res = strlcpy (dest_, src_, size); + return res >= size ? ERANGE : 0; +} +#endif + +#ifndef HAVE_STRNLEN +static inline size_t strnlen (const char *s, size_t len) +{ + for (size_t i = 0; i < len; i++) { + if (s[i] == '\0') + return i + 1; + } + + return len; +} +#endif + +#endif diff --git a/3rd/libzmq/src/condition_variable.hpp b/3rd/libzmq/src/condition_variable.hpp new file mode 100644 index 00000000..47d14e79 --- /dev/null +++ b/3rd/libzmq/src/condition_variable.hpp @@ -0,0 +1,313 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CONDITON_VARIABLE_HPP_INCLUDED__ +#define __ZMQ_CONDITON_VARIABLE_HPP_INCLUDED__ + +#include "err.hpp" +#include "mutex.hpp" + +// Condition variable class encapsulates OS mutex in a platform-independent way. + +#if defined(ZMQ_USE_CV_IMPL_NONE) + +namespace zmq +{ +class condition_variable_t +{ + public: + inline condition_variable_t () { zmq_assert (false); } + + inline int wait (mutex_t *mutex_, int timeout_) + { + zmq_assert (false); + return -1; + } + + inline void broadcast () { zmq_assert (false); } + + ZMQ_NON_COPYABLE_NOR_MOVABLE (condition_variable_t) +}; +} + +#elif defined(ZMQ_USE_CV_IMPL_WIN32API) + +#include "windows.hpp" + +namespace zmq +{ +class condition_variable_t +{ + public: + inline condition_variable_t () { InitializeConditionVariable (&_cv); } + + inline int wait (mutex_t *mutex_, int timeout_) + { + int rc = SleepConditionVariableCS (&_cv, mutex_->get_cs (), timeout_); + + if (rc != 0) + return 0; + + rc = GetLastError (); + + if (rc != ERROR_TIMEOUT) + win_assert (rc); + + errno = EAGAIN; + return -1; + } + + inline void broadcast () { WakeAllConditionVariable (&_cv); } + + private: + CONDITION_VARIABLE _cv; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (condition_variable_t) +}; +} + +#elif defined(ZMQ_USE_CV_IMPL_STL11) + +#include + +namespace zmq +{ +class condition_variable_t +{ + public: + condition_variable_t () ZMQ_DEFAULT; + + int wait (mutex_t *mutex_, int timeout_) + { + // this assumes that the mutex mutex_ has been locked by the caller + int res = 0; + if (timeout_ == -1) { + _cv.wait ( + *mutex_); // unlock mtx and wait cv.notify_all(), lock mtx after cv.notify_all() + } else if (_cv.wait_for (*mutex_, std::chrono::milliseconds (timeout_)) + == std::cv_status::timeout) { + // time expired + errno = EAGAIN; + res = -1; + } + return res; + } + + void broadcast () + { + // this assumes that the mutex associated with _cv has been locked by the caller + _cv.notify_all (); + } + + private: + std::condition_variable_any _cv; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (condition_variable_t) +}; +} + +#elif defined(ZMQ_USE_CV_IMPL_VXWORKS) + +#include + +namespace zmq +{ +class condition_variable_t +{ + public: + inline condition_variable_t () ZMQ_DEFAULT; + + inline ~condition_variable_t () + { + scoped_lock_t l (_listenersMutex); + for (size_t i = 0; i < _listeners.size (); i++) { + semDelete (_listeners[i]); + } + } + + inline int wait (mutex_t *mutex_, int timeout_) + { + //Atomically releases lock, blocks the current executing thread, + //and adds it to the list of threads waiting on *this. The thread + //will be unblocked when broadcast() is executed. + //It may also be unblocked spuriously. When unblocked, regardless + //of the reason, lock is reacquired and wait exits. + + SEM_ID sem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY); + { + scoped_lock_t l (_listenersMutex); + _listeners.push_back (sem); + } + mutex_->unlock (); + + int rc; + if (timeout_ < 0) + rc = semTake (sem, WAIT_FOREVER); + else { + int ticksPerSec = sysClkRateGet (); + int timeoutTicks = (timeout_ * ticksPerSec) / 1000 + 1; + rc = semTake (sem, timeoutTicks); + } + + { + scoped_lock_t l (_listenersMutex); + // remove sem from listeners + for (size_t i = 0; i < _listeners.size (); i++) { + if (_listeners[i] == sem) { + _listeners.erase (_listeners.begin () + i); + break; + } + } + semDelete (sem); + } + mutex_->lock (); + + if (rc == 0) + return 0; + + if (rc == S_objLib_OBJ_TIMEOUT) { + errno = EAGAIN; + return -1; + } + + return -1; + } + + inline void broadcast () + { + scoped_lock_t l (_listenersMutex); + for (size_t i = 0; i < _listeners.size (); i++) { + semGive (_listeners[i]); + } + } + + private: + mutex_t _listenersMutex; + std::vector _listeners; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (condition_variable_t) +}; +} + +#elif defined(ZMQ_USE_CV_IMPL_PTHREADS) + +#include + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 +#define ANDROID_LEGACY +extern "C" int pthread_cond_timedwait_monotonic_np (pthread_cond_t *, + pthread_mutex_t *, + const struct timespec *); +#endif + +namespace zmq +{ +class condition_variable_t +{ + public: + inline condition_variable_t () + { + pthread_condattr_t attr; + pthread_condattr_init (&attr); +#if !defined(ZMQ_HAVE_OSX) && !defined(ANDROID_LEGACY) + pthread_condattr_setclock (&attr, CLOCK_MONOTONIC); +#endif + int rc = pthread_cond_init (&_cond, &attr); + posix_assert (rc); + } + + inline ~condition_variable_t () + { + int rc = pthread_cond_destroy (&_cond); + posix_assert (rc); + } + + inline int wait (mutex_t *mutex_, int timeout_) + { + int rc; + + if (timeout_ != -1) { + struct timespec timeout; + +#ifdef ZMQ_HAVE_OSX + timeout.tv_sec = 0; + timeout.tv_nsec = 0; +#else + rc = clock_gettime (CLOCK_MONOTONIC, &timeout); + posix_assert (rc); +#endif + + timeout.tv_sec += timeout_ / 1000; + timeout.tv_nsec += (timeout_ % 1000) * 1000000; + + if (timeout.tv_nsec >= 1000000000) { + timeout.tv_sec++; + timeout.tv_nsec -= 1000000000; + } +#ifdef ZMQ_HAVE_OSX + rc = pthread_cond_timedwait_relative_np ( + &_cond, mutex_->get_mutex (), &timeout); +#elif defined(ANDROID_LEGACY) + rc = pthread_cond_timedwait_monotonic_np ( + &_cond, mutex_->get_mutex (), &timeout); +#else + rc = + pthread_cond_timedwait (&_cond, mutex_->get_mutex (), &timeout); +#endif + } else + rc = pthread_cond_wait (&_cond, mutex_->get_mutex ()); + + if (rc == 0) + return 0; + + if (rc == ETIMEDOUT) { + errno = EAGAIN; + return -1; + } + + posix_assert (rc); + return -1; + } + + inline void broadcast () + { + int rc = pthread_cond_broadcast (&_cond); + posix_assert (rc); + } + + private: + pthread_cond_t _cond; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (condition_variable_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/config.hpp b/3rd/libzmq/src/config.hpp new file mode 100644 index 00000000..c355c6d7 --- /dev/null +++ b/3rd/libzmq/src/config.hpp @@ -0,0 +1,89 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CONFIG_HPP_INCLUDED__ +#define __ZMQ_CONFIG_HPP_INCLUDED__ + +namespace zmq +{ +// Compile-time settings. + +enum +{ + // Number of new messages in message pipe needed to trigger new memory + // allocation. Setting this parameter to 256 decreases the impact of + // memory allocation by approximately 99.6% + message_pipe_granularity = 256, + + // Commands in pipe per allocation event. + command_pipe_granularity = 16, + + // Determines how often does socket poll for new commands when it + // still has unprocessed messages to handle. Thus, if it is set to 100, + // socket will process 100 inbound messages before doing the poll. + // If there are no unprocessed messages available, poll is done + // immediately. Decreasing the value trades overall latency for more + // real-time behaviour (less latency peaks). + inbound_poll_rate = 100, + + // Maximal delta between high and low watermark. + max_wm_delta = 1024, + + // Maximum number of events the I/O thread can process in one go. + max_io_events = 256, + + // Maximal batch size of packets forwarded by a ZMQ proxy. + // Increasing this value improves throughput at the expense of + // latency and fairness. + proxy_burst_size = 1000, + + // Maximal delay to process command in API thread (in CPU ticks). + // 3,000,000 ticks equals to 1 - 2 milliseconds on current CPUs. + // Note that delay is only applied when there is continuous stream of + // messages to process. If not so, commands are processed immediately. + max_command_delay = 3000000, + + // Low-precision clock precision in CPU ticks. 1ms. Value of 1000000 + // should be OK for CPU frequencies above 1GHz. If should work + // reasonably well for CPU frequencies above 500MHz. For lower CPU + // frequencies you may consider lowering this value to get best + // possible latencies. + clock_precision = 1000000, + + // On some OSes the signaler has to be emulated using a TCP + // connection. In such cases following port is used. + // If 0, it lets the OS choose a free port without requiring use of a + // global mutex. The original implementation of a Windows signaler + // socket used port 5905 instead of letting the OS choose a free port. + // https://github.com/zeromq/libzmq/issues/1542 + signaler_port = 0 +}; +} + +#endif diff --git a/3rd/libzmq/src/ctx.cpp b/3rd/libzmq/src/ctx.cpp new file mode 100644 index 00000000..2b64c510 --- /dev/null +++ b/3rd/libzmq/src/ctx.cpp @@ -0,0 +1,893 @@ +/* + Copyright (c) 2007-2017 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#ifndef ZMQ_HAVE_WINDOWS +#include +#endif + +#include +#include +#include +#include +#include + +#include "ctx.hpp" +#include "socket_base.hpp" +#include "io_thread.hpp" +#include "reaper.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" +#include "random.hpp" + +#ifdef ZMQ_HAVE_VMCI +#include +#endif + +#ifdef ZMQ_USE_NSS +#include +#endif + +#ifdef ZMQ_USE_GNUTLS +#include +#endif + +#define ZMQ_CTX_TAG_VALUE_GOOD 0xabadcafe +#define ZMQ_CTX_TAG_VALUE_BAD 0xdeadbeef + +static int clipped_maxsocket (int max_requested_) +{ + if (max_requested_ >= zmq::poller_t::max_fds () + && zmq::poller_t::max_fds () != -1) + // -1 because we need room for the reaper mailbox. + max_requested_ = zmq::poller_t::max_fds () - 1; + + return max_requested_; +} + +zmq::ctx_t::ctx_t () : + _tag (ZMQ_CTX_TAG_VALUE_GOOD), + _starting (true), + _terminating (false), + _reaper (NULL), + _max_sockets (clipped_maxsocket (ZMQ_MAX_SOCKETS_DFLT)), + _max_msgsz (INT_MAX), + _io_thread_count (ZMQ_IO_THREADS_DFLT), + _blocky (true), + _ipv6 (false), + _zero_copy (true) +{ +#ifdef HAVE_FORK + _pid = getpid (); +#endif +#ifdef ZMQ_HAVE_VMCI + _vmci_fd = -1; + _vmci_family = -1; +#endif + + // Initialise crypto library, if needed. + zmq::random_open (); + +#ifdef ZMQ_USE_NSS + NSS_NoDB_Init (NULL); +#endif + +#ifdef ZMQ_USE_GNUTLS + gnutls_global_init (); +#endif +} + +bool zmq::ctx_t::check_tag () const +{ + return _tag == ZMQ_CTX_TAG_VALUE_GOOD; +} + +zmq::ctx_t::~ctx_t () +{ + // Check that there are no remaining _sockets. + zmq_assert (_sockets.empty ()); + + // Ask I/O threads to terminate. If stop signal wasn't sent to I/O + // thread subsequent invocation of destructor would hang-up. + const io_threads_t::size_type io_threads_size = _io_threads.size (); + for (io_threads_t::size_type i = 0; i != io_threads_size; i++) { + _io_threads[i]->stop (); + } + + // Wait till I/O threads actually terminate. + for (io_threads_t::size_type i = 0; i != io_threads_size; i++) { + LIBZMQ_DELETE (_io_threads[i]); + } + + // Deallocate the reaper thread object. + LIBZMQ_DELETE (_reaper); + + // The mailboxes in _slots themselves were deallocated with their + // corresponding io_thread/socket objects. + + // De-initialise crypto library, if needed. + zmq::random_close (); + +#ifdef ZMQ_USE_NSS + 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; +} + +bool zmq::ctx_t::valid () const +{ + return _term_mailbox.valid (); +} + +int zmq::ctx_t::terminate () +{ + _slot_sync.lock (); + + const bool save_terminating = _terminating; + _terminating = false; + + // Connect up any pending inproc connections, otherwise we will hang + pending_connections_t copy = _pending_connections; + for (pending_connections_t::iterator p = copy.begin (), end = copy.end (); + p != end; ++p) { + zmq::socket_base_t *s = create_socket (ZMQ_PAIR); + // create_socket might fail eg: out of memory/sockets limit reached + zmq_assert (s); + s->bind (p->first.c_str ()); + s->close (); + } + _terminating = save_terminating; + + if (!_starting) { +#ifdef HAVE_FORK + if (_pid != getpid ()) { + // we are a forked child process. Close all file descriptors + // inherited from the parent. + for (sockets_t::size_type i = 0, size = _sockets.size (); i != size; + i++) { + _sockets[i]->get_mailbox ()->forked (); + } + _term_mailbox.forked (); + } +#endif + + // Check whether termination was already underway, but interrupted and now + // restarted. + const bool restarted = _terminating; + _terminating = true; + + // First attempt to terminate the context. + if (!restarted) { + // First send stop command to sockets so that any blocking calls + // can be interrupted. If there are no sockets we can ask reaper + // thread to stop. + for (sockets_t::size_type i = 0, size = _sockets.size (); i != size; + i++) { + _sockets[i]->stop (); + } + if (_sockets.empty ()) + _reaper->stop (); + } + _slot_sync.unlock (); + + // Wait till reaper thread closes all the sockets. + command_t cmd; + const int rc = _term_mailbox.recv (&cmd, -1); + if (rc == -1 && errno == EINTR) + return -1; + errno_assert (rc == 0); + zmq_assert (cmd.type == command_t::done); + _slot_sync.lock (); + zmq_assert (_sockets.empty ()); + } + _slot_sync.unlock (); + +#ifdef ZMQ_HAVE_VMCI + _vmci_sync.lock (); + + VMCISock_ReleaseAFValueFd (_vmci_fd); + _vmci_family = -1; + _vmci_fd = -1; + + _vmci_sync.unlock (); +#endif + + // Deallocate the resources. + delete this; + + return 0; +} + +int zmq::ctx_t::shutdown () +{ + scoped_lock_t locker (_slot_sync); + + if (!_terminating) { + _terminating = true; + + if (!_starting) { + // Send stop command to sockets so that any blocking calls + // can be interrupted. If there are no sockets we can ask reaper + // thread to stop. + for (sockets_t::size_type i = 0, size = _sockets.size (); i != size; + i++) { + _sockets[i]->stop (); + } + if (_sockets.empty ()) + _reaper->stop (); + } + } + + return 0; +} + +int zmq::ctx_t::set (int option_, const void *optval_, size_t optvallen_) +{ + const bool is_int = (optvallen_ == sizeof (int)); + int value = 0; + if (is_int) + memcpy (&value, optval_, sizeof (int)); + + switch (option_) { + case ZMQ_MAX_SOCKETS: + if (is_int && value >= 1 && value == clipped_maxsocket (value)) { + scoped_lock_t locker (_opt_sync); + _max_sockets = value; + return 0; + } + break; + + case ZMQ_IO_THREADS: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _io_thread_count = value; + return 0; + } + break; + + case ZMQ_IPV6: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _ipv6 = (value != 0); + return 0; + } + break; + + case ZMQ_BLOCKY: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _blocky = (value != 0); + return 0; + } + break; + + case ZMQ_MAX_MSGSZ: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _max_msgsz = value < INT_MAX ? value : INT_MAX; + return 0; + } + break; + + case ZMQ_ZERO_COPY_RECV: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _zero_copy = (value != 0); + return 0; + } + break; + + default: { + return thread_ctx_t::set (option_, optval_, optvallen_); + } + } + + errno = EINVAL; + return -1; +} + +int zmq::ctx_t::get (int option_, void *optval_, const size_t *optvallen_) +{ + const bool is_int = (*optvallen_ == sizeof (int)); + int *value = static_cast (optval_); + + switch (option_) { + case ZMQ_MAX_SOCKETS: + if (is_int) { + *value = _max_sockets; + return 0; + } + break; + + case ZMQ_SOCKET_LIMIT: + if (is_int) { + *value = clipped_maxsocket (65535); + return 0; + } + break; + + case ZMQ_IO_THREADS: + if (is_int) { + *value = _io_thread_count; + return 0; + } + break; + + case ZMQ_IPV6: + if (is_int) { + *value = _ipv6; + return 0; + } + break; + + case ZMQ_BLOCKY: + if (is_int) { + *value = _blocky; + return 0; + } + break; + + case ZMQ_MAX_MSGSZ: + if (is_int) { + *value = _max_msgsz; + return 0; + } + break; + + case ZMQ_MSG_T_SIZE: + if (is_int) { + *value = sizeof (zmq_msg_t); + return 0; + } + break; + + case ZMQ_ZERO_COPY_RECV: + if (is_int) { + *value = _zero_copy; + return 0; + } + break; + + default: { + return thread_ctx_t::get (option_, optval_, optvallen_); + } + } + + errno = EINVAL; + return -1; +} + +int zmq::ctx_t::get (int option_) +{ + int optval = 0; + size_t optvallen = sizeof (int); + + if (get (option_, &optval, &optvallen) == 0) + return optval; + + errno = EINVAL; + return -1; +} + +bool zmq::ctx_t::start () +{ + // Initialise the array of mailboxes. Additional two slots are for + // zmq_ctx_term thread and reaper thread. + _opt_sync.lock (); + const int term_and_reaper_threads_count = 2; + const int mazmq = _max_sockets; + const int ios = _io_thread_count; + _opt_sync.unlock (); + const int slot_count = mazmq + ios + term_and_reaper_threads_count; + try { + _slots.reserve (slot_count); + _empty_slots.reserve (slot_count - term_and_reaper_threads_count); + } + catch (const std::bad_alloc &) { + errno = ENOMEM; + return false; + } + _slots.resize (term_and_reaper_threads_count); + + // Initialise the infrastructure for zmq_ctx_term thread. + _slots[term_tid] = &_term_mailbox; + + // Create the reaper thread. + _reaper = new (std::nothrow) reaper_t (this, reaper_tid); + if (!_reaper) { + errno = ENOMEM; + goto fail_cleanup_slots; + } + if (!_reaper->get_mailbox ()->valid ()) + goto fail_cleanup_reaper; + _slots[reaper_tid] = _reaper->get_mailbox (); + _reaper->start (); + + // Create I/O thread objects and launch them. + _slots.resize (slot_count, NULL); + + for (int i = term_and_reaper_threads_count; + i != ios + term_and_reaper_threads_count; i++) { + io_thread_t *io_thread = new (std::nothrow) io_thread_t (this, i); + if (!io_thread) { + errno = ENOMEM; + goto fail_cleanup_reaper; + } + if (!io_thread->get_mailbox ()->valid ()) { + delete io_thread; + goto fail_cleanup_reaper; + } + _io_threads.push_back (io_thread); + _slots[i] = io_thread->get_mailbox (); + io_thread->start (); + } + + // In the unused part of the slot array, create a list of empty slots. + for (int32_t i = static_cast (_slots.size ()) - 1; + i >= static_cast (ios) + term_and_reaper_threads_count; i--) { + _empty_slots.push_back (i); + } + + _starting = false; + return true; + +fail_cleanup_reaper: + _reaper->stop (); + delete _reaper; + _reaper = NULL; + +fail_cleanup_slots: + _slots.clear (); + return false; +} + +zmq::socket_base_t *zmq::ctx_t::create_socket (int type_) +{ + scoped_lock_t locker (_slot_sync); + + // Once zmq_ctx_term() or zmq_ctx_shutdown() was called, we can't create + // new sockets. + if (_terminating) { + errno = ETERM; + return NULL; + } + + if (unlikely (_starting)) { + if (!start ()) + return NULL; + } + + // If max_sockets limit was reached, return error. + if (_empty_slots.empty ()) { + errno = EMFILE; + return NULL; + } + + // Choose a slot for the socket. + const uint32_t slot = _empty_slots.back (); + _empty_slots.pop_back (); + + // Generate new unique socket ID. + const int sid = (static_cast (max_socket_id.add (1))) + 1; + + // Create the socket and register its mailbox. + socket_base_t *s = socket_base_t::create (type_, this, slot, sid); + if (!s) { + _empty_slots.push_back (slot); + return NULL; + } + _sockets.push_back (s); + _slots[slot] = s->get_mailbox (); + + return s; +} + +void zmq::ctx_t::destroy_socket (class socket_base_t *socket_) +{ + scoped_lock_t locker (_slot_sync); + + // Free the associated thread slot. + const uint32_t tid = socket_->get_tid (); + _empty_slots.push_back (tid); + _slots[tid] = NULL; + + // Remove the socket from the list of sockets. + _sockets.erase (socket_); + + // If zmq_ctx_term() was already called and there are no more socket + // we can ask reaper thread to terminate. + if (_terminating && _sockets.empty ()) + _reaper->stop (); +} + +zmq::object_t *zmq::ctx_t::get_reaper () const +{ + return _reaper; +} + +zmq::thread_ctx_t::thread_ctx_t () : + _thread_priority (ZMQ_THREAD_PRIORITY_DFLT), + _thread_sched_policy (ZMQ_THREAD_SCHED_POLICY_DFLT) +{ +} + +void zmq::thread_ctx_t::start_thread (thread_t &thread_, + thread_fn *tfn_, + void *arg_, + const char *name_) const +{ + thread_.setSchedulingParameters (_thread_priority, _thread_sched_policy, + _thread_affinity_cpus); + + char namebuf[16] = ""; + snprintf (namebuf, sizeof (namebuf), "%s%sZMQbg%s%s", + _thread_name_prefix.empty () ? "" : _thread_name_prefix.c_str (), + _thread_name_prefix.empty () ? "" : "/", name_ ? "/" : "", + name_ ? name_ : ""); + thread_.start (tfn_, arg_, namebuf); +} + +int zmq::thread_ctx_t::set (int option_, const void *optval_, size_t optvallen_) +{ + const bool is_int = (optvallen_ == sizeof (int)); + int value = 0; + if (is_int) + memcpy (&value, optval_, sizeof (int)); + + switch (option_) { + case ZMQ_THREAD_SCHED_POLICY: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _thread_sched_policy = value; + return 0; + } + break; + + case ZMQ_THREAD_AFFINITY_CPU_ADD: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _thread_affinity_cpus.insert (value); + return 0; + } + break; + + case ZMQ_THREAD_AFFINITY_CPU_REMOVE: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + if (0 == _thread_affinity_cpus.erase (value)) { + errno = EINVAL; + return -1; + } + return 0; + } + break; + + case ZMQ_THREAD_PRIORITY: + if (is_int && value >= 0) { + scoped_lock_t locker (_opt_sync); + _thread_priority = value; + return 0; + } + break; + + case ZMQ_THREAD_NAME_PREFIX: + // start_thread() allows max 16 chars for thread name + if (is_int) { + std::ostringstream s; + s << value; + scoped_lock_t locker (_opt_sync); + _thread_name_prefix = s.str (); + return 0; + } else if (optvallen_ > 0 && optvallen_ <= 16) { + scoped_lock_t locker (_opt_sync); + _thread_name_prefix.assign (static_cast (optval_), + optvallen_); + return 0; + } + break; + } + + errno = EINVAL; + return -1; +} + +int zmq::thread_ctx_t::get (int option_, + void *optval_, + const size_t *optvallen_) +{ + const bool is_int = (*optvallen_ == sizeof (int)); + int *value = static_cast (optval_); + + switch (option_) { + case ZMQ_THREAD_SCHED_POLICY: + if (is_int) { + scoped_lock_t locker (_opt_sync); + *value = _thread_sched_policy; + return 0; + } + break; + + case ZMQ_THREAD_NAME_PREFIX: + if (is_int) { + scoped_lock_t locker (_opt_sync); + *value = atoi (_thread_name_prefix.c_str ()); + return 0; + } else if (*optvallen_ >= _thread_name_prefix.size ()) { + scoped_lock_t locker (_opt_sync); + memcpy (optval_, _thread_name_prefix.data (), + _thread_name_prefix.size ()); + return 0; + } + break; + } + + errno = EINVAL; + return -1; +} + +void zmq::ctx_t::send_command (uint32_t tid_, const command_t &command_) +{ + _slots[tid_]->send (command_); +} + +zmq::io_thread_t *zmq::ctx_t::choose_io_thread (uint64_t affinity_) +{ + if (_io_threads.empty ()) + return NULL; + + // Find the I/O thread with minimum load. + int min_load = -1; + io_thread_t *selected_io_thread = NULL; + for (io_threads_t::size_type i = 0, size = _io_threads.size (); i != size; + i++) { + if (!affinity_ || (affinity_ & (uint64_t (1) << i))) { + const int load = _io_threads[i]->get_load (); + if (selected_io_thread == NULL || load < min_load) { + min_load = load; + selected_io_thread = _io_threads[i]; + } + } + } + return selected_io_thread; +} + +int zmq::ctx_t::register_endpoint (const char *addr_, + const endpoint_t &endpoint_) +{ + scoped_lock_t locker (_endpoints_sync); + + const bool inserted = + _endpoints.ZMQ_MAP_INSERT_OR_EMPLACE (std::string (addr_), endpoint_) + .second; + if (!inserted) { + errno = EADDRINUSE; + return -1; + } + return 0; +} + +int zmq::ctx_t::unregister_endpoint (const std::string &addr_, + const socket_base_t *const socket_) +{ + scoped_lock_t locker (_endpoints_sync); + + const endpoints_t::iterator it = _endpoints.find (addr_); + if (it == _endpoints.end () || it->second.socket != socket_) { + errno = ENOENT; + return -1; + } + + // Remove endpoint. + _endpoints.erase (it); + + return 0; +} + +void zmq::ctx_t::unregister_endpoints (const socket_base_t *const socket_) +{ + scoped_lock_t locker (_endpoints_sync); + + for (endpoints_t::iterator it = _endpoints.begin (), + end = _endpoints.end (); + it != end;) { + if (it->second.socket == socket_) +#if __cplusplus >= 201103L || (defined _MSC_VER && _MSC_VER >= 1700) + it = _endpoints.erase (it); +#else + _endpoints.erase (it++); +#endif + else + ++it; + } +} + +zmq::endpoint_t zmq::ctx_t::find_endpoint (const char *addr_) +{ + scoped_lock_t locker (_endpoints_sync); + + endpoints_t::iterator it = _endpoints.find (addr_); + if (it == _endpoints.end ()) { + errno = ECONNREFUSED; + endpoint_t empty = {NULL, options_t ()}; + return empty; + } + endpoint_t endpoint = it->second; + + // Increment the command sequence number of the peer so that it won't + // get deallocated until "bind" command is issued by the caller. + // The subsequent 'bind' has to be called with inc_seqnum parameter + // set to false, so that the seqnum isn't incremented twice. + endpoint.socket->inc_seqnum (); + + return endpoint; +} + +void zmq::ctx_t::pend_connection (const std::string &addr_, + const endpoint_t &endpoint_, + pipe_t **pipes_) +{ + scoped_lock_t locker (_endpoints_sync); + + const pending_connection_t pending_connection = {endpoint_, pipes_[0], + pipes_[1]}; + + const endpoints_t::iterator it = _endpoints.find (addr_); + if (it == _endpoints.end ()) { + // Still no bind. + endpoint_.socket->inc_seqnum (); + _pending_connections.ZMQ_MAP_INSERT_OR_EMPLACE (addr_, + pending_connection); + } else { + // Bind has happened in the mean time, connect directly + connect_inproc_sockets (it->second.socket, it->second.options, + pending_connection, connect_side); + } +} + +void zmq::ctx_t::connect_pending (const char *addr_, + zmq::socket_base_t *bind_socket_) +{ + scoped_lock_t locker (_endpoints_sync); + + const std::pair + pending = _pending_connections.equal_range (addr_); + for (pending_connections_t::iterator p = pending.first; p != pending.second; + ++p) + connect_inproc_sockets (bind_socket_, _endpoints[addr_].options, + p->second, bind_side); + + _pending_connections.erase (pending.first, pending.second); +} + +void zmq::ctx_t::connect_inproc_sockets ( + zmq::socket_base_t *bind_socket_, + const options_t &bind_options_, + const pending_connection_t &pending_connection_, + side side_) +{ + bind_socket_->inc_seqnum (); + pending_connection_.bind_pipe->set_tid (bind_socket_->get_tid ()); + + if (!bind_options_.recv_routing_id) { + msg_t msg; + const bool ok = pending_connection_.bind_pipe->read (&msg); + zmq_assert (ok); + const int rc = msg.close (); + errno_assert (rc == 0); + } + + if (!get_effective_conflate_option (pending_connection_.endpoint.options)) { + pending_connection_.connect_pipe->set_hwms_boost (bind_options_.sndhwm, + bind_options_.rcvhwm); + pending_connection_.bind_pipe->set_hwms_boost ( + pending_connection_.endpoint.options.sndhwm, + pending_connection_.endpoint.options.rcvhwm); + + pending_connection_.connect_pipe->set_hwms ( + pending_connection_.endpoint.options.rcvhwm, + pending_connection_.endpoint.options.sndhwm); + pending_connection_.bind_pipe->set_hwms (bind_options_.rcvhwm, + bind_options_.sndhwm); + } else { + pending_connection_.connect_pipe->set_hwms (-1, -1); + pending_connection_.bind_pipe->set_hwms (-1, -1); + } + +#ifdef ZMQ_BUILD_DRAFT_API + if (bind_options_.can_recv_disconnect_msg + && !bind_options_.disconnect_msg.empty ()) + pending_connection_.connect_pipe->set_disconnect_msg ( + bind_options_.disconnect_msg); +#endif + + if (side_ == bind_side) { + command_t cmd; + cmd.type = command_t::bind; + cmd.args.bind.pipe = pending_connection_.bind_pipe; + bind_socket_->process_command (cmd); + bind_socket_->send_inproc_connected ( + pending_connection_.endpoint.socket); + } else + pending_connection_.connect_pipe->send_bind ( + bind_socket_, pending_connection_.bind_pipe, false); + + // When a ctx is terminated all pending inproc connection will be + // connected, but the socket will already be closed and the pipe will be + // in waiting_for_delimiter state, which means no more writes can be done + // and the routing id write fails and causes an assert. Check if the socket + // is open before sending. + if (pending_connection_.endpoint.options.recv_routing_id + && pending_connection_.endpoint.socket->check_tag ()) { + send_routing_id (pending_connection_.bind_pipe, bind_options_); + } + +#ifdef ZMQ_BUILD_DRAFT_API + // If set, send the hello msg of the bind socket to the pending connection. + if (bind_options_.can_send_hello_msg + && bind_options_.hello_msg.size () > 0) { + send_hello_msg (pending_connection_.bind_pipe, bind_options_); + } +#endif +} + +#ifdef ZMQ_HAVE_VMCI + +int zmq::ctx_t::get_vmci_socket_family () +{ + zmq::scoped_lock_t locker (_vmci_sync); + + if (_vmci_fd == -1) { + _vmci_family = VMCISock_GetAFValueFd (&_vmci_fd); + + if (_vmci_fd != -1) { +#ifdef FD_CLOEXEC + int rc = fcntl (_vmci_fd, F_SETFD, FD_CLOEXEC); + errno_assert (rc != -1); +#endif + } + } + + return _vmci_family; +} + +#endif + +// The last used socket ID, or 0 if no socket was used so far. Note that this +// is a global variable. Thus, even sockets created in different contexts have +// unique IDs. +zmq::atomic_counter_t zmq::ctx_t::max_socket_id; diff --git a/3rd/libzmq/src/ctx.hpp b/3rd/libzmq/src/ctx.hpp new file mode 100644 index 00000000..0ccd68fa --- /dev/null +++ b/3rd/libzmq/src/ctx.hpp @@ -0,0 +1,271 @@ +/* + Copyright (c) 2007-2017 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 . +*/ + +#ifndef __ZMQ_CTX_HPP_INCLUDED__ +#define __ZMQ_CTX_HPP_INCLUDED__ + +#include +#include +#include +#include + +#include "mailbox.hpp" +#include "array.hpp" +#include "config.hpp" +#include "mutex.hpp" +#include "stdint.hpp" +#include "options.hpp" +#include "atomic_counter.hpp" +#include "thread.hpp" + +namespace zmq +{ +class object_t; +class io_thread_t; +class socket_base_t; +class reaper_t; +class pipe_t; + +// Information associated with inproc endpoint. Note that endpoint options +// are registered as well so that the peer can access them without a need +// for synchronisation, handshaking or similar. +struct endpoint_t +{ + socket_base_t *socket; + options_t options; +}; + +class thread_ctx_t +{ + public: + thread_ctx_t (); + + // Start a new thread with proper scheduling parameters. + void start_thread (thread_t &thread_, + thread_fn *tfn_, + void *arg_, + const char *name_ = NULL) const; + + int set (int option_, const void *optval_, size_t optvallen_); + int get (int option_, void *optval_, const size_t *optvallen_); + + protected: + // Synchronisation of access to context options. + mutex_t _opt_sync; + + private: + // Thread parameters. + int _thread_priority; + int _thread_sched_policy; + std::set _thread_affinity_cpus; + std::string _thread_name_prefix; +}; + +// Context object encapsulates all the global state associated with +// the library. + +class ctx_t ZMQ_FINAL : public thread_ctx_t +{ + public: + // Create the context object. + ctx_t (); + + // Returns false if object is not a context. + bool check_tag () const; + + // This function is called when user invokes zmq_ctx_term. If there are + // no more sockets open it'll cause all the infrastructure to be shut + // down. If there are open sockets still, the deallocation happens + // after the last one is closed. + int terminate (); + + // This function starts the terminate process by unblocking any blocking + // operations currently in progress and stopping any more socket activity + // (except zmq_close). + // This function is non-blocking. + // terminate must still be called afterwards. + // This function is optional, terminate will unblock any current + // operations as well. + int shutdown (); + + // Set and get context properties. + int set (int option_, const void *optval_, size_t optvallen_); + int get (int option_, void *optval_, const size_t *optvallen_); + int get (int option_); + + // Create and destroy a socket. + zmq::socket_base_t *create_socket (int type_); + void destroy_socket (zmq::socket_base_t *socket_); + + // Send command to the destination thread. + void send_command (uint32_t tid_, const command_t &command_); + + // Returns the I/O thread that is the least busy at the moment. + // Affinity specifies which I/O threads are eligible (0 = all). + // Returns NULL if no I/O thread is available. + zmq::io_thread_t *choose_io_thread (uint64_t affinity_); + + // Returns reaper thread object. + zmq::object_t *get_reaper () const; + + // Management of inproc endpoints. + int register_endpoint (const char *addr_, const endpoint_t &endpoint_); + int unregister_endpoint (const std::string &addr_, + const socket_base_t *socket_); + void unregister_endpoints (const zmq::socket_base_t *socket_); + endpoint_t find_endpoint (const char *addr_); + void pend_connection (const std::string &addr_, + const endpoint_t &endpoint_, + pipe_t **pipes_); + void connect_pending (const char *addr_, zmq::socket_base_t *bind_socket_); + +#ifdef ZMQ_HAVE_VMCI + // Return family for the VMCI socket or -1 if it's not available. + int get_vmci_socket_family (); +#endif + + enum + { + term_tid = 0, + reaper_tid = 1 + }; + + ~ctx_t (); + + bool valid () const; + + private: + bool start (); + + struct pending_connection_t + { + endpoint_t endpoint; + pipe_t *connect_pipe; + pipe_t *bind_pipe; + }; + + // Used to check whether the object is a context. + uint32_t _tag; + + // Sockets belonging to this context. We need the list so that + // we can notify the sockets when zmq_ctx_term() is called. + // The sockets will return ETERM then. + typedef array_t sockets_t; + sockets_t _sockets; + + // List of unused thread slots. + typedef std::vector empty_slots_t; + empty_slots_t _empty_slots; + + // If true, zmq_init has been called but no socket has been created + // yet. Launching of I/O threads is delayed. + bool _starting; + + // If true, zmq_ctx_term was already called. + bool _terminating; + + // Synchronisation of accesses to global slot-related data: + // sockets, empty_slots, terminating. It also synchronises + // access to zombie sockets as such (as opposed to slots) and provides + // a memory barrier to ensure that all CPU cores see the same data. + mutex_t _slot_sync; + + // The reaper thread. + zmq::reaper_t *_reaper; + + // I/O threads. + typedef std::vector io_threads_t; + io_threads_t _io_threads; + + // Array of pointers to mailboxes for both application and I/O threads. + std::vector _slots; + + // Mailbox for zmq_ctx_term thread. + mailbox_t _term_mailbox; + + // List of inproc endpoints within this context. + typedef std::map endpoints_t; + endpoints_t _endpoints; + + // List of inproc connection endpoints pending a bind + typedef std::multimap + pending_connections_t; + pending_connections_t _pending_connections; + + // Synchronisation of access to the list of inproc endpoints. + mutex_t _endpoints_sync; + + // Maximum socket ID. + static atomic_counter_t max_socket_id; + + // Maximum number of sockets that can be opened at the same time. + int _max_sockets; + + // Maximum allowed message size + int _max_msgsz; + + // Number of I/O threads to launch. + int _io_thread_count; + + // Does context wait (possibly forever) on termination? + bool _blocky; + + // Is IPv6 enabled on this context? + bool _ipv6; + + // Should we use zero copy message decoding in this context? + bool _zero_copy; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ctx_t) + +#ifdef HAVE_FORK + // the process that created this context. Used to detect forking. + pid_t _pid; +#endif + enum side + { + connect_side, + bind_side + }; + + static void + connect_inproc_sockets (zmq::socket_base_t *bind_socket_, + const options_t &bind_options_, + const pending_connection_t &pending_connection_, + side side_); + +#ifdef ZMQ_HAVE_VMCI + int _vmci_fd; + int _vmci_family; + mutex_t _vmci_sync; +#endif +}; +} + +#endif diff --git a/3rd/libzmq/src/curve_client.cpp b/3rd/libzmq/src/curve_client.cpp new file mode 100644 index 00000000..5d1f64e8 --- /dev/null +++ b/3rd/libzmq/src/curve_client.cpp @@ -0,0 +1,287 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" + +#ifdef ZMQ_HAVE_CURVE + +#include "msg.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "curve_client.hpp" +#include "wire.hpp" +#include "curve_client_tools.hpp" +#include "secure_allocator.hpp" + +zmq::curve_client_t::curve_client_t (session_base_t *session_, + const options_t &options_, + const bool downgrade_sub_) : + mechanism_base_t (session_, options_), + curve_mechanism_base_t (session_, + options_, + "CurveZMQMESSAGEC", + "CurveZMQMESSAGES", + downgrade_sub_), + _state (send_hello), + _tools (options_.curve_public_key, + options_.curve_secret_key, + options_.curve_server_key) +{ +} + +zmq::curve_client_t::~curve_client_t () +{ +} + +int zmq::curve_client_t::next_handshake_command (msg_t *msg_) +{ + int rc = 0; + + switch (_state) { + case send_hello: + rc = produce_hello (msg_); + if (rc == 0) + _state = expect_welcome; + break; + case send_initiate: + rc = produce_initiate (msg_); + if (rc == 0) + _state = expect_ready; + break; + default: + errno = EAGAIN; + rc = -1; + } + return rc; +} + +int zmq::curve_client_t::process_handshake_command (msg_t *msg_) +{ + const unsigned char *msg_data = + static_cast (msg_->data ()); + const size_t msg_size = msg_->size (); + + int rc = 0; + if (curve_client_tools_t::is_handshake_command_welcome (msg_data, msg_size)) + rc = process_welcome (msg_data, msg_size); + else if (curve_client_tools_t::is_handshake_command_ready (msg_data, + msg_size)) + rc = process_ready (msg_data, msg_size); + else if (curve_client_tools_t::is_handshake_command_error (msg_data, + msg_size)) + rc = process_error (msg_data, msg_size); + else { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + rc = -1; + } + + if (rc == 0) { + rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + } + + return rc; +} + +int zmq::curve_client_t::encode (msg_t *msg_) +{ + zmq_assert (_state == connected); + return curve_mechanism_base_t::encode (msg_); +} + +int zmq::curve_client_t::decode (msg_t *msg_) +{ + zmq_assert (_state == connected); + return curve_mechanism_base_t::decode (msg_); +} + +zmq::mechanism_t::status_t zmq::curve_client_t::status () const +{ + if (_state == connected) + return mechanism_t::ready; + if (_state == error_received) + return mechanism_t::error; + + return mechanism_t::handshaking; +} + +int zmq::curve_client_t::produce_hello (msg_t *msg_) +{ + int rc = msg_->init_size (200); + errno_assert (rc == 0); + + rc = _tools.produce_hello (msg_->data (), get_and_inc_nonce ()); + if (rc == -1) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + + // TODO this is somewhat inconsistent: we call init_size, but we may + // not close msg_; i.e. we assume that msg_ is initialized but empty + // (if it were non-empty, calling init_size might cause a leak!) + + // msg_->close (); + return -1; + } + + return 0; +} + +int zmq::curve_client_t::process_welcome (const uint8_t *msg_data_, + size_t msg_size_) +{ + const int rc = _tools.process_welcome (msg_data_, msg_size_, + get_writable_precom_buffer ()); + + if (rc == -1) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + + errno = EPROTO; + return -1; + } + + _state = send_initiate; + + return 0; +} + +int zmq::curve_client_t::produce_initiate (msg_t *msg_) +{ + const size_t metadata_length = basic_properties_len (); + std::vector > + metadata_plaintext (metadata_length); + + add_basic_properties (&metadata_plaintext[0], metadata_length); + + const size_t msg_size = + 113 + 128 + crypto_box_BOXZEROBYTES + metadata_length; + int rc = msg_->init_size (msg_size); + errno_assert (rc == 0); + + rc = _tools.produce_initiate (msg_->data (), msg_size, get_and_inc_nonce (), + &metadata_plaintext[0], metadata_length); + + if (-1 == rc) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + + // TODO see comment in produce_hello + return -1; + } + + return 0; +} + +int zmq::curve_client_t::process_ready (const uint8_t *msg_data_, + size_t msg_size_) +{ + if (msg_size_ < 30) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY); + errno = EPROTO; + return -1; + } + + const size_t clen = (msg_size_ - 14) + crypto_box_BOXZEROBYTES; + + uint8_t ready_nonce[crypto_box_NONCEBYTES]; + std::vector > ready_plaintext ( + crypto_box_ZEROBYTES + clen); + std::vector ready_box (crypto_box_BOXZEROBYTES + 16 + clen); + + std::fill (ready_box.begin (), ready_box.begin () + crypto_box_BOXZEROBYTES, + 0); + memcpy (&ready_box[crypto_box_BOXZEROBYTES], msg_data_ + 14, + clen - crypto_box_BOXZEROBYTES); + + memcpy (ready_nonce, "CurveZMQREADY---", 16); + memcpy (ready_nonce + 16, msg_data_ + 6, 8); + set_peer_nonce (get_uint64 (msg_data_ + 6)); + + int rc = crypto_box_open_afternm (&ready_plaintext[0], &ready_box[0], clen, + ready_nonce, get_precom_buffer ()); + + if (rc != 0) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + errno = EPROTO; + return -1; + } + + rc = parse_metadata (&ready_plaintext[crypto_box_ZEROBYTES], + clen - crypto_box_ZEROBYTES); + + if (rc == 0) + _state = connected; + else { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA); + errno = EPROTO; + } + + return rc; +} + +int zmq::curve_client_t::process_error (const uint8_t *msg_data_, + size_t msg_size_) +{ + if (_state != expect_welcome && _state != expect_ready) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + if (msg_size_ < 7) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); + errno = EPROTO; + return -1; + } + const size_t error_reason_len = static_cast (msg_data_[6]); + if (error_reason_len > msg_size_ - 7) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); + errno = EPROTO; + return -1; + } + const char *error_reason = reinterpret_cast (msg_data_) + 7; + handle_error_reason (error_reason, error_reason_len); + _state = error_received; + return 0; +} + +#endif diff --git a/3rd/libzmq/src/curve_client.hpp b/3rd/libzmq/src/curve_client.hpp new file mode 100644 index 00000000..9d51d3a0 --- /dev/null +++ b/3rd/libzmq/src/curve_client.hpp @@ -0,0 +1,86 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CURVE_CLIENT_HPP_INCLUDED__ +#define __ZMQ_CURVE_CLIENT_HPP_INCLUDED__ + +#ifdef ZMQ_HAVE_CURVE + +#include "curve_mechanism_base.hpp" +#include "options.hpp" +#include "curve_client_tools.hpp" + +namespace zmq +{ +class msg_t; +class session_base_t; + +class curve_client_t ZMQ_FINAL : public curve_mechanism_base_t +{ + public: + curve_client_t (session_base_t *session_, + const options_t &options_, + const bool downgrade_sub_); + ~curve_client_t () ZMQ_FINAL; + + // mechanism implementation + int next_handshake_command (msg_t *msg_) ZMQ_FINAL; + int process_handshake_command (msg_t *msg_) ZMQ_FINAL; + int encode (msg_t *msg_) ZMQ_FINAL; + int decode (msg_t *msg_) ZMQ_FINAL; + status_t status () const ZMQ_FINAL; + + private: + enum state_t + { + send_hello, + expect_welcome, + send_initiate, + expect_ready, + error_received, + connected + }; + + // Current FSM state + state_t _state; + + // CURVE protocol tools + curve_client_tools_t _tools; + + int produce_hello (msg_t *msg_); + int process_welcome (const uint8_t *msg_data_, size_t msg_size_); + int produce_initiate (msg_t *msg_); + int process_ready (const uint8_t *msg_data_, size_t msg_size_); + int process_error (const uint8_t *msg_data_, size_t msg_size_); +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/curve_client_tools.hpp b/3rd/libzmq/src/curve_client_tools.hpp new file mode 100644 index 00000000..3c098ba9 --- /dev/null +++ b/3rd/libzmq/src/curve_client_tools.hpp @@ -0,0 +1,316 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CURVE_CLIENT_TOOLS_HPP_INCLUDED__ +#define __ZMQ_CURVE_CLIENT_TOOLS_HPP_INCLUDED__ + +#ifdef ZMQ_HAVE_CURVE + +#if defined(ZMQ_USE_TWEETNACL) +#include "tweetnacl.h" +#elif defined(ZMQ_USE_LIBSODIUM) +#include "sodium.h" +#endif + +#if crypto_box_NONCEBYTES != 24 || crypto_box_PUBLICKEYBYTES != 32 \ + || crypto_box_SECRETKEYBYTES != 32 || crypto_box_ZEROBYTES != 32 \ + || crypto_box_BOXZEROBYTES != 16 +#error "CURVE library not built properly" +#endif + +#include "wire.hpp" +#include "err.hpp" +#include "secure_allocator.hpp" + +#include + +namespace zmq +{ +struct curve_client_tools_t +{ + static int produce_hello (void *data_, + const uint8_t *server_key_, + const uint64_t cn_nonce_, + const uint8_t *cn_public_, + const uint8_t *cn_secret_) + { + uint8_t hello_nonce[crypto_box_NONCEBYTES]; + std::vector > hello_plaintext ( + crypto_box_ZEROBYTES + 64, 0); + uint8_t hello_box[crypto_box_BOXZEROBYTES + 80]; + + // Prepare the full nonce + memcpy (hello_nonce, "CurveZMQHELLO---", 16); + put_uint64 (hello_nonce + 16, cn_nonce_); + + // Create Box [64 * %x0](C'->S) + const int rc = + crypto_box (hello_box, &hello_plaintext[0], hello_plaintext.size (), + hello_nonce, server_key_, cn_secret_); + if (rc == -1) + return -1; + + uint8_t *hello = static_cast (data_); + + memcpy (hello, "\x05HELLO", 6); + // CurveZMQ major and minor version numbers + memcpy (hello + 6, "\1\0", 2); + // Anti-amplification padding + memset (hello + 8, 0, 72); + // Client public connection key + memcpy (hello + 80, cn_public_, crypto_box_PUBLICKEYBYTES); + // Short nonce, prefixed by "CurveZMQHELLO---" + memcpy (hello + 112, hello_nonce + 16, 8); + // Signature, Box [64 * %x0](C'->S) + memcpy (hello + 120, hello_box + crypto_box_BOXZEROBYTES, 80); + + return 0; + } + + static int process_welcome (const uint8_t *msg_data_, + size_t msg_size_, + const uint8_t *server_key_, + const uint8_t *cn_secret_, + uint8_t *cn_server_, + uint8_t *cn_cookie_, + uint8_t *cn_precom_) + { + if (msg_size_ != 168) { + errno = EPROTO; + return -1; + } + + uint8_t welcome_nonce[crypto_box_NONCEBYTES]; + std::vector > welcome_plaintext ( + crypto_box_ZEROBYTES + 128); + uint8_t welcome_box[crypto_box_BOXZEROBYTES + 144]; + + // Open Box [S' + cookie](C'->S) + memset (welcome_box, 0, crypto_box_BOXZEROBYTES); + memcpy (welcome_box + crypto_box_BOXZEROBYTES, msg_data_ + 24, 144); + + memcpy (welcome_nonce, "WELCOME-", 8); + memcpy (welcome_nonce + 8, msg_data_ + 8, 16); + + int rc = crypto_box_open (&welcome_plaintext[0], welcome_box, + sizeof welcome_box, welcome_nonce, + server_key_, cn_secret_); + if (rc != 0) { + errno = EPROTO; + return -1; + } + + memcpy (cn_server_, &welcome_plaintext[crypto_box_ZEROBYTES], 32); + memcpy (cn_cookie_, &welcome_plaintext[crypto_box_ZEROBYTES + 32], + 16 + 80); + + // Message independent precomputation + rc = crypto_box_beforenm (cn_precom_, cn_server_, cn_secret_); + zmq_assert (rc == 0); + + return 0; + } + + static int produce_initiate (void *data_, + size_t size_, + const uint64_t cn_nonce_, + const uint8_t *server_key_, + const uint8_t *public_key_, + const uint8_t *secret_key_, + const uint8_t *cn_public_, + const uint8_t *cn_secret_, + const uint8_t *cn_server_, + const uint8_t *cn_cookie_, + const uint8_t *metadata_plaintext_, + const size_t metadata_length_) + { + uint8_t vouch_nonce[crypto_box_NONCEBYTES]; + std::vector > vouch_plaintext ( + crypto_box_ZEROBYTES + 64); + uint8_t vouch_box[crypto_box_BOXZEROBYTES + 80]; + + // Create vouch = Box [C',S](C->S') + std::fill (vouch_plaintext.begin (), + vouch_plaintext.begin () + crypto_box_ZEROBYTES, 0); + memcpy (&vouch_plaintext[crypto_box_ZEROBYTES], cn_public_, 32); + memcpy (&vouch_plaintext[crypto_box_ZEROBYTES + 32], server_key_, 32); + + memset (vouch_nonce, 0, crypto_box_NONCEBYTES); + memcpy (vouch_nonce, "VOUCH---", 8); + randombytes (vouch_nonce + 8, 16); + + int rc = + crypto_box (vouch_box, &vouch_plaintext[0], vouch_plaintext.size (), + vouch_nonce, cn_server_, secret_key_); + if (rc == -1) + return -1; + + uint8_t initiate_nonce[crypto_box_NONCEBYTES]; + std::vector initiate_box (crypto_box_BOXZEROBYTES + 144 + + metadata_length_); + std::vector > initiate_plaintext ( + crypto_box_ZEROBYTES + 128 + metadata_length_); + + // Create Box [C + vouch + metadata](C'->S') + std::fill (initiate_plaintext.begin (), + initiate_plaintext.begin () + crypto_box_ZEROBYTES, 0); + memcpy (&initiate_plaintext[crypto_box_ZEROBYTES], public_key_, 32); + memcpy (&initiate_plaintext[crypto_box_ZEROBYTES + 32], vouch_nonce + 8, + 16); + memcpy (&initiate_plaintext[crypto_box_ZEROBYTES + 48], + vouch_box + crypto_box_BOXZEROBYTES, 80); + if (metadata_length_) { + memcpy (&initiate_plaintext[crypto_box_ZEROBYTES + 48 + 80], + metadata_plaintext_, metadata_length_); + } + + memcpy (initiate_nonce, "CurveZMQINITIATE", 16); + put_uint64 (initiate_nonce + 16, cn_nonce_); + + rc = crypto_box (&initiate_box[0], &initiate_plaintext[0], + crypto_box_ZEROBYTES + 128 + metadata_length_, + initiate_nonce, cn_server_, cn_secret_); + + if (rc == -1) + return -1; + + uint8_t *initiate = static_cast (data_); + + zmq_assert (size_ + == 113 + 128 + crypto_box_BOXZEROBYTES + metadata_length_); + + memcpy (initiate, "\x08INITIATE", 9); + // Cookie provided by the server in the WELCOME command + memcpy (initiate + 9, cn_cookie_, 96); + // Short nonce, prefixed by "CurveZMQINITIATE" + memcpy (initiate + 105, initiate_nonce + 16, 8); + // Box [C + vouch + metadata](C'->S') + memcpy (initiate + 113, &initiate_box[crypto_box_BOXZEROBYTES], + 128 + metadata_length_ + crypto_box_BOXZEROBYTES); + + return 0; + } + + static bool is_handshake_command_welcome (const uint8_t *msg_data_, + const size_t msg_size_) + { + return is_handshake_command (msg_data_, msg_size_, "\7WELCOME"); + } + + static bool is_handshake_command_ready (const uint8_t *msg_data_, + const size_t msg_size_) + { + return is_handshake_command (msg_data_, msg_size_, "\5READY"); + } + + static bool is_handshake_command_error (const uint8_t *msg_data_, + const size_t msg_size_) + { + return is_handshake_command (msg_data_, msg_size_, "\5ERROR"); + } + + // non-static functions + curve_client_tools_t ( + const uint8_t (&curve_public_key_)[crypto_box_PUBLICKEYBYTES], + const uint8_t (&curve_secret_key_)[crypto_box_SECRETKEYBYTES], + const uint8_t (&curve_server_key_)[crypto_box_PUBLICKEYBYTES]) + { + int rc; + memcpy (public_key, curve_public_key_, crypto_box_PUBLICKEYBYTES); + memcpy (secret_key, curve_secret_key_, crypto_box_SECRETKEYBYTES); + memcpy (server_key, curve_server_key_, crypto_box_PUBLICKEYBYTES); + + // Generate short-term key pair + memset (cn_secret, 0, crypto_box_SECRETKEYBYTES); + memset (cn_public, 0, crypto_box_PUBLICKEYBYTES); + rc = crypto_box_keypair (cn_public, cn_secret); + zmq_assert (rc == 0); + } + + int produce_hello (void *data_, const uint64_t cn_nonce_) const + { + return produce_hello (data_, server_key, cn_nonce_, cn_public, + cn_secret); + } + + int process_welcome (const uint8_t *msg_data_, + size_t msg_size_, + uint8_t *cn_precom_) + { + return process_welcome (msg_data_, msg_size_, server_key, cn_secret, + cn_server, cn_cookie, cn_precom_); + } + + int produce_initiate (void *data_, + size_t size_, + const uint64_t cn_nonce_, + const uint8_t *metadata_plaintext_, + const size_t metadata_length_) const + { + return produce_initiate (data_, size_, cn_nonce_, server_key, + public_key, secret_key, cn_public, cn_secret, + cn_server, cn_cookie, metadata_plaintext_, + metadata_length_); + } + + // Our public key (C) + uint8_t public_key[crypto_box_PUBLICKEYBYTES]; + + // Our secret key (c) + uint8_t secret_key[crypto_box_SECRETKEYBYTES]; + + // Our short-term public key (C') + uint8_t cn_public[crypto_box_PUBLICKEYBYTES]; + + // Our short-term secret key (c') + uint8_t cn_secret[crypto_box_SECRETKEYBYTES]; + + // Server's public key (S) + uint8_t server_key[crypto_box_PUBLICKEYBYTES]; + + // Server's short-term public key (S') + uint8_t cn_server[crypto_box_PUBLICKEYBYTES]; + + // Cookie received from server + uint8_t cn_cookie[16 + 80]; + + private: + template + static bool is_handshake_command (const uint8_t *msg_data_, + const size_t msg_size_, + const char (&prefix_)[N]) + { + return msg_size_ >= (N - 1) && !memcmp (msg_data_, prefix_, N - 1); + } +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/curve_mechanism_base.cpp b/3rd/libzmq/src/curve_mechanism_base.cpp new file mode 100644 index 00000000..6f0173e1 --- /dev/null +++ b/3rd/libzmq/src/curve_mechanism_base.cpp @@ -0,0 +1,313 @@ +/* + 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 . +*/ + + +#include "precompiled.hpp" +#include "curve_mechanism_base.hpp" +#include "msg.hpp" +#include "wire.hpp" +#include "session_base.hpp" + +#ifdef ZMQ_HAVE_CURVE + +#ifdef ZMQ_USE_LIBSODIUM +// libsodium added crypto_box_easy_afternm and crypto_box_open_easy_afternm with +// https: //github.com/jedisct1/libsodium/commit/aaf5fbf2e53a33b18d8ea9bdf2c6f73d7acc8c3e +#if SODIUM_LIBRARY_VERSION_MAJOR > 7 \ + || (SODIUM_LIBRARY_VERSION_MAJOR == 7 && SODIUM_LIBRARY_VERSION_MINOR >= 4) +#define ZMQ_HAVE_CRYPTO_BOX_EASY_FNS 1 +#endif +#endif + +zmq::curve_mechanism_base_t::curve_mechanism_base_t ( + session_base_t *session_, + const options_t &options_, + const char *encode_nonce_prefix_, + const char *decode_nonce_prefix_, + const bool downgrade_sub_) : + mechanism_base_t (session_, options_), + curve_encoding_t ( + encode_nonce_prefix_, decode_nonce_prefix_, downgrade_sub_) +{ +} + +int zmq::curve_mechanism_base_t::encode (msg_t *msg_) +{ + return curve_encoding_t::encode (msg_); +} + +int zmq::curve_mechanism_base_t::decode (msg_t *msg_) +{ + int rc = check_basic_command_structure (msg_); + if (rc == -1) + return -1; + + int error_event_code; + rc = curve_encoding_t::decode (msg_, &error_event_code); + if (-1 == rc) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), error_event_code); + } + + return rc; +} + +zmq::curve_encoding_t::curve_encoding_t (const char *encode_nonce_prefix_, + const char *decode_nonce_prefix_, + const bool downgrade_sub_) : + _encode_nonce_prefix (encode_nonce_prefix_), + _decode_nonce_prefix (decode_nonce_prefix_), + _cn_nonce (1), + _cn_peer_nonce (1), + _downgrade_sub (downgrade_sub_) +{ +} + +// Right now, we only transport the lower two bit flags of zmq::msg_t, so they +// are binary identical, and we can just use a bitmask to select them. If we +// happened to add more flags, this might change. +static const uint8_t flag_mask = zmq::msg_t::more | zmq::msg_t::command; +static const size_t flags_len = 1; +static const size_t nonce_prefix_len = 16; +static const char message_command[] = "\x07MESSAGE"; +static const size_t message_command_len = sizeof (message_command) - 1; +static const size_t message_header_len = + message_command_len + sizeof (zmq::curve_encoding_t::nonce_t); + +#ifndef ZMQ_USE_LIBSODIUM +static const size_t crypto_box_MACBYTES = 16; +#endif + +int zmq::curve_encoding_t::check_validity (msg_t *msg_, int *error_event_code_) +{ + const size_t size = msg_->size (); + const uint8_t *const message = static_cast (msg_->data ()); + + if (size < message_command_len + || 0 != memcmp (message, message_command, message_command_len)) { + *error_event_code_ = ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND; + errno = EPROTO; + return -1; + } + + if (size < message_header_len + crypto_box_MACBYTES + flags_len) { + *error_event_code_ = ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE; + errno = EPROTO; + return -1; + } + + { + const uint64_t nonce = get_uint64 (message + message_command_len); + if (nonce <= _cn_peer_nonce) { + *error_event_code_ = ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE; + errno = EPROTO; + return -1; + } + set_peer_nonce (nonce); + } + + return 0; +} + +int zmq::curve_encoding_t::encode (msg_t *msg_) +{ + size_t sub_cancel_len = 0; + uint8_t message_nonce[crypto_box_NONCEBYTES]; + memcpy (message_nonce, _encode_nonce_prefix, nonce_prefix_len); + put_uint64 (message_nonce + nonce_prefix_len, get_and_inc_nonce ()); + + if (msg_->is_subscribe () || msg_->is_cancel ()) { + if (_downgrade_sub) + sub_cancel_len = 1; + else + sub_cancel_len = msg_->is_cancel () + ? zmq::msg_t::cancel_cmd_name_size + : zmq::msg_t::sub_cmd_name_size; + } + +#ifdef ZMQ_HAVE_CRYPTO_BOX_EASY_FNS + const size_t mlen = flags_len + sub_cancel_len + msg_->size (); + std::vector message_plaintext (mlen); +#else + const size_t mlen = + crypto_box_ZEROBYTES + flags_len + sub_cancel_len + msg_->size (); + std::vector message_plaintext_with_zerobytes (mlen); + uint8_t *const message_plaintext = + &message_plaintext_with_zerobytes[crypto_box_ZEROBYTES]; + + std::fill (message_plaintext_with_zerobytes.begin (), + message_plaintext_with_zerobytes.begin () + crypto_box_ZEROBYTES, + 0); +#endif + + const uint8_t flags = msg_->flags () & flag_mask; + message_plaintext[0] = flags; + + // For backward compatibility subscribe/cancel command messages are not stored with + // the message flags, and are encoded in the encoder, so that messages for < 3.0 peers + // can be encoded in the "old" 0/1 way rather than as commands. + if (sub_cancel_len == 1) + message_plaintext[flags_len] = msg_->is_subscribe () ? 1 : 0; + else if (sub_cancel_len == zmq::msg_t::sub_cmd_name_size) { + message_plaintext[0] |= zmq::msg_t::command; + memcpy (&message_plaintext[flags_len], zmq::sub_cmd_name, + zmq::msg_t::sub_cmd_name_size); + } else if (sub_cancel_len == zmq::msg_t::cancel_cmd_name_size) { + message_plaintext[0] |= zmq::msg_t::command; + memcpy (&message_plaintext[flags_len], zmq::cancel_cmd_name, + zmq::msg_t::cancel_cmd_name_size); + } + + // this is copying the data from insecure memory, so there is no point in + // using secure_allocator_t for message_plaintext + if (msg_->size () > 0) + memcpy (&message_plaintext[flags_len + sub_cancel_len], msg_->data (), + msg_->size ()); + +#ifdef ZMQ_HAVE_CRYPTO_BOX_EASY_FNS + msg_t msg_box; + int rc = + msg_box.init_size (message_header_len + mlen + crypto_box_MACBYTES); + zmq_assert (rc == 0); + + rc = crypto_box_easy_afternm ( + static_cast (msg_box.data ()) + message_header_len, + &message_plaintext[0], mlen, message_nonce, _cn_precom); + zmq_assert (rc == 0); + + msg_->move (msg_box); + + uint8_t *const message = static_cast (msg_->data ()); +#else + std::vector message_box (mlen); + + int rc = + crypto_box_afternm (&message_box[0], &message_plaintext_with_zerobytes[0], + mlen, message_nonce, _cn_precom); + zmq_assert (rc == 0); + + rc = msg_->close (); + zmq_assert (rc == 0); + + rc = msg_->init_size (16 + mlen - crypto_box_BOXZEROBYTES); + zmq_assert (rc == 0); + + uint8_t *const message = static_cast (msg_->data ()); + + memcpy (message + message_header_len, &message_box[crypto_box_BOXZEROBYTES], + mlen - crypto_box_BOXZEROBYTES); +#endif + + memcpy (message, message_command, message_command_len); + memcpy (message + message_command_len, message_nonce + nonce_prefix_len, + sizeof (nonce_t)); + + return 0; +} + +int zmq::curve_encoding_t::decode (msg_t *msg_, int *error_event_code_) +{ + int rc = check_validity (msg_, error_event_code_); + if (0 != rc) { + return rc; + } + + uint8_t *const message = static_cast (msg_->data ()); + + uint8_t message_nonce[crypto_box_NONCEBYTES]; + memcpy (message_nonce, _decode_nonce_prefix, nonce_prefix_len); + memcpy (message_nonce + nonce_prefix_len, message + message_command_len, + sizeof (nonce_t)); + +#ifdef ZMQ_HAVE_CRYPTO_BOX_EASY_FNS + const size_t clen = msg_->size () - message_header_len; + + uint8_t *const message_plaintext = message + message_header_len; + + rc = crypto_box_open_easy_afternm (message_plaintext, + message + message_header_len, clen, + message_nonce, _cn_precom); +#else + const size_t clen = + crypto_box_BOXZEROBYTES + msg_->size () - message_header_len; + + std::vector message_plaintext_with_zerobytes (clen); + std::vector message_box (clen); + + std::fill (message_box.begin (), + message_box.begin () + crypto_box_BOXZEROBYTES, 0); + memcpy (&message_box[crypto_box_BOXZEROBYTES], message + message_header_len, + msg_->size () - message_header_len); + + rc = crypto_box_open_afternm (&message_plaintext_with_zerobytes[0], + &message_box[0], clen, message_nonce, + _cn_precom); + + const uint8_t *const message_plaintext = + &message_plaintext_with_zerobytes[crypto_box_ZEROBYTES]; +#endif + + if (rc == 0) { + const uint8_t flags = message_plaintext[0]; + +#ifdef ZMQ_HAVE_CRYPTO_BOX_EASY_FNS + const size_t plaintext_size = clen - flags_len - crypto_box_MACBYTES; + + if (plaintext_size > 0) { + memmove (msg_->data (), &message_plaintext[flags_len], + plaintext_size); + } + + msg_->shrink (plaintext_size); +#else + rc = msg_->close (); + zmq_assert (rc == 0); + + rc = msg_->init_size (clen - flags_len - crypto_box_ZEROBYTES); + zmq_assert (rc == 0); + + // this is copying the data to insecure memory, so there is no point in + // using secure_allocator_t for message_plaintext + if (msg_->size () > 0) { + memcpy (msg_->data (), &message_plaintext[flags_len], + msg_->size ()); + } +#endif + + msg_->set_flags (flags & flag_mask); + } else { + // CURVE I : connection key used for MESSAGE is wrong + *error_event_code_ = ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC; + errno = EPROTO; + } + + return rc; +} + +#endif diff --git a/3rd/libzmq/src/curve_mechanism_base.hpp b/3rd/libzmq/src/curve_mechanism_base.hpp new file mode 100644 index 00000000..a72965e9 --- /dev/null +++ b/3rd/libzmq/src/curve_mechanism_base.hpp @@ -0,0 +1,108 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CURVE_MECHANISM_BASE_HPP_INCLUDED__ +#define __ZMQ_CURVE_MECHANISM_BASE_HPP_INCLUDED__ + +#ifdef ZMQ_HAVE_CURVE + +#if defined(ZMQ_USE_TWEETNACL) +#include "tweetnacl.h" +#elif defined(ZMQ_USE_LIBSODIUM) +#include "sodium.h" +#endif + +#if crypto_box_NONCEBYTES != 24 || crypto_box_PUBLICKEYBYTES != 32 \ + || crypto_box_SECRETKEYBYTES != 32 || crypto_box_ZEROBYTES != 32 \ + || crypto_box_BOXZEROBYTES != 16 || crypto_secretbox_NONCEBYTES != 24 \ + || crypto_secretbox_ZEROBYTES != 32 || crypto_secretbox_BOXZEROBYTES != 16 +#error "CURVE library not built properly" +#endif + +#include "mechanism_base.hpp" +#include "options.hpp" + +#include + +namespace zmq +{ +class curve_encoding_t +{ + public: + curve_encoding_t (const char *encode_nonce_prefix_, + const char *decode_nonce_prefix_, + const bool downgrade_sub_); + + int encode (msg_t *msg_); + int decode (msg_t *msg_, int *error_event_code_); + + uint8_t *get_writable_precom_buffer () { return _cn_precom; } + const uint8_t *get_precom_buffer () const { return _cn_precom; } + + typedef uint64_t nonce_t; + + nonce_t get_and_inc_nonce () { return _cn_nonce++; } + void set_peer_nonce (nonce_t peer_nonce_) { _cn_peer_nonce = peer_nonce_; }; + + private: + int check_validity (msg_t *msg_, int *error_event_code_); + + const char *_encode_nonce_prefix; + const char *_decode_nonce_prefix; + + nonce_t _cn_nonce; + nonce_t _cn_peer_nonce; + + // Intermediary buffer used to speed up boxing and unboxing. + uint8_t _cn_precom[crypto_box_BEFORENMBYTES]; + + const bool _downgrade_sub; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (curve_encoding_t) +}; + +class curve_mechanism_base_t : public virtual mechanism_base_t, + public curve_encoding_t +{ + public: + curve_mechanism_base_t (session_base_t *session_, + const options_t &options_, + const char *encode_nonce_prefix_, + const char *decode_nonce_prefix_, + const bool downgrade_sub_); + + // mechanism implementation + int encode (msg_t *msg_) ZMQ_OVERRIDE; + int decode (msg_t *msg_) ZMQ_OVERRIDE; +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/curve_server.cpp b/3rd/libzmq/src/curve_server.cpp new file mode 100644 index 00000000..fadfc746 --- /dev/null +++ b/3rd/libzmq/src/curve_server.cpp @@ -0,0 +1,506 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" + +#ifdef ZMQ_HAVE_CURVE + +#include "msg.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "curve_server.hpp" +#include "wire.hpp" +#include "secure_allocator.hpp" + +zmq::curve_server_t::curve_server_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_, + const bool downgrade_sub_) : + mechanism_base_t (session_, options_), + zap_client_common_handshake_t ( + session_, peer_address_, options_, sending_ready), + curve_mechanism_base_t (session_, + options_, + "CurveZMQMESSAGES", + "CurveZMQMESSAGEC", + downgrade_sub_) +{ + int rc; + // Fetch our secret key from socket options + memcpy (_secret_key, options_.curve_secret_key, crypto_box_SECRETKEYBYTES); + + // Generate short-term key pair + memset (_cn_secret, 0, crypto_box_SECRETKEYBYTES); + memset (_cn_public, 0, crypto_box_PUBLICKEYBYTES); + rc = crypto_box_keypair (_cn_public, _cn_secret); + zmq_assert (rc == 0); +} + +zmq::curve_server_t::~curve_server_t () +{ +} + +int zmq::curve_server_t::next_handshake_command (msg_t *msg_) +{ + int rc = 0; + + switch (state) { + case sending_welcome: + rc = produce_welcome (msg_); + if (rc == 0) + state = waiting_for_initiate; + break; + case sending_ready: + rc = produce_ready (msg_); + if (rc == 0) + state = ready; + break; + case sending_error: + rc = produce_error (msg_); + if (rc == 0) + state = error_sent; + break; + default: + errno = EAGAIN; + rc = -1; + break; + } + return rc; +} + +int zmq::curve_server_t::process_handshake_command (msg_t *msg_) +{ + int rc = 0; + + switch (state) { + case waiting_for_hello: + rc = process_hello (msg_); + break; + case waiting_for_initiate: + rc = process_initiate (msg_); + break; + default: + // TODO I think this is not a case reachable with a misbehaving + // client. It is not an "invalid handshake command", but would be + // trying to process a handshake command in an invalid state, + // which is purely under control of this peer. + // Therefore, it should be changed to zmq_assert (false); + + // CURVE I: invalid handshake command + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED); + errno = EPROTO; + rc = -1; + break; + } + if (rc == 0) { + rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + } + return rc; +} + +int zmq::curve_server_t::encode (msg_t *msg_) +{ + zmq_assert (state == ready); + return curve_mechanism_base_t::encode (msg_); +} + +int zmq::curve_server_t::decode (msg_t *msg_) +{ + zmq_assert (state == ready); + return curve_mechanism_base_t::decode (msg_); +} + +int zmq::curve_server_t::process_hello (msg_t *msg_) +{ + int rc = check_basic_command_structure (msg_); + if (rc == -1) + return -1; + + const size_t size = msg_->size (); + const uint8_t *const hello = static_cast (msg_->data ()); + + if (size < 6 || memcmp (hello, "\x05HELLO", 6)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + + if (size != 200) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); + errno = EPROTO; + return -1; + } + + const uint8_t major = hello[6]; + const uint8_t minor = hello[7]; + + if (major != 1 || minor != 0) { + // CURVE I: client HELLO has unknown version number + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); + errno = EPROTO; + return -1; + } + + // Save client's short-term public key (C') + memcpy (_cn_client, hello + 80, 32); + + uint8_t hello_nonce[crypto_box_NONCEBYTES]; + std::vector > hello_plaintext ( + crypto_box_ZEROBYTES + 64); + uint8_t hello_box[crypto_box_BOXZEROBYTES + 80]; + + memcpy (hello_nonce, "CurveZMQHELLO---", 16); + memcpy (hello_nonce + 16, hello + 112, 8); + set_peer_nonce (get_uint64 (hello + 112)); + + memset (hello_box, 0, crypto_box_BOXZEROBYTES); + memcpy (hello_box + crypto_box_BOXZEROBYTES, hello + 120, 80); + + // Open Box [64 * %x0](C'->S) + rc = crypto_box_open (&hello_plaintext[0], hello_box, sizeof hello_box, + hello_nonce, _cn_client, _secret_key); + if (rc != 0) { + // CURVE I: cannot open client HELLO -- wrong server key? + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + errno = EPROTO; + return -1; + } + + state = sending_welcome; + return rc; +} + +int zmq::curve_server_t::produce_welcome (msg_t *msg_) +{ + uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES]; + std::vector > cookie_plaintext ( + crypto_secretbox_ZEROBYTES + 64); + uint8_t cookie_ciphertext[crypto_secretbox_BOXZEROBYTES + 80]; + + // Create full nonce for encryption + // 8-byte prefix plus 16-byte random nonce + memset (cookie_nonce, 0, crypto_secretbox_NONCEBYTES); + memcpy (cookie_nonce, "COOKIE--", 8); + randombytes (cookie_nonce + 8, 16); + + // Generate cookie = Box [C' + s'](t) + std::fill (cookie_plaintext.begin (), + cookie_plaintext.begin () + crypto_secretbox_ZEROBYTES, 0); + memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES], _cn_client, 32); + memcpy (&cookie_plaintext[crypto_secretbox_ZEROBYTES + 32], _cn_secret, 32); + + // Generate fresh cookie key + memset (_cookie_key, 0, crypto_secretbox_KEYBYTES); + randombytes (_cookie_key, crypto_secretbox_KEYBYTES); + + // Encrypt using symmetric cookie key + int rc = + crypto_secretbox (cookie_ciphertext, &cookie_plaintext[0], + cookie_plaintext.size (), cookie_nonce, _cookie_key); + zmq_assert (rc == 0); + + uint8_t welcome_nonce[crypto_box_NONCEBYTES]; + std::vector > welcome_plaintext ( + crypto_box_ZEROBYTES + 128); + uint8_t welcome_ciphertext[crypto_box_BOXZEROBYTES + 144]; + + // Create full nonce for encryption + // 8-byte prefix plus 16-byte random nonce + memset (welcome_nonce, 0, crypto_box_NONCEBYTES); + memcpy (welcome_nonce, "WELCOME-", 8); + randombytes (welcome_nonce + 8, crypto_box_NONCEBYTES - 8); + + // Create 144-byte Box [S' + cookie](S->C') + std::fill (welcome_plaintext.begin (), + welcome_plaintext.begin () + crypto_box_ZEROBYTES, 0); + memcpy (&welcome_plaintext[crypto_box_ZEROBYTES], _cn_public, 32); + memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 32], cookie_nonce + 8, + 16); + memcpy (&welcome_plaintext[crypto_box_ZEROBYTES + 48], + cookie_ciphertext + crypto_secretbox_BOXZEROBYTES, 80); + + rc = crypto_box (welcome_ciphertext, &welcome_plaintext[0], + welcome_plaintext.size (), welcome_nonce, _cn_client, + _secret_key); + + // TODO I think we should change this back to zmq_assert (rc == 0); + // as it was before https://github.com/zeromq/libzmq/pull/1832 + // The reason given there was that secret_key might be 0ed. + // But if it were, we would never get this far, since we could + // not have opened the client's hello box with a 0ed key. + + if (rc == -1) + return -1; + + rc = msg_->init_size (168); + errno_assert (rc == 0); + + uint8_t *const welcome = static_cast (msg_->data ()); + memcpy (welcome, "\x07WELCOME", 8); + memcpy (welcome + 8, welcome_nonce + 8, 16); + memcpy (welcome + 24, welcome_ciphertext + crypto_box_BOXZEROBYTES, 144); + + return 0; +} + +int zmq::curve_server_t::process_initiate (msg_t *msg_) +{ + int rc = check_basic_command_structure (msg_); + if (rc == -1) + return -1; + + const size_t size = msg_->size (); + const uint8_t *initiate = static_cast (msg_->data ()); + + if (size < 9 || memcmp (initiate, "\x08INITIATE", 9)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + + if (size < 257) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE); + errno = EPROTO; + return -1; + } + + uint8_t cookie_nonce[crypto_secretbox_NONCEBYTES]; + uint8_t cookie_plaintext[crypto_secretbox_ZEROBYTES + 64]; + uint8_t cookie_box[crypto_secretbox_BOXZEROBYTES + 80]; + + // Open Box [C' + s'](t) + memset (cookie_box, 0, crypto_secretbox_BOXZEROBYTES); + memcpy (cookie_box + crypto_secretbox_BOXZEROBYTES, initiate + 25, 80); + + memcpy (cookie_nonce, "COOKIE--", 8); + memcpy (cookie_nonce + 8, initiate + 9, 16); + + rc = crypto_secretbox_open (cookie_plaintext, cookie_box, sizeof cookie_box, + cookie_nonce, _cookie_key); + if (rc != 0) { + // CURVE I: cannot open client INITIATE cookie + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + errno = EPROTO; + return -1; + } + + // Check cookie plain text is as expected [C' + s'] + if (memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES, _cn_client, 32) + || memcmp (cookie_plaintext + crypto_secretbox_ZEROBYTES + 32, + _cn_secret, 32)) { + // TODO this case is very hard to test, as it would require a modified + // client that knows the server's secret temporary cookie key + + // CURVE I: client INITIATE cookie is not valid + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + errno = EPROTO; + return -1; + } + + const size_t clen = (size - 113) + crypto_box_BOXZEROBYTES; + + uint8_t initiate_nonce[crypto_box_NONCEBYTES]; + std::vector > initiate_plaintext ( + crypto_box_ZEROBYTES + clen); + std::vector initiate_box (crypto_box_BOXZEROBYTES + clen); + + // Open Box [C + vouch + metadata](C'->S') + std::fill (initiate_box.begin (), + initiate_box.begin () + crypto_box_BOXZEROBYTES, 0); + memcpy (&initiate_box[crypto_box_BOXZEROBYTES], initiate + 113, + clen - crypto_box_BOXZEROBYTES); + + memcpy (initiate_nonce, "CurveZMQINITIATE", 16); + memcpy (initiate_nonce + 16, initiate + 105, 8); + set_peer_nonce (get_uint64 (initiate + 105)); + + const uint8_t *client_key = &initiate_plaintext[crypto_box_ZEROBYTES]; + + rc = crypto_box_open (&initiate_plaintext[0], &initiate_box[0], clen, + initiate_nonce, _cn_client, _cn_secret); + if (rc != 0) { + // CURVE I: cannot open client INITIATE + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + errno = EPROTO; + return -1; + } + + uint8_t vouch_nonce[crypto_box_NONCEBYTES]; + std::vector > vouch_plaintext ( + crypto_box_ZEROBYTES + 64); + uint8_t vouch_box[crypto_box_BOXZEROBYTES + 80]; + + // Open Box Box [C',S](C->S') and check contents + memset (vouch_box, 0, crypto_box_BOXZEROBYTES); + memcpy (vouch_box + crypto_box_BOXZEROBYTES, + &initiate_plaintext[crypto_box_ZEROBYTES + 48], 80); + + memset (vouch_nonce, 0, crypto_box_NONCEBYTES); + memcpy (vouch_nonce, "VOUCH---", 8); + memcpy (vouch_nonce + 8, &initiate_plaintext[crypto_box_ZEROBYTES + 32], + 16); + + rc = crypto_box_open (&vouch_plaintext[0], vouch_box, sizeof vouch_box, + vouch_nonce, client_key, _cn_secret); + if (rc != 0) { + // CURVE I: cannot open client INITIATE vouch + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + errno = EPROTO; + return -1; + } + + // What we decrypted must be the client's short-term public key + if (memcmp (&vouch_plaintext[crypto_box_ZEROBYTES], _cn_client, 32)) { + // TODO this case is very hard to test, as it would require a modified + // client that knows the server's secret short-term key + + // CURVE I: invalid handshake from client (public key) + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE); + errno = EPROTO; + return -1; + } + + // Precompute connection secret from client key + rc = crypto_box_beforenm (get_writable_precom_buffer (), _cn_client, + _cn_secret); + zmq_assert (rc == 0); + + // Given this is a backward-incompatible change, it's behind a socket + // option disabled by default. + if (zap_required () || !options.zap_enforce_domain) { + // Use ZAP protocol (RFC 27) to authenticate the user. + rc = session->zap_connect (); + if (rc == 0) { + send_zap_request (client_key); + state = waiting_for_zap_reply; + + // TODO actually, it is quite unlikely that we can read the ZAP + // reply already, but removing this has some strange side-effect + // (probably because the pipe's in_active flag is true until a read + // is attempted) + if (-1 == receive_and_process_zap_reply ()) + return -1; + } else if (!options.zap_enforce_domain) { + // This supports the Stonehouse pattern (encryption without + // authentication) in legacy mode (domain set but no handler). + state = sending_ready; + } else { + session->get_socket ()->event_handshake_failed_no_detail ( + session->get_endpoint (), EFAULT); + return -1; + } + } else { + // This supports the Stonehouse pattern (encryption without authentication). + state = sending_ready; + } + + return parse_metadata (&initiate_plaintext[crypto_box_ZEROBYTES + 128], + clen - crypto_box_ZEROBYTES - 128); +} + +int zmq::curve_server_t::produce_ready (msg_t *msg_) +{ + const size_t metadata_length = basic_properties_len (); + uint8_t ready_nonce[crypto_box_NONCEBYTES]; + + std::vector > ready_plaintext ( + crypto_box_ZEROBYTES + metadata_length); + + // Create Box [metadata](S'->C') + std::fill (ready_plaintext.begin (), + ready_plaintext.begin () + crypto_box_ZEROBYTES, 0); + uint8_t *ptr = &ready_plaintext[crypto_box_ZEROBYTES]; + + ptr += add_basic_properties (ptr, metadata_length); + const size_t mlen = ptr - &ready_plaintext[0]; + + memcpy (ready_nonce, "CurveZMQREADY---", 16); + put_uint64 (ready_nonce + 16, get_and_inc_nonce ()); + + std::vector ready_box (crypto_box_BOXZEROBYTES + 16 + + metadata_length); + + int rc = crypto_box_afternm (&ready_box[0], &ready_plaintext[0], mlen, + ready_nonce, get_precom_buffer ()); + zmq_assert (rc == 0); + + rc = msg_->init_size (14 + mlen - crypto_box_BOXZEROBYTES); + errno_assert (rc == 0); + + uint8_t *ready = static_cast (msg_->data ()); + + memcpy (ready, "\x05READY", 6); + // Short nonce, prefixed by "CurveZMQREADY---" + memcpy (ready + 6, ready_nonce + 16, 8); + // Box [metadata](S'->C') + memcpy (ready + 14, &ready_box[crypto_box_BOXZEROBYTES], + mlen - crypto_box_BOXZEROBYTES); + + return 0; +} + +int zmq::curve_server_t::produce_error (msg_t *msg_) const +{ + const size_t expected_status_code_length = 3; + zmq_assert (status_code.length () == 3); + const int rc = msg_->init_size (6 + 1 + expected_status_code_length); + zmq_assert (rc == 0); + char *msg_data = static_cast (msg_->data ()); + memcpy (msg_data, "\5ERROR", 6); + msg_data[6] = expected_status_code_length; + memcpy (msg_data + 7, status_code.c_str (), expected_status_code_length); + return 0; +} + +void zmq::curve_server_t::send_zap_request (const uint8_t *key_) +{ + zap_client_t::send_zap_request ("CURVE", 5, key_, + crypto_box_PUBLICKEYBYTES); +} + +#endif diff --git a/3rd/libzmq/src/curve_server.hpp b/3rd/libzmq/src/curve_server.hpp new file mode 100644 index 00000000..995efce0 --- /dev/null +++ b/3rd/libzmq/src/curve_server.hpp @@ -0,0 +1,92 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_CURVE_SERVER_HPP_INCLUDED__ +#define __ZMQ_CURVE_SERVER_HPP_INCLUDED__ + +#ifdef ZMQ_HAVE_CURVE + +#include "curve_mechanism_base.hpp" +#include "options.hpp" +#include "zap_client.hpp" + +namespace zmq +{ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4250) +#endif +class curve_server_t ZMQ_FINAL : public zap_client_common_handshake_t, + public curve_mechanism_base_t +{ + public: + curve_server_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_, + const bool downgrade_sub_); + ~curve_server_t (); + + // mechanism implementation + int next_handshake_command (msg_t *msg_); + int process_handshake_command (msg_t *msg_); + int encode (msg_t *msg_); + int decode (msg_t *msg_); + + private: + // Our secret key (s) + uint8_t _secret_key[crypto_box_SECRETKEYBYTES]; + + // Our short-term public key (S') + uint8_t _cn_public[crypto_box_PUBLICKEYBYTES]; + + // Our short-term secret key (s') + uint8_t _cn_secret[crypto_box_SECRETKEYBYTES]; + + // Client's short-term public key (C') + uint8_t _cn_client[crypto_box_PUBLICKEYBYTES]; + + // Key used to produce cookie + uint8_t _cookie_key[crypto_secretbox_KEYBYTES]; + + int process_hello (msg_t *msg_); + int produce_welcome (msg_t *msg_); + int process_initiate (msg_t *msg_); + int produce_ready (msg_t *msg_); + int produce_error (msg_t *msg_) const; + + void send_zap_request (const uint8_t *key_); +}; +#ifdef _MSC_VER +#pragma warning(pop) +#endif +} + +#endif + +#endif diff --git a/3rd/libzmq/src/dbuffer.hpp b/3rd/libzmq/src/dbuffer.hpp new file mode 100644 index 00000000..00ea66be --- /dev/null +++ b/3rd/libzmq/src/dbuffer.hpp @@ -0,0 +1,134 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DBUFFER_HPP_INCLUDED__ +#define __ZMQ_DBUFFER_HPP_INCLUDED__ + +#include +#include +#include + +#include "mutex.hpp" +#include "msg.hpp" + +namespace zmq +{ +// dbuffer is a single-producer single-consumer double-buffer +// implementation. +// +// The producer writes to a back buffer and then tries to swap +// pointers between the back and front buffers. If it fails, +// due to the consumer reading from the front buffer, it just +// gives up, which is ok since writes are many and redundant. +// +// The reader simply reads from the front buffer. +// +// has_msg keeps track of whether there has been a not yet read +// value written, it is used by ypipe_conflate to mimic ypipe +// functionality regarding a reader being asleep + +template class dbuffer_t; + +template <> class dbuffer_t +{ + public: + dbuffer_t () : _back (&_storage[0]), _front (&_storage[1]), _has_msg (false) + { + _back->init (); + _front->init (); + } + + ~dbuffer_t () + { + _back->close (); + _front->close (); + } + + void write (const msg_t &value_) + { + zmq_assert (value_.check ()); + *_back = value_; + + zmq_assert (_back->check ()); + + if (_sync.try_lock ()) { + _front->move (*_back); + _has_msg = true; + + _sync.unlock (); + } + } + + bool read (msg_t *value_) + { + if (!value_) + return false; + + { + scoped_lock_t lock (_sync); + if (!_has_msg) + return false; + + zmq_assert (_front->check ()); + + *value_ = *_front; + _front->init (); // avoid double free + + _has_msg = false; + return true; + } + } + + + bool check_read () + { + scoped_lock_t lock (_sync); + + return _has_msg; + } + + bool probe (bool (*fn_) (const msg_t &)) + { + scoped_lock_t lock (_sync); + return (*fn_) (*_front); + } + + + private: + msg_t _storage[2]; + msg_t *_back, *_front; + + mutex_t _sync; + bool _has_msg; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (dbuffer_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/dealer.cpp b/3rd/libzmq/src/dealer.cpp new file mode 100644 index 00000000..bf1c41da --- /dev/null +++ b/3rd/libzmq/src/dealer.cpp @@ -0,0 +1,145 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "dealer.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::dealer_t::dealer_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_), + _probe_router (false) +{ + options.type = ZMQ_DEALER; + options.can_send_hello_msg = true; +} + +zmq::dealer_t::~dealer_t () +{ +} + +void zmq::dealer_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + + if (_probe_router) { + msg_t probe_msg; + int rc = probe_msg.init (); + errno_assert (rc == 0); + + rc = pipe_->write (&probe_msg); + // zmq_assert (rc) is not applicable here, since it is not a bug. + LIBZMQ_UNUSED (rc); + + pipe_->flush (); + + rc = probe_msg.close (); + errno_assert (rc == 0); + } + + _fq.attach (pipe_); + _lb.attach (pipe_); +} + +int zmq::dealer_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + const bool is_int = (optvallen_ == sizeof (int)); + int value = 0; + if (is_int) + memcpy (&value, optval_, sizeof (int)); + + switch (option_) { + case ZMQ_PROBE_ROUTER: + if (is_int && value >= 0) { + _probe_router = (value != 0); + return 0; + } + break; + + default: + break; + } + + errno = EINVAL; + return -1; +} + +int zmq::dealer_t::xsend (msg_t *msg_) +{ + return sendpipe (msg_, NULL); +} + +int zmq::dealer_t::xrecv (msg_t *msg_) +{ + return recvpipe (msg_, NULL); +} + +bool zmq::dealer_t::xhas_in () +{ + return _fq.has_in (); +} + +bool zmq::dealer_t::xhas_out () +{ + return _lb.has_out (); +} + +void zmq::dealer_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +void zmq::dealer_t::xwrite_activated (pipe_t *pipe_) +{ + _lb.activated (pipe_); +} + +void zmq::dealer_t::xpipe_terminated (pipe_t *pipe_) +{ + _fq.pipe_terminated (pipe_); + _lb.pipe_terminated (pipe_); +} + +int zmq::dealer_t::sendpipe (msg_t *msg_, pipe_t **pipe_) +{ + return _lb.sendpipe (msg_, pipe_); +} + +int zmq::dealer_t::recvpipe (msg_t *msg_, pipe_t **pipe_) +{ + return _fq.recvpipe (msg_, pipe_); +} diff --git a/3rd/libzmq/src/dealer.hpp b/3rd/libzmq/src/dealer.hpp new file mode 100644 index 00000000..5515fd39 --- /dev/null +++ b/3rd/libzmq/src/dealer.hpp @@ -0,0 +1,85 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DEALER_HPP_INCLUDED__ +#define __ZMQ_DEALER_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "fq.hpp" +#include "lb.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; +class io_thread_t; +class socket_base_t; + +class dealer_t : public socket_base_t +{ + public: + dealer_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~dealer_t () ZMQ_OVERRIDE; + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) ZMQ_FINAL; + int xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) ZMQ_OVERRIDE; + int xsend (zmq::msg_t *msg_) ZMQ_OVERRIDE; + int xrecv (zmq::msg_t *msg_) ZMQ_OVERRIDE; + bool xhas_in () ZMQ_OVERRIDE; + bool xhas_out () ZMQ_OVERRIDE; + void xread_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void xwrite_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void xpipe_terminated (zmq::pipe_t *pipe_) ZMQ_OVERRIDE; + + // Send and recv - knowing which pipe was used. + int sendpipe (zmq::msg_t *msg_, zmq::pipe_t **pipe_); + int recvpipe (zmq::msg_t *msg_, zmq::pipe_t **pipe_); + + private: + // Messages are fair-queued from inbound pipes. And load-balanced to + // the outbound pipes. + fq_t _fq; + lb_t _lb; + + // if true, send an empty message to every connected router peer + bool _probe_router; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (dealer_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/decoder.hpp b/3rd/libzmq/src/decoder.hpp new file mode 100644 index 00000000..a2cd5a99 --- /dev/null +++ b/3rd/libzmq/src/decoder.hpp @@ -0,0 +1,191 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DECODER_HPP_INCLUDED__ +#define __ZMQ_DECODER_HPP_INCLUDED__ + +#include +#include +#include + +#include "decoder_allocators.hpp" +#include "err.hpp" +#include "i_decoder.hpp" +#include "stdint.hpp" + +namespace zmq +{ +// Helper base class for decoders that know the amount of data to read +// in advance at any moment. Knowing the amount in advance is a property +// of the protocol used. 0MQ framing protocol is based size-prefixed +// paradigm, which qualifies it to be parsed by this class. +// On the other hand, XML-based transports (like XMPP or SOAP) don't allow +// for knowing the size of data to read in advance and should use different +// decoding algorithms. +// +// This class implements the state machine that parses the incoming buffer. +// Derived class should implement individual state machine actions. +// +// Buffer management is done by an allocator policy. +template +class decoder_base_t : public i_decoder +{ + public: + explicit decoder_base_t (const size_t buf_size_) : + _next (NULL), + _read_pos (NULL), + _to_read (0), + _allocator (buf_size_) + { + _buf = _allocator.allocate (); + } + + ~decoder_base_t () ZMQ_OVERRIDE { _allocator.deallocate (); } + + // Returns a buffer to be filled with binary data. + void get_buffer (unsigned char **data_, std::size_t *size_) ZMQ_FINAL + { + _buf = _allocator.allocate (); + + // If we are expected to read large message, we'll opt for zero- + // copy, i.e. we'll ask caller to fill the data directly to the + // message. Note that subsequent read(s) are non-blocking, thus + // each single read reads at most SO_RCVBUF bytes at once not + // depending on how large is the chunk returned from here. + // As a consequence, large messages being received won't block + // other engines running in the same I/O thread for excessive + // amounts of time. + if (_to_read >= _allocator.size ()) { + *data_ = _read_pos; + *size_ = _to_read; + return; + } + + *data_ = _buf; + *size_ = _allocator.size (); + } + + // Processes the data in the buffer previously allocated using + // get_buffer function. size_ argument specifies number of bytes + // actually filled into the buffer. Function returns 1 when the + // whole message was decoded or 0 when more data is required. + // On error, -1 is returned and errno set accordingly. + // Number of bytes processed is returned in bytes_used_. + int decode (const unsigned char *data_, + std::size_t size_, + std::size_t &bytes_used_) ZMQ_FINAL + { + bytes_used_ = 0; + + // In case of zero-copy simply adjust the pointers, no copying + // is required. Also, run the state machine in case all the data + // were processed. + if (data_ == _read_pos) { + zmq_assert (size_ <= _to_read); + _read_pos += size_; + _to_read -= size_; + bytes_used_ = size_; + + while (!_to_read) { + const int rc = + (static_cast (this)->*_next) (data_ + bytes_used_); + if (rc != 0) + return rc; + } + return 0; + } + + while (bytes_used_ < size_) { + // Copy the data from buffer to the message. + const size_t to_copy = std::min (_to_read, size_ - bytes_used_); + // Only copy when destination address is different from the + // current address in the buffer. + if (_read_pos != data_ + bytes_used_) { + memcpy (_read_pos, data_ + bytes_used_, to_copy); + } + + _read_pos += to_copy; + _to_read -= to_copy; + bytes_used_ += to_copy; + // Try to get more space in the message to fill in. + // If none is available, return. + while (_to_read == 0) { + // pass current address in the buffer + const int rc = + (static_cast (this)->*_next) (data_ + bytes_used_); + if (rc != 0) + return rc; + } + } + + return 0; + } + + void resize_buffer (std::size_t new_size_) ZMQ_FINAL + { + _allocator.resize (new_size_); + } + + protected: + // Prototype of state machine action. Action should return false if + // it is unable to push the data to the system. + typedef int (T::*step_t) (unsigned char const *); + + // This function should be called from derived class to read data + // from the buffer and schedule next state machine action. + void next_step (void *read_pos_, std::size_t to_read_, step_t next_) + { + _read_pos = static_cast (read_pos_); + _to_read = to_read_; + _next = next_; + } + + A &get_allocator () { return _allocator; } + + private: + // Next step. If set to NULL, it means that associated data stream + // is dead. Note that there can be still data in the process in such + // case. + step_t _next; + + // Where to store the read data. + unsigned char *_read_pos; + + // How much data to read before taking next step. + std::size_t _to_read; + + // The duffer for data to decode. + A _allocator; + unsigned char *_buf; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (decoder_base_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/decoder_allocators.cpp b/3rd/libzmq/src/decoder_allocators.cpp new file mode 100644 index 00000000..078d294a --- /dev/null +++ b/3rd/libzmq/src/decoder_allocators.cpp @@ -0,0 +1,151 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "decoder_allocators.hpp" + +#include "msg.hpp" + +zmq::shared_message_memory_allocator::shared_message_memory_allocator ( + std::size_t bufsize_) : + _buf (NULL), + _buf_size (0), + _max_size (bufsize_), + _msg_content (NULL), + _max_counters ((_max_size + msg_t::max_vsm_size - 1) / msg_t::max_vsm_size) +{ +} + +zmq::shared_message_memory_allocator::shared_message_memory_allocator ( + std::size_t bufsize_, std::size_t max_messages_) : + _buf (NULL), + _buf_size (0), + _max_size (bufsize_), + _msg_content (NULL), + _max_counters (max_messages_) +{ +} + +zmq::shared_message_memory_allocator::~shared_message_memory_allocator () +{ + deallocate (); +} + +unsigned char *zmq::shared_message_memory_allocator::allocate () +{ + if (_buf) { + // release reference count to couple lifetime to messages + zmq::atomic_counter_t *c = + reinterpret_cast (_buf); + + // if refcnt drops to 0, there are no message using the buffer + // because either all messages have been closed or only vsm-messages + // were created + if (c->sub (1)) { + // buffer is still in use as message data. "Release" it and create a new one + // release pointer because we are going to create a new buffer + release (); + } + } + + // if buf != NULL it is not used by any message so we can re-use it for the next run + if (!_buf) { + // allocate memory for reference counters together with reception buffer + std::size_t const allocationsize = + _max_size + sizeof (zmq::atomic_counter_t) + + _max_counters * sizeof (zmq::msg_t::content_t); + + _buf = static_cast (std::malloc (allocationsize)); + alloc_assert (_buf); + + new (_buf) atomic_counter_t (1); + } else { + // release reference count to couple lifetime to messages + zmq::atomic_counter_t *c = + reinterpret_cast (_buf); + c->set (1); + } + + _buf_size = _max_size; + _msg_content = reinterpret_cast ( + _buf + sizeof (atomic_counter_t) + _max_size); + return _buf + sizeof (zmq::atomic_counter_t); +} + +void zmq::shared_message_memory_allocator::deallocate () +{ + zmq::atomic_counter_t *c = reinterpret_cast (_buf); + if (_buf && !c->sub (1)) { + std::free (_buf); + } + clear (); +} + +unsigned char *zmq::shared_message_memory_allocator::release () +{ + unsigned char *b = _buf; + clear (); + return b; +} + +void zmq::shared_message_memory_allocator::clear () +{ + _buf = NULL; + _buf_size = 0; + _msg_content = NULL; +} + +void zmq::shared_message_memory_allocator::inc_ref () +{ + (reinterpret_cast (_buf))->add (1); +} + +void zmq::shared_message_memory_allocator::call_dec_ref (void *, void *hint_) +{ + zmq_assert (hint_); + unsigned char *buf = static_cast (hint_); + zmq::atomic_counter_t *c = reinterpret_cast (buf); + + if (!c->sub (1)) { + c->~atomic_counter_t (); + std::free (buf); + buf = NULL; + } +} + + +std::size_t zmq::shared_message_memory_allocator::size () const +{ + return _buf_size; +} + +unsigned char *zmq::shared_message_memory_allocator::data () +{ + return _buf + sizeof (zmq::atomic_counter_t); +} diff --git a/3rd/libzmq/src/decoder_allocators.hpp b/3rd/libzmq/src/decoder_allocators.hpp new file mode 100644 index 00000000..2620f1b8 --- /dev/null +++ b/3rd/libzmq/src/decoder_allocators.hpp @@ -0,0 +1,133 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DECODER_ALLOCATORS_HPP_INCLUDED__ +#define __ZMQ_DECODER_ALLOCATORS_HPP_INCLUDED__ + +#include +#include + +#include "atomic_counter.hpp" +#include "msg.hpp" +#include "err.hpp" + +namespace zmq +{ +// Static buffer policy. +class c_single_allocator +{ + public: + explicit c_single_allocator (std::size_t bufsize_) : + _buf_size (bufsize_), + _buf (static_cast (std::malloc (_buf_size))) + { + alloc_assert (_buf); + } + + ~c_single_allocator () { std::free (_buf); } + + unsigned char *allocate () { return _buf; } + + void deallocate () {} + + std::size_t size () const { return _buf_size; } + + // This buffer is fixed, size must not be changed + void resize (std::size_t new_size_) { LIBZMQ_UNUSED (new_size_); } + + private: + std::size_t _buf_size; + unsigned char *_buf; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (c_single_allocator) +}; + +// This allocator allocates a reference counted buffer which is used by v2_decoder_t +// to use zero-copy msg::init_data to create messages with memory from this buffer as +// data storage. +// +// The buffer is allocated with a reference count of 1 to make sure that is is alive while +// decoding messages. Otherwise, it is possible that e.g. the first message increases the count +// from zero to one, gets passed to the user application, processed in the user thread and deleted +// which would then deallocate the buffer. The drawback is that the buffer may be allocated longer +// than necessary because it is only deleted when allocate is called the next time. +class shared_message_memory_allocator +{ + public: + explicit shared_message_memory_allocator (std::size_t bufsize_); + + // Create an allocator for a maximum number of messages + shared_message_memory_allocator (std::size_t bufsize_, + std::size_t max_messages_); + + ~shared_message_memory_allocator (); + + // Allocate a new buffer + // + // This releases the current buffer to be bound to the lifetime of the messages + // created on this buffer. + unsigned char *allocate (); + + // force deallocation of buffer. + void deallocate (); + + // Give up ownership of the buffer. The buffer's lifetime is now coupled to + // the messages constructed on top of it. + unsigned char *release (); + + void inc_ref (); + + static void call_dec_ref (void *, void *hint_); + + std::size_t size () const; + + // Return pointer to the first message data byte. + unsigned char *data (); + + // Return pointer to the first byte of the buffer. + unsigned char *buffer () { return _buf; } + + void resize (std::size_t new_size_) { _buf_size = new_size_; } + + zmq::msg_t::content_t *provide_content () { return _msg_content; } + + void advance_content () { _msg_content++; } + + private: + void clear (); + + unsigned char *_buf; + std::size_t _buf_size; + const std::size_t _max_size; + zmq::msg_t::content_t *_msg_content; + std::size_t _max_counters; +}; +} + +#endif diff --git a/3rd/libzmq/src/devpoll.cpp b/3rd/libzmq/src/devpoll.cpp new file mode 100644 index 00000000..11b38ccb --- /dev/null +++ b/3rd/libzmq/src/devpoll.cpp @@ -0,0 +1,208 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "devpoll.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_DEVPOLL + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devpoll.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zmq::devpoll_t::devpoll_t (const zmq::thread_ctx_t &ctx_) : + worker_poller_base_t (ctx_) +{ + devpoll_fd = open ("/dev/poll", O_RDWR); + errno_assert (devpoll_fd != -1); +} + +zmq::devpoll_t::~devpoll_t () +{ + // Wait till the worker thread exits. + stop_worker (); + + close (devpoll_fd); +} + +void zmq::devpoll_t::devpoll_ctl (fd_t fd_, short events_) +{ + struct pollfd pfd = {fd_, events_, 0}; + ssize_t rc = write (devpoll_fd, &pfd, sizeof pfd); + zmq_assert (rc == sizeof pfd); +} + +zmq::devpoll_t::handle_t zmq::devpoll_t::add_fd (fd_t fd_, + i_poll_events *reactor_) +{ + check_thread (); + // If the file descriptor table is too small expand it. + fd_table_t::size_type sz = fd_table.size (); + if (sz <= (fd_table_t::size_type) fd_) { + fd_table.resize (fd_ + 1); + while (sz != (fd_table_t::size_type) (fd_ + 1)) { + fd_table[sz].valid = false; + ++sz; + } + } + + zmq_assert (!fd_table[fd_].valid); + + fd_table[fd_].events = 0; + fd_table[fd_].reactor = reactor_; + fd_table[fd_].valid = true; + fd_table[fd_].accepted = false; + + devpoll_ctl (fd_, 0); + pending_list.push_back (fd_); + + // Increase the load metric of the thread. + adjust_load (1); + + return fd_; +} + +void zmq::devpoll_t::rm_fd (handle_t handle_) +{ + check_thread (); + zmq_assert (fd_table[handle_].valid); + + devpoll_ctl (handle_, POLLREMOVE); + fd_table[handle_].valid = false; + + // Decrease the load metric of the thread. + adjust_load (-1); +} + +void zmq::devpoll_t::set_pollin (handle_t handle_) +{ + check_thread (); + devpoll_ctl (handle_, POLLREMOVE); + fd_table[handle_].events |= POLLIN; + devpoll_ctl (handle_, fd_table[handle_].events); +} + +void zmq::devpoll_t::reset_pollin (handle_t handle_) +{ + check_thread (); + devpoll_ctl (handle_, POLLREMOVE); + fd_table[handle_].events &= ~((short) POLLIN); + devpoll_ctl (handle_, fd_table[handle_].events); +} + +void zmq::devpoll_t::set_pollout (handle_t handle_) +{ + check_thread (); + devpoll_ctl (handle_, POLLREMOVE); + fd_table[handle_].events |= POLLOUT; + devpoll_ctl (handle_, fd_table[handle_].events); +} + +void zmq::devpoll_t::reset_pollout (handle_t handle_) +{ + check_thread (); + devpoll_ctl (handle_, POLLREMOVE); + fd_table[handle_].events &= ~((short) POLLOUT); + devpoll_ctl (handle_, fd_table[handle_].events); +} + +void zmq::devpoll_t::stop () +{ + check_thread (); +} + +int zmq::devpoll_t::max_fds () +{ + return -1; +} + +void zmq::devpoll_t::loop () +{ + while (true) { + struct pollfd ev_buf[max_io_events]; + struct dvpoll poll_req; + + for (pending_list_t::size_type i = 0; i < pending_list.size (); i++) + fd_table[pending_list[i]].accepted = true; + pending_list.clear (); + + // Execute any due timers. + int timeout = (int) execute_timers (); + + if (get_load () == 0) { + if (timeout == 0) + break; + + // TODO sleep for timeout + continue; + } + + // Wait for events. + // On Solaris, we can retrieve no more then (OPEN_MAX - 1) events. + poll_req.dp_fds = &ev_buf[0]; +#if defined ZMQ_HAVE_SOLARIS + poll_req.dp_nfds = std::min ((int) max_io_events, OPEN_MAX - 1); +#else + poll_req.dp_nfds = max_io_events; +#endif + poll_req.dp_timeout = timeout ? timeout : -1; + int n = ioctl (devpoll_fd, DP_POLL, &poll_req); + if (n == -1 && errno == EINTR) + continue; + errno_assert (n != -1); + + for (int i = 0; i < n; i++) { + fd_entry_t *fd_ptr = &fd_table[ev_buf[i].fd]; + if (!fd_ptr->valid || !fd_ptr->accepted) + continue; + if (ev_buf[i].revents & (POLLERR | POLLHUP)) + fd_ptr->reactor->in_event (); + if (!fd_ptr->valid || !fd_ptr->accepted) + continue; + if (ev_buf[i].revents & POLLOUT) + fd_ptr->reactor->out_event (); + if (!fd_ptr->valid || !fd_ptr->accepted) + continue; + if (ev_buf[i].revents & POLLIN) + fd_ptr->reactor->in_event (); + } + } +} + +#endif diff --git a/3rd/libzmq/src/devpoll.hpp b/3rd/libzmq/src/devpoll.hpp new file mode 100644 index 00000000..e57814b9 --- /dev/null +++ b/3rd/libzmq/src/devpoll.hpp @@ -0,0 +1,101 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DEVPOLL_HPP_INCLUDED__ +#define __ZMQ_DEVPOLL_HPP_INCLUDED__ + +// poller.hpp decides which polling mechanism to use. +#include "poller.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_DEVPOLL + +#include + +#include "ctx.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "poller_base.hpp" + +namespace zmq +{ +struct i_poll_events; + +// Implements socket polling mechanism using the "/dev/poll" interface. + +class devpoll_t ZMQ_FINAL : public worker_poller_base_t +{ + public: + typedef fd_t handle_t; + + devpoll_t (const thread_ctx_t &ctx_); + ~devpoll_t () ZMQ_FINAL; + + // "poller" concept. + handle_t add_fd (fd_t fd_, zmq::i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void stop (); + + static int max_fds (); + + private: + // Main event loop. + void loop () ZMQ_FINAL; + + // File descriptor referring to "/dev/poll" pseudo-device. + fd_t devpoll_fd; + + struct fd_entry_t + { + short events; + zmq::i_poll_events *reactor; + bool valid; + bool accepted; + }; + + typedef std::vector fd_table_t; + fd_table_t fd_table; + + typedef std::vector pending_list_t; + pending_list_t pending_list; + + // Pollset manipulation function. + void devpoll_ctl (fd_t fd_, short events_); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (devpoll_t) +}; + +typedef devpoll_t poller_t; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/dgram.cpp b/3rd/libzmq/src/dgram.cpp new file mode 100644 index 00000000..879ff4da --- /dev/null +++ b/3rd/libzmq/src/dgram.cpp @@ -0,0 +1,169 @@ +/* + Copyright (c) 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "dgram.hpp" +#include "pipe.hpp" +#include "wire.hpp" +#include "random.hpp" +#include "likely.hpp" +#include "err.hpp" + +zmq::dgram_t::dgram_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_), + _pipe (NULL), + _last_in (NULL), + _more_out (false) +{ + options.type = ZMQ_DGRAM; + options.raw_socket = true; +} + +zmq::dgram_t::~dgram_t () +{ + zmq_assert (!_pipe); +} + +void zmq::dgram_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + + // ZMQ_DGRAM socket can only be connected to a single peer. + // The socket rejects any further connection requests. + if (_pipe == NULL) + _pipe = pipe_; + else + pipe_->terminate (false); +} + +void zmq::dgram_t::xpipe_terminated (pipe_t *pipe_) +{ + if (pipe_ == _pipe) { + if (_last_in == _pipe) { + _last_in = NULL; + } + _pipe = NULL; + } +} + +void zmq::dgram_t::xread_activated (pipe_t *) +{ + // There's just one pipe. No lists of active and inactive pipes. + // There's nothing to do here. +} + +void zmq::dgram_t::xwrite_activated (pipe_t *) +{ + // There's just one pipe. No lists of active and inactive pipes. + // There's nothing to do here. +} + +int zmq::dgram_t::xsend (msg_t *msg_) +{ + // If there's no out pipe, just drop it. + if (!_pipe) { + const int rc = msg_->close (); + errno_assert (rc == 0); + return -1; + } + + // If this is the first part of the message it's the ID of the + // peer to send the message to. + if (!_more_out) { + if (!(msg_->flags () & msg_t::more)) { + errno = EINVAL; + return -1; + } + } else { + // dgram messages are two part only, reject part if more is set + if (msg_->flags () & msg_t::more) { + errno = EINVAL; + return -1; + } + } + + // Push the message into the pipe. + if (!_pipe->write (msg_)) { + errno = EAGAIN; + return -1; + } + + if (!(msg_->flags () & msg_t::more)) + _pipe->flush (); + + // flip the more flag + _more_out = !_more_out; + + // Detach the message from the data buffer. + const int rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +int zmq::dgram_t::xrecv (msg_t *msg_) +{ + // Deallocate old content of the message. + int rc = msg_->close (); + errno_assert (rc == 0); + + if (!_pipe || !_pipe->read (msg_)) { + // Initialise the output parameter to be a 0-byte message. + rc = msg_->init (); + errno_assert (rc == 0); + + errno = EAGAIN; + return -1; + } + _last_in = _pipe; + + return 0; +} + +bool zmq::dgram_t::xhas_in () +{ + if (!_pipe) + return false; + + return _pipe->check_read (); +} + +bool zmq::dgram_t::xhas_out () +{ + if (!_pipe) + return false; + + return _pipe->check_write (); +} diff --git a/3rd/libzmq/src/dgram.hpp b/3rd/libzmq/src/dgram.hpp new file mode 100644 index 00000000..51385aad --- /dev/null +++ b/3rd/libzmq/src/dgram.hpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 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 . +*/ + +#ifndef __ZMQ_DGRAM_HPP_INCLUDED__ +#define __ZMQ_DGRAM_HPP_INCLUDED__ + +#include "blob.hpp" +#include "socket_base.hpp" +#include "session_base.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; +class io_thread_t; + +class dgram_t ZMQ_FINAL : public socket_base_t +{ + public: + dgram_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~dgram_t (); + + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + void xread_activated (zmq::pipe_t *pipe_); + void xwrite_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + zmq::pipe_t *_pipe; + + zmq::pipe_t *_last_in; + + // If true, more outgoing message parts are expected. + bool _more_out; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (dgram_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/dish.cpp b/3rd/libzmq/src/dish.cpp new file mode 100644 index 00000000..d8a4befc --- /dev/null +++ b/3rd/libzmq/src/dish.cpp @@ -0,0 +1,350 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include "macros.hpp" +#include "dish.hpp" +#include "err.hpp" + +zmq::dish_t::dish_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_, true), + _has_message (false) +{ + options.type = ZMQ_DISH; + + // When socket is being closed down we don't want to wait till pending + // subscription commands are sent to the wire. + options.linger.store (0); + + const int rc = _message.init (); + errno_assert (rc == 0); +} + +zmq::dish_t::~dish_t () +{ + const int rc = _message.close (); + errno_assert (rc == 0); +} + +void zmq::dish_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + _fq.attach (pipe_); + _dist.attach (pipe_); + + // Send all the cached subscriptions to the new upstream peer. + send_subscriptions (pipe_); +} + +void zmq::dish_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +void zmq::dish_t::xwrite_activated (pipe_t *pipe_) +{ + _dist.activated (pipe_); +} + +void zmq::dish_t::xpipe_terminated (pipe_t *pipe_) +{ + _fq.pipe_terminated (pipe_); + _dist.pipe_terminated (pipe_); +} + +void zmq::dish_t::xhiccuped (pipe_t *pipe_) +{ + // Send all the cached subscriptions to the hiccuped pipe. + send_subscriptions (pipe_); +} + +int zmq::dish_t::xjoin (const char *group_) +{ + const std::string group = std::string (group_); + + if (group.length () > ZMQ_GROUP_MAX_LENGTH) { + errno = EINVAL; + return -1; + } + + // User cannot join same group twice + if (!_subscriptions.insert (group).second) { + errno = EINVAL; + return -1; + } + + msg_t msg; + int rc = msg.init_join (); + errno_assert (rc == 0); + + rc = msg.set_group (group_); + errno_assert (rc == 0); + + int err = 0; + rc = _dist.send_to_all (&msg); + if (rc != 0) + err = errno; + const int rc2 = msg.close (); + errno_assert (rc2 == 0); + if (rc != 0) + errno = err; + return rc; +} + +int zmq::dish_t::xleave (const char *group_) +{ + const std::string group = std::string (group_); + + if (group.length () > ZMQ_GROUP_MAX_LENGTH) { + errno = EINVAL; + return -1; + } + + if (0 == _subscriptions.erase (group)) { + errno = EINVAL; + return -1; + } + + msg_t msg; + int rc = msg.init_leave (); + errno_assert (rc == 0); + + rc = msg.set_group (group_); + errno_assert (rc == 0); + + int err = 0; + rc = _dist.send_to_all (&msg); + if (rc != 0) + err = errno; + const int rc2 = msg.close (); + errno_assert (rc2 == 0); + if (rc != 0) + errno = err; + return rc; +} + +int zmq::dish_t::xsend (msg_t *msg_) +{ + LIBZMQ_UNUSED (msg_); + errno = ENOTSUP; + return -1; +} + +bool zmq::dish_t::xhas_out () +{ + // Subscription can be added/removed anytime. + return true; +} + +int zmq::dish_t::xrecv (msg_t *msg_) +{ + // If there's already a message prepared by a previous call to zmq_poll, + // return it straight ahead. + if (_has_message) { + const int rc = msg_->move (_message); + errno_assert (rc == 0); + _has_message = false; + return 0; + } + + return xxrecv (msg_); +} + +int zmq::dish_t::xxrecv (msg_t *msg_) +{ + do { + // Get a message using fair queueing algorithm. + const int rc = _fq.recv (msg_); + + // If there's no message available, return immediately. + // The same when error occurs. + if (rc != 0) + return -1; + + // Skip non matching messages + } while (0 == _subscriptions.count (std::string (msg_->group ()))); + + // Found a matching message + return 0; +} + +bool zmq::dish_t::xhas_in () +{ + // If there's already a message prepared by a previous call to zmq_poll, + // return straight ahead. + if (_has_message) + return true; + + const int rc = xxrecv (&_message); + if (rc != 0) { + errno_assert (errno == EAGAIN); + return false; + } + + // Matching message found + _has_message = true; + return true; +} + +void zmq::dish_t::send_subscriptions (pipe_t *pipe_) +{ + for (subscriptions_t::iterator it = _subscriptions.begin (), + end = _subscriptions.end (); + it != end; ++it) { + msg_t msg; + int rc = msg.init_join (); + errno_assert (rc == 0); + + rc = msg.set_group (it->c_str ()); + errno_assert (rc == 0); + + // Send it to the pipe. + pipe_->write (&msg); + } + + pipe_->flush (); +} + +zmq::dish_session_t::dish_session_t (io_thread_t *io_thread_, + bool connect_, + socket_base_t *socket_, + const options_t &options_, + address_t *addr_) : + session_base_t (io_thread_, connect_, socket_, options_, addr_), + _state (group) +{ +} + +zmq::dish_session_t::~dish_session_t () +{ +} + +int zmq::dish_session_t::push_msg (msg_t *msg_) +{ + if (_state == group) { + if ((msg_->flags () & msg_t::more) != msg_t::more) { + errno = EFAULT; + return -1; + } + + if (msg_->size () > ZMQ_GROUP_MAX_LENGTH) { + errno = EFAULT; + return -1; + } + + _group_msg = *msg_; + _state = body; + + const int rc = msg_->init (); + errno_assert (rc == 0); + return 0; + } + const char *group_setting = msg_->group (); + int rc; + if (group_setting[0] != 0) + goto has_group; + + // Set the message group + rc = msg_->set_group (static_cast (_group_msg.data ()), + _group_msg.size ()); + errno_assert (rc == 0); + + // We set the group, so we don't need the group_msg anymore + rc = _group_msg.close (); + errno_assert (rc == 0); +has_group: + // Thread safe socket doesn't support multipart messages + if ((msg_->flags () & msg_t::more) == msg_t::more) { + errno = EFAULT; + return -1; + } + + // Push message to dish socket + rc = session_base_t::push_msg (msg_); + + if (rc == 0) + _state = group; + + return rc; +} + +int zmq::dish_session_t::pull_msg (msg_t *msg_) +{ + int rc = session_base_t::pull_msg (msg_); + + if (rc != 0) + return rc; + + if (!msg_->is_join () && !msg_->is_leave ()) + return rc; + + const int group_length = static_cast (strlen (msg_->group ())); + + msg_t command; + int offset; + + if (msg_->is_join ()) { + rc = command.init_size (group_length + 5); + errno_assert (rc == 0); + offset = 5; + memcpy (command.data (), "\4JOIN", 5); + } else { + rc = command.init_size (group_length + 6); + errno_assert (rc == 0); + offset = 6; + memcpy (command.data (), "\5LEAVE", 6); + } + + command.set_flags (msg_t::command); + char *command_data = static_cast (command.data ()); + + // Copy the group + memcpy (command_data + offset, msg_->group (), group_length); + + // Close the join message + rc = msg_->close (); + errno_assert (rc == 0); + + *msg_ = command; + + return 0; +} + +void zmq::dish_session_t::reset () +{ + session_base_t::reset (); + _state = group; +} diff --git a/3rd/libzmq/src/dish.hpp b/3rd/libzmq/src/dish.hpp new file mode 100644 index 00000000..c41ca92e --- /dev/null +++ b/3rd/libzmq/src/dish.hpp @@ -0,0 +1,121 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DISH_HPP_INCLUDED__ +#define __ZMQ_DISH_HPP_INCLUDED__ + +#include + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "dist.hpp" +#include "fq.hpp" +#include "msg.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; +class io_thread_t; + +class dish_t ZMQ_FINAL : public socket_base_t +{ + public: + dish_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~dish_t (); + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + bool xhas_out (); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + void xread_activated (zmq::pipe_t *pipe_); + void xwrite_activated (zmq::pipe_t *pipe_); + void xhiccuped (pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + int xjoin (const char *group_); + int xleave (const char *group_); + + private: + int xxrecv (zmq::msg_t *msg_); + + // Send subscriptions to a pipe + void send_subscriptions (pipe_t *pipe_); + + // Fair queueing object for inbound pipes. + fq_t _fq; + + // Object for distributing the subscriptions upstream. + dist_t _dist; + + // The repository of subscriptions. + typedef std::set subscriptions_t; + subscriptions_t _subscriptions; + + // If true, 'message' contains a matching message to return on the + // next recv call. + bool _has_message; + msg_t _message; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (dish_t) +}; + +class dish_session_t ZMQ_FINAL : public session_base_t +{ + public: + dish_session_t (zmq::io_thread_t *io_thread_, + bool connect_, + zmq::socket_base_t *socket_, + const options_t &options_, + address_t *addr_); + ~dish_session_t (); + + // Overrides of the functions from session_base_t. + int push_msg (msg_t *msg_); + int pull_msg (msg_t *msg_); + void reset (); + + private: + enum + { + group, + body + } _state; + + msg_t _group_msg; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (dish_session_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/dist.cpp b/3rd/libzmq/src/dist.cpp new file mode 100644 index 00000000..7795bb36 --- /dev/null +++ b/3rd/libzmq/src/dist.cpp @@ -0,0 +1,237 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "dist.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" +#include "likely.hpp" + +zmq::dist_t::dist_t () : + _matching (0), + _active (0), + _eligible (0), + _more (false) +{ +} + +zmq::dist_t::~dist_t () +{ + zmq_assert (_pipes.empty ()); +} + +void zmq::dist_t::attach (pipe_t *pipe_) +{ + // If we are in the middle of sending a message, we'll add new pipe + // into the list of eligible pipes. Otherwise we add it to the list + // of active pipes. + if (_more) { + _pipes.push_back (pipe_); + _pipes.swap (_eligible, _pipes.size () - 1); + _eligible++; + } else { + _pipes.push_back (pipe_); + _pipes.swap (_active, _pipes.size () - 1); + _active++; + _eligible++; + } +} + +void zmq::dist_t::match (pipe_t *pipe_) +{ + // If pipe is already matching do nothing. + if (_pipes.index (pipe_) < _matching) + return; + + // If the pipe isn't eligible, ignore it. + if (_pipes.index (pipe_) >= _eligible) + return; + + // Mark the pipe as matching. + _pipes.swap (_pipes.index (pipe_), _matching); + _matching++; +} + +void zmq::dist_t::reverse_match () +{ + const pipes_t::size_type prev_matching = _matching; + + // Reset matching to 0 + unmatch (); + + // Mark all matching pipes as not matching and vice-versa. + // To do this, push all pipes that are eligible but not + // matched - i.e. between "matching" and "eligible" - + // to the beginning of the queue. + for (pipes_t::size_type i = prev_matching; i < _eligible; ++i) { + _pipes.swap (i, _matching++); + } +} + +void zmq::dist_t::unmatch () +{ + _matching = 0; +} + +void zmq::dist_t::pipe_terminated (pipe_t *pipe_) +{ + // Remove the pipe from the list; adjust number of matching, active and/or + // eligible pipes accordingly. + if (_pipes.index (pipe_) < _matching) { + _pipes.swap (_pipes.index (pipe_), _matching - 1); + _matching--; + } + if (_pipes.index (pipe_) < _active) { + _pipes.swap (_pipes.index (pipe_), _active - 1); + _active--; + } + if (_pipes.index (pipe_) < _eligible) { + _pipes.swap (_pipes.index (pipe_), _eligible - 1); + _eligible--; + } + + _pipes.erase (pipe_); +} + +void zmq::dist_t::activated (pipe_t *pipe_) +{ + // Move the pipe from passive to eligible state. + if (_eligible < _pipes.size ()) { + _pipes.swap (_pipes.index (pipe_), _eligible); + _eligible++; + } + + // If there's no message being sent at the moment, move it to + // the active state. + if (!_more && _active < _pipes.size ()) { + _pipes.swap (_eligible - 1, _active); + _active++; + } +} + +int zmq::dist_t::send_to_all (msg_t *msg_) +{ + _matching = _active; + return send_to_matching (msg_); +} + +int zmq::dist_t::send_to_matching (msg_t *msg_) +{ + // Is this end of a multipart message? + const bool msg_more = (msg_->flags () & msg_t::more) != 0; + + // Push the message to matching pipes. + distribute (msg_); + + // If multipart message is fully sent, activate all the eligible pipes. + if (!msg_more) + _active = _eligible; + + _more = msg_more; + + return 0; +} + +void zmq::dist_t::distribute (msg_t *msg_) +{ + // If there are no matching pipes available, simply drop the message. + if (_matching == 0) { + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + return; + } + + if (msg_->is_vsm ()) { + for (pipes_t::size_type i = 0; i < _matching;) { + if (!write (_pipes[i], msg_)) { + // Use same index again because entry will have been removed. + } else { + ++i; + } + } + int rc = msg_->init (); + errno_assert (rc == 0); + return; + } + + // Add matching-1 references to the message. We already hold one reference, + // that's why -1. + msg_->add_refs (static_cast (_matching) - 1); + + // Push copy of the message to each matching pipe. + int failed = 0; + for (pipes_t::size_type i = 0; i < _matching;) { + if (!write (_pipes[i], msg_)) { + ++failed; + // Use same index again because entry will have been removed. + } else { + ++i; + } + } + if (unlikely (failed)) + msg_->rm_refs (failed); + + // Detach the original message from the data buffer. Note that we don't + // close the message. That's because we've already used all the references. + const int rc = msg_->init (); + errno_assert (rc == 0); +} + +bool zmq::dist_t::has_out () +{ + return true; +} + +bool zmq::dist_t::write (pipe_t *pipe_, msg_t *msg_) +{ + if (!pipe_->write (msg_)) { + _pipes.swap (_pipes.index (pipe_), _matching - 1); + _matching--; + _pipes.swap (_pipes.index (pipe_), _active - 1); + _active--; + _pipes.swap (_active, _eligible - 1); + _eligible--; + return false; + } + if (!(msg_->flags () & msg_t::more)) + pipe_->flush (); + return true; +} + +bool zmq::dist_t::check_hwm () +{ + for (pipes_t::size_type i = 0; i < _matching; ++i) + if (!_pipes[i]->check_hwm ()) + return false; + + return true; +} diff --git a/3rd/libzmq/src/dist.hpp b/3rd/libzmq/src/dist.hpp new file mode 100644 index 00000000..f772a716 --- /dev/null +++ b/3rd/libzmq/src/dist.hpp @@ -0,0 +1,115 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DIST_HPP_INCLUDED__ +#define __ZMQ_DIST_HPP_INCLUDED__ + +#include + +#include "array.hpp" +#include "macros.hpp" + +namespace zmq +{ +class pipe_t; +class msg_t; + +// Class manages a set of outbound pipes. It sends each messages to +// each of them. +class dist_t +{ + public: + dist_t (); + ~dist_t (); + + // Adds the pipe to the distributor object. + void attach (zmq::pipe_t *pipe_); + + // Activates pipe that have previously reached high watermark. + void activated (zmq::pipe_t *pipe_); + + // Mark the pipe as matching. Subsequent call to send_to_matching + // will send message also to this pipe. + void match (zmq::pipe_t *pipe_); + + // Marks all pipes that are not matched as matched and vice-versa. + void reverse_match (); + + // Mark all pipes as non-matching. + void unmatch (); + + // Removes the pipe from the distributor object. + void pipe_terminated (zmq::pipe_t *pipe_); + + // Send the message to the matching outbound pipes. + int send_to_matching (zmq::msg_t *msg_); + + // Send the message to all the outbound pipes. + int send_to_all (zmq::msg_t *msg_); + + static bool has_out (); + + // check HWM of all pipes matching + bool check_hwm (); + + private: + // Write the message to the pipe. Make the pipe inactive if writing + // fails. In such a case false is returned. + bool write (zmq::pipe_t *pipe_, zmq::msg_t *msg_); + + // Put the message to all active pipes. + void distribute (zmq::msg_t *msg_); + + // List of outbound pipes. + typedef array_t pipes_t; + pipes_t _pipes; + + // Number of all the pipes to send the next message to. + pipes_t::size_type _matching; + + // Number of active pipes. All the active pipes are located at the + // beginning of the pipes array. These are the pipes the messages + // can be sent to at the moment. + pipes_t::size_type _active; + + // Number of pipes eligible for sending messages to. This includes all + // the active pipes plus all the pipes that we can in theory send + // messages to (the HWM is not yet reached), but sending a message + // to them would result in partial message being delivered, ie. message + // with initial parts missing. + pipes_t::size_type _eligible; + + // True if last we are in the middle of a multipart message. + bool _more; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (dist_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/encoder.hpp b/3rd/libzmq/src/encoder.hpp new file mode 100644 index 00000000..84ffe329 --- /dev/null +++ b/3rd/libzmq/src/encoder.hpp @@ -0,0 +1,178 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ENCODER_HPP_INCLUDED__ +#define __ZMQ_ENCODER_HPP_INCLUDED__ + +#if defined(_MSC_VER) +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +#include +#include +#include +#include + +#include "err.hpp" +#include "i_encoder.hpp" +#include "msg.hpp" + +namespace zmq +{ +// Helper base class for encoders. It implements the state machine that +// fills the outgoing buffer. Derived classes should implement individual +// state machine actions. + +template class encoder_base_t : public i_encoder +{ + public: + explicit encoder_base_t (size_t bufsize_) : + _write_pos (0), + _to_write (0), + _next (NULL), + _new_msg_flag (false), + _buf_size (bufsize_), + _buf (static_cast (malloc (bufsize_))), + _in_progress (NULL) + { + alloc_assert (_buf); + } + + ~encoder_base_t () ZMQ_OVERRIDE { free (_buf); } + + // The function returns a batch of binary data. The data + // are filled to a supplied buffer. If no buffer is supplied (data_ + // points to NULL) decoder object will provide buffer of its own. + size_t encode (unsigned char **data_, size_t size_) ZMQ_FINAL + { + unsigned char *buffer = !*data_ ? _buf : *data_; + const size_t buffersize = !*data_ ? _buf_size : size_; + + if (in_progress () == NULL) + return 0; + + size_t pos = 0; + while (pos < buffersize) { + // If there are no more data to return, run the state machine. + // If there are still no data, return what we already have + // in the buffer. + if (!_to_write) { + if (_new_msg_flag) { + int rc = _in_progress->close (); + errno_assert (rc == 0); + rc = _in_progress->init (); + errno_assert (rc == 0); + _in_progress = NULL; + break; + } + (static_cast (this)->*_next) (); + } + + // If there are no data in the buffer yet and we are able to + // fill whole buffer in a single go, let's use zero-copy. + // There's no disadvantage to it as we cannot stuck multiple + // messages into the buffer anyway. Note that subsequent + // write(s) are non-blocking, thus each single write writes + // at most SO_SNDBUF bytes at once not depending on how large + // is the chunk returned from here. + // As a consequence, large messages being sent won't block + // other engines running in the same I/O thread for excessive + // amounts of time. + if (!pos && !*data_ && _to_write >= buffersize) { + *data_ = _write_pos; + pos = _to_write; + _write_pos = NULL; + _to_write = 0; + return pos; + } + + // Copy data to the buffer. If the buffer is full, return. + const size_t to_copy = std::min (_to_write, buffersize - pos); + memcpy (buffer + pos, _write_pos, to_copy); + pos += to_copy; + _write_pos += to_copy; + _to_write -= to_copy; + } + + *data_ = buffer; + return pos; + } + + void load_msg (msg_t *msg_) ZMQ_FINAL + { + zmq_assert (in_progress () == NULL); + _in_progress = msg_; + (static_cast (this)->*_next) (); + } + + protected: + // Prototype of state machine action. + typedef void (T::*step_t) (); + + // This function should be called from derived class to write the data + // to the buffer and schedule next state machine action. + void next_step (void *write_pos_, + size_t to_write_, + step_t next_, + bool new_msg_flag_) + { + _write_pos = static_cast (write_pos_); + _to_write = to_write_; + _next = next_; + _new_msg_flag = new_msg_flag_; + } + + msg_t *in_progress () { return _in_progress; } + + private: + // Where to get the data to write from. + unsigned char *_write_pos; + + // How much data to write before next step should be executed. + size_t _to_write; + + // Next step. If set to NULL, it means that associated data stream + // is dead. + step_t _next; + + bool _new_msg_flag; + + // The buffer for encoded data. + const size_t _buf_size; + unsigned char *const _buf; + + msg_t *_in_progress; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (encoder_base_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/endpoint.cpp b/3rd/libzmq/src/endpoint.cpp new file mode 100644 index 00000000..c9ad6d66 --- /dev/null +++ b/3rd/libzmq/src/endpoint.cpp @@ -0,0 +1,44 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "endpoint.hpp" + +zmq::endpoint_uri_pair_t +zmq::make_unconnected_connect_endpoint_pair (const std::string &endpoint_) +{ + return endpoint_uri_pair_t (std::string (), endpoint_, + endpoint_type_connect); +} + +zmq::endpoint_uri_pair_t +zmq::make_unconnected_bind_endpoint_pair (const std::string &endpoint_) +{ + return endpoint_uri_pair_t (endpoint_, std::string (), endpoint_type_bind); +} diff --git a/3rd/libzmq/src/endpoint.hpp b/3rd/libzmq/src/endpoint.hpp new file mode 100644 index 00000000..56b7d0e7 --- /dev/null +++ b/3rd/libzmq/src/endpoint.hpp @@ -0,0 +1,72 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ENDPOINT_HPP_INCLUDED__ +#define __ZMQ_ENDPOINT_HPP_INCLUDED__ + +#include + +namespace zmq +{ +enum endpoint_type_t +{ + endpoint_type_none, // a connection-less endpoint + endpoint_type_bind, // a connection-oriented bind endpoint + endpoint_type_connect // a connection-oriented connect endpoint +}; + +struct endpoint_uri_pair_t +{ + endpoint_uri_pair_t () : local_type (endpoint_type_none) {} + endpoint_uri_pair_t (const std::string &local, + const std::string &remote, + endpoint_type_t local_type) : + local (local), + remote (remote), + local_type (local_type) + { + } + + const std::string &identifier () const + { + return local_type == endpoint_type_bind ? local : remote; + } + + std::string local, remote; + endpoint_type_t local_type; +}; + +endpoint_uri_pair_t +make_unconnected_connect_endpoint_pair (const std::string &endpoint_); + +endpoint_uri_pair_t +make_unconnected_bind_endpoint_pair (const std::string &endpoint_); +} + +#endif diff --git a/3rd/libzmq/src/epoll.cpp b/3rd/libzmq/src/epoll.cpp new file mode 100644 index 00000000..fc641b74 --- /dev/null +++ b/3rd/libzmq/src/epoll.cpp @@ -0,0 +1,218 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_EPOLL +#include "epoll.hpp" + +#if !defined ZMQ_HAVE_WINDOWS +#include +#endif + +#include +#include +#include +#include +#include + +#include "macros.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +#ifdef ZMQ_HAVE_WINDOWS +const zmq::epoll_t::epoll_fd_t zmq::epoll_t::epoll_retired_fd = + INVALID_HANDLE_VALUE; +#endif + +zmq::epoll_t::epoll_t (const zmq::thread_ctx_t &ctx_) : + worker_poller_base_t (ctx_) +{ +#ifdef ZMQ_IOTHREAD_POLLER_USE_EPOLL_CLOEXEC + // Setting this option result in sane behaviour when exec() functions + // are used. Old sockets are closed and don't block TCP ports, avoid + // leaks, etc. + _epoll_fd = epoll_create1 (EPOLL_CLOEXEC); +#else + _epoll_fd = epoll_create (1); +#endif + errno_assert (_epoll_fd != epoll_retired_fd); +} + +zmq::epoll_t::~epoll_t () +{ + // Wait till the worker thread exits. + stop_worker (); + +#ifdef ZMQ_HAVE_WINDOWS + epoll_close (_epoll_fd); +#else + close (_epoll_fd); +#endif + for (retired_t::iterator it = _retired.begin (), end = _retired.end (); + it != end; ++it) { + LIBZMQ_DELETE (*it); + } +} + +zmq::epoll_t::handle_t zmq::epoll_t::add_fd (fd_t fd_, i_poll_events *events_) +{ + check_thread (); + poll_entry_t *pe = new (std::nothrow) poll_entry_t; + alloc_assert (pe); + + // The memset is not actually needed. It's here to prevent debugging + // tools to complain about using uninitialised memory. + memset (pe, 0, sizeof (poll_entry_t)); + + pe->fd = fd_; + pe->ev.events = 0; + pe->ev.data.ptr = pe; + pe->events = events_; + + const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_ADD, fd_, &pe->ev); + errno_assert (rc != -1); + + // Increase the load metric of the thread. + adjust_load (1); + + return pe; +} + +void zmq::epoll_t::rm_fd (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = static_cast (handle_); + const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_DEL, pe->fd, &pe->ev); + errno_assert (rc != -1); + pe->fd = retired_fd; + _retired.push_back (pe); + + // Decrease the load metric of the thread. + adjust_load (-1); +} + +void zmq::epoll_t::set_pollin (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = static_cast (handle_); + pe->ev.events |= EPOLLIN; + const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::reset_pollin (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = static_cast (handle_); + pe->ev.events &= ~(static_cast (EPOLLIN)); + const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::set_pollout (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = static_cast (handle_); + pe->ev.events |= EPOLLOUT; + const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::reset_pollout (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = static_cast (handle_); + pe->ev.events &= ~(static_cast (EPOLLOUT)); + const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zmq::epoll_t::stop () +{ + check_thread (); +} + +int zmq::epoll_t::max_fds () +{ + return -1; +} + +void zmq::epoll_t::loop () +{ + epoll_event ev_buf[max_io_events]; + + while (true) { + // Execute any due timers. + const int timeout = static_cast (execute_timers ()); + + if (get_load () == 0) { + if (timeout == 0) + break; + + // TODO sleep for timeout + continue; + } + + // Wait for events. + const int n = epoll_wait (_epoll_fd, &ev_buf[0], max_io_events, + timeout ? timeout : -1); + if (n == -1) { + errno_assert (errno == EINTR); + continue; + } + + for (int i = 0; i < n; i++) { + const poll_entry_t *const pe = + static_cast (ev_buf[i].data.ptr); + + if (pe->fd == retired_fd) + continue; + if (ev_buf[i].events & (EPOLLERR | EPOLLHUP)) + pe->events->in_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf[i].events & EPOLLOUT) + pe->events->out_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf[i].events & EPOLLIN) + pe->events->in_event (); + } + + // Destroy retired event sources. + for (retired_t::iterator it = _retired.begin (), end = _retired.end (); + it != end; ++it) { + LIBZMQ_DELETE (*it); + } + _retired.clear (); + } +} + +#endif diff --git a/3rd/libzmq/src/epoll.hpp b/3rd/libzmq/src/epoll.hpp new file mode 100644 index 00000000..0e7bad02 --- /dev/null +++ b/3rd/libzmq/src/epoll.hpp @@ -0,0 +1,114 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_EPOLL_HPP_INCLUDED__ +#define __ZMQ_EPOLL_HPP_INCLUDED__ + +// poller.hpp decides which polling mechanism to use. +#include "poller.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_EPOLL + +#include + +#if defined ZMQ_HAVE_WINDOWS +#include "../external/wepoll/wepoll.h" +#else +#include +#endif + +#include "ctx.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "poller_base.hpp" +#include "mutex.hpp" + +namespace zmq +{ +struct i_poll_events; + +// This class implements socket polling mechanism using the Linux-specific +// epoll mechanism. + +class epoll_t ZMQ_FINAL : public worker_poller_base_t +{ + public: + typedef void *handle_t; + + epoll_t (const thread_ctx_t &ctx_); + ~epoll_t () ZMQ_OVERRIDE; + + // "poller" concept. + handle_t add_fd (fd_t fd_, zmq::i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void stop (); + + static int max_fds (); + + private: +#if defined ZMQ_HAVE_WINDOWS + typedef HANDLE epoll_fd_t; + static const epoll_fd_t epoll_retired_fd; +#else + typedef fd_t epoll_fd_t; + enum + { + epoll_retired_fd = retired_fd + }; +#endif + + // Main event loop. + void loop (); + + // Main epoll file descriptor + epoll_fd_t _epoll_fd; + + struct poll_entry_t + { + fd_t fd; + epoll_event ev; + zmq::i_poll_events *events; + }; + + // List of retired event sources. + typedef std::vector retired_t; + retired_t _retired; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (epoll_t) +}; + +typedef epoll_t poller_t; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/err.cpp b/3rd/libzmq/src/err.cpp new file mode 100644 index 00000000..ea2a9b51 --- /dev/null +++ b/3rd/libzmq/src/err.cpp @@ -0,0 +1,453 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "err.hpp" +#include "macros.hpp" + +const char *zmq::errno_to_string (int errno_) +{ + switch (errno_) { +#if defined ZMQ_HAVE_WINDOWS + case ENOTSUP: + return "Not supported"; + case EPROTONOSUPPORT: + return "Protocol not supported"; + case ENOBUFS: + return "No buffer space available"; + case ENETDOWN: + return "Network is down"; + case EADDRINUSE: + return "Address in use"; + case EADDRNOTAVAIL: + return "Address not available"; + case ECONNREFUSED: + return "Connection refused"; + case EINPROGRESS: + return "Operation in progress"; +#endif + case EFSM: + return "Operation cannot be accomplished in current state"; + case ENOCOMPATPROTO: + return "The protocol is not compatible with the socket type"; + case ETERM: + return "Context was terminated"; + case EMTHREAD: + return "No thread available"; + case EHOSTUNREACH: + return "Host unreachable"; + default: +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + return strerror (errno_); +#if defined _MSC_VER +#pragma warning(pop) +#endif + } +} + +void zmq::zmq_abort (const char *errmsg_) +{ +#if defined ZMQ_HAVE_WINDOWS + + // Raise STATUS_FATAL_APP_EXIT. + ULONG_PTR extra_info[1]; + extra_info[0] = (ULONG_PTR) errmsg_; + RaiseException (0x40000015, EXCEPTION_NONCONTINUABLE, 1, extra_info); +#else + LIBZMQ_UNUSED (errmsg_); + print_backtrace (); + abort (); +#endif +} + +#ifdef ZMQ_HAVE_WINDOWS + +const char *zmq::wsa_error () +{ + return wsa_error_no (WSAGetLastError (), NULL); +} + +const char *zmq::wsa_error_no (int no_, const char *wsae_wouldblock_string_) +{ + // TODO: It seems that list of Windows socket errors is longer than this. + // Investigate whether there's a way to convert it into the string + // automatically (wsaError->HRESULT->string?). + switch (no_) { + case WSABASEERR: + return "No Error"; + case WSAEINTR: + return "Interrupted system call"; + case WSAEBADF: + return "Bad file number"; + case WSAEACCES: + return "Permission denied"; + case WSAEFAULT: + return "Bad address"; + case WSAEINVAL: + return "Invalid argument"; + case WSAEMFILE: + return "Too many open files"; + case WSAEWOULDBLOCK: + return wsae_wouldblock_string_; + case WSAEINPROGRESS: + return "Operation now in progress"; + case WSAEALREADY: + return "Operation already in progress"; + case WSAENOTSOCK: + return "Socket operation on non-socket"; + case WSAEDESTADDRREQ: + return "Destination address required"; + case WSAEMSGSIZE: + return "Message too long"; + case WSAEPROTOTYPE: + return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: + return "Bas protocol option"; + case WSAEPROTONOSUPPORT: + return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: + return "Socket type not supported"; + case WSAEOPNOTSUPP: + return "Operation not supported on socket"; + case WSAEPFNOSUPPORT: + return "Protocol family not supported"; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family"; + case WSAEADDRINUSE: + return "Address already in use"; + case WSAEADDRNOTAVAIL: + return "Can't assign requested address"; + case WSAENETDOWN: + return "Network is down"; + case WSAENETUNREACH: + return "Network is unreachable"; + case WSAENETRESET: + return "Net dropped connection or reset"; + case WSAECONNABORTED: + return "Software caused connection abort"; + case WSAECONNRESET: + return "Connection reset by peer"; + case WSAENOBUFS: + return "No buffer space available"; + case WSAEISCONN: + return "Socket is already connected"; + case WSAENOTCONN: + return "Socket is not connected"; + case WSAESHUTDOWN: + return "Can't send after socket shutdown"; + case WSAETOOMANYREFS: + return "Too many references can't splice"; + case WSAETIMEDOUT: + return "Connection timed out"; + case WSAECONNREFUSED: + return "Connection refused"; + case WSAELOOP: + return "Too many levels of symbolic links"; + case WSAENAMETOOLONG: + return "File name too long"; + case WSAEHOSTDOWN: + return "Host is down"; + case WSAEHOSTUNREACH: + return "No Route to Host"; + case WSAENOTEMPTY: + return "Directory not empty"; + case WSAEPROCLIM: + return "Too many processes"; + case WSAEUSERS: + return "Too many users"; + case WSAEDQUOT: + return "Disc Quota Exceeded"; + case WSAESTALE: + return "Stale NFS file handle"; + case WSAEREMOTE: + return "Too many levels of remote in path"; + case WSASYSNOTREADY: + return "Network SubSystem is unavailable"; + case WSAVERNOTSUPPORTED: + return "WINSOCK DLL Version out of range"; + case WSANOTINITIALISED: + return "Successful WSASTARTUP not yet performed"; + case WSAHOST_NOT_FOUND: + return "Host not found"; + case WSATRY_AGAIN: + return "Non-Authoritative Host not found"; + case WSANO_RECOVERY: + return "Non-Recoverable errors: FORMERR REFUSED NOTIMP"; + case WSANO_DATA: + return "Valid name no data record of requested"; + default: + return "error not defined"; + } +} + +void zmq::win_error (char *buffer_, size_t buffer_size_) +{ + const DWORD errcode = GetLastError (); +#if defined _WIN32_WCE + DWORD rc = FormatMessageW ( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errcode, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) buffer_, + buffer_size_ / sizeof (wchar_t), NULL); +#else + const DWORD rc = FormatMessageA ( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errcode, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), buffer_, + static_cast (buffer_size_), NULL); +#endif + zmq_assert (rc); +} + +int zmq::wsa_error_to_errno (int errcode_) +{ + switch (errcode_) { + // 10004 - Interrupted system call. + case WSAEINTR: + return EINTR; + // 10009 - File handle is not valid. + case WSAEBADF: + return EBADF; + // 10013 - Permission denied. + case WSAEACCES: + return EACCES; + // 10014 - Bad address. + case WSAEFAULT: + return EFAULT; + // 10022 - Invalid argument. + case WSAEINVAL: + return EINVAL; + // 10024 - Too many open files. + case WSAEMFILE: + return EMFILE; + // 10035 - Operation would block. + case WSAEWOULDBLOCK: + return EBUSY; + // 10036 - Operation now in progress. + case WSAEINPROGRESS: + return EAGAIN; + // 10037 - Operation already in progress. + case WSAEALREADY: + return EAGAIN; + // 10038 - Socket operation on non-socket. + case WSAENOTSOCK: + return ENOTSOCK; + // 10039 - Destination address required. + case WSAEDESTADDRREQ: + return EFAULT; + // 10040 - Message too long. + case WSAEMSGSIZE: + return EMSGSIZE; + // 10041 - Protocol wrong type for socket. + case WSAEPROTOTYPE: + return EFAULT; + // 10042 - Bad protocol option. + case WSAENOPROTOOPT: + return EINVAL; + // 10043 - Protocol not supported. + case WSAEPROTONOSUPPORT: + return EPROTONOSUPPORT; + // 10044 - Socket type not supported. + case WSAESOCKTNOSUPPORT: + return EFAULT; + // 10045 - Operation not supported on socket. + case WSAEOPNOTSUPP: + return EFAULT; + // 10046 - Protocol family not supported. + case WSAEPFNOSUPPORT: + return EPROTONOSUPPORT; + // 10047 - Address family not supported by protocol family. + case WSAEAFNOSUPPORT: + return EAFNOSUPPORT; + // 10048 - Address already in use. + case WSAEADDRINUSE: + return EADDRINUSE; + // 10049 - Cannot assign requested address. + case WSAEADDRNOTAVAIL: + return EADDRNOTAVAIL; + // 10050 - Network is down. + case WSAENETDOWN: + return ENETDOWN; + // 10051 - Network is unreachable. + case WSAENETUNREACH: + return ENETUNREACH; + // 10052 - Network dropped connection on reset. + case WSAENETRESET: + return ENETRESET; + // 10053 - Software caused connection abort. + case WSAECONNABORTED: + return ECONNABORTED; + // 10054 - Connection reset by peer. + case WSAECONNRESET: + return ECONNRESET; + // 10055 - No buffer space available. + case WSAENOBUFS: + return ENOBUFS; + // 10056 - Socket is already connected. + case WSAEISCONN: + return EFAULT; + // 10057 - Socket is not connected. + case WSAENOTCONN: + return ENOTCONN; + // 10058 - Can't send after socket shutdown. + case WSAESHUTDOWN: + return EFAULT; + // 10059 - Too many references can't splice. + case WSAETOOMANYREFS: + return EFAULT; + // 10060 - Connection timed out. + case WSAETIMEDOUT: + return ETIMEDOUT; + // 10061 - Connection refused. + case WSAECONNREFUSED: + return ECONNREFUSED; + // 10062 - Too many levels of symbolic links. + case WSAELOOP: + return EFAULT; + // 10063 - File name too long. + case WSAENAMETOOLONG: + return EFAULT; + // 10064 - Host is down. + case WSAEHOSTDOWN: + return EAGAIN; + // 10065 - No route to host. + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + // 10066 - Directory not empty. + case WSAENOTEMPTY: + return EFAULT; + // 10067 - Too many processes. + case WSAEPROCLIM: + return EFAULT; + // 10068 - Too many users. + case WSAEUSERS: + return EFAULT; + // 10069 - Disc Quota Exceeded. + case WSAEDQUOT: + return EFAULT; + // 10070 - Stale NFS file handle. + case WSAESTALE: + return EFAULT; + // 10071 - Too many levels of remote in path. + case WSAEREMOTE: + return EFAULT; + // 10091 - Network SubSystem is unavailable. + case WSASYSNOTREADY: + return EFAULT; + // 10092 - WINSOCK DLL Version out of range. + case WSAVERNOTSUPPORTED: + return EFAULT; + // 10093 - Successful WSASTARTUP not yet performed. + case WSANOTINITIALISED: + return EFAULT; + // 11001 - Host not found. + case WSAHOST_NOT_FOUND: + return EFAULT; + // 11002 - Non-Authoritative Host not found. + case WSATRY_AGAIN: + return EFAULT; + // 11003 - Non-Recoverable errors: FORMERR REFUSED NOTIMP. + case WSANO_RECOVERY: + return EFAULT; + // 11004 - Valid name no data record of requested. + case WSANO_DATA: + return EFAULT; + default: + wsa_assert (false); + } + // Not reachable + return 0; +} + +#endif + +#if defined(HAVE_LIBUNWIND) && !defined(__SUNPRO_CC) + +#define UNW_LOCAL_ONLY +#include +#include +#include +#include "mutex.hpp" + +void zmq::print_backtrace (void) +{ + static zmq::mutex_t mtx; + mtx.lock (); + Dl_info dl_info; + unw_cursor_t cursor; + unw_context_t ctx; + unsigned frame_n = 0; + + unw_getcontext (&ctx); + unw_init_local (&cursor, &ctx); + + while (unw_step (&cursor) > 0) { + unw_word_t offset; + unw_proc_info_t p_info; + static const char unknown[] = "?"; + const char *file_name; + char *demangled_name; + char func_name[256] = ""; + void *addr; + int rc; + + if (unw_get_proc_info (&cursor, &p_info)) + break; + + rc = unw_get_proc_name (&cursor, func_name, 256, &offset); + if (rc == -UNW_ENOINFO) + memcpy (func_name, unknown, sizeof unknown); + + addr = (void *) (p_info.start_ip + offset); + + if (dladdr (addr, &dl_info) && dl_info.dli_fname) + file_name = dl_info.dli_fname; + else + file_name = unknown; + + demangled_name = abi::__cxa_demangle (func_name, NULL, NULL, &rc); + + printf ("#%u %p in %s (%s+0x%lx)\n", frame_n++, addr, file_name, + rc ? func_name : demangled_name, (unsigned long) offset); + free (demangled_name); + } + puts (""); + + fflush (stdout); + mtx.unlock (); +} + +#else + +void zmq::print_backtrace () +{ +} + +#endif diff --git a/3rd/libzmq/src/err.hpp b/3rd/libzmq/src/err.hpp new file mode 100644 index 00000000..3f3278f8 --- /dev/null +++ b/3rd/libzmq/src/err.hpp @@ -0,0 +1,183 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ERR_HPP_INCLUDED__ +#define __ZMQ_ERR_HPP_INCLUDED__ + +#include +#if defined _WIN32_WCE +#include "..\builds\msvc\errno.hpp" +#else +#include +#endif +#include +#include +#include + +#ifndef ZMQ_HAVE_WINDOWS +#include +#endif + +#include "likely.hpp" + +// 0MQ-specific error codes are defined in zmq.h + +// EPROTO is not used by OpenBSD and maybe other platforms. +#ifndef EPROTO +#define EPROTO 0 +#endif + +namespace zmq +{ +const char *errno_to_string (int errno_); +#if defined __clang__ +#if __has_feature(attribute_analyzer_noreturn) +void zmq_abort (const char *errmsg_) __attribute__ ((analyzer_noreturn)); +#else +void zmq_abort (const char *errmsg_); +#endif +#elif defined __MSCVER__ +__declspec(noreturn) void zmq_abort (const char *errmsg_); +#else +void zmq_abort (const char *errmsg_); +#endif +void print_backtrace (); +} + +#ifdef ZMQ_HAVE_WINDOWS + +namespace zmq +{ +const char *wsa_error (); +const char * +wsa_error_no (int no_, + const char *wsae_wouldblock_string_ = "Operation would block"); +void win_error (char *buffer_, size_t buffer_size_); +int wsa_error_to_errno (int errcode_); +} + +// Provides convenient way to check WSA-style errors on Windows. +#define wsa_assert(x) \ + do { \ + if (unlikely (!(x))) { \ + const char *errstr = zmq::wsa_error (); \ + if (errstr != NULL) { \ + fprintf (stderr, "Assertion failed: %s [%i] (%s:%d)\n", \ + errstr, WSAGetLastError (), __FILE__, __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort (errstr); \ + } \ + } \ + } while (false) + +// Provides convenient way to assert on WSA-style errors on Windows. +#define wsa_assert_no(no) \ + do { \ + const char *errstr = zmq::wsa_error_no (no); \ + if (errstr != NULL) { \ + fprintf (stderr, "Assertion failed: %s (%s:%d)\n", errstr, \ + __FILE__, __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort (errstr); \ + } \ + } while (false) + +// Provides convenient way to check GetLastError-style errors on Windows. +#define win_assert(x) \ + do { \ + if (unlikely (!(x))) { \ + char errstr[256]; \ + zmq::win_error (errstr, 256); \ + fprintf (stderr, "Assertion failed: %s (%s:%d)\n", errstr, \ + __FILE__, __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort (errstr); \ + } \ + } while (false) + +#endif + +// This macro works in exactly the same way as the normal assert. It is used +// in its stead because standard assert on Win32 in broken - it prints nothing +// when used within the scope of JNI library. +#define zmq_assert(x) \ + do { \ + if (unlikely (!(x))) { \ + fprintf (stderr, "Assertion failed: %s (%s:%d)\n", #x, __FILE__, \ + __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort (#x); \ + } \ + } while (false) + +// Provides convenient way to check for errno-style errors. +#define errno_assert(x) \ + do { \ + if (unlikely (!(x))) { \ + const char *errstr = strerror (errno); \ + fprintf (stderr, "%s (%s:%d)\n", errstr, __FILE__, __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort (errstr); \ + } \ + } while (false) + +// Provides convenient way to check for POSIX errors. +#define posix_assert(x) \ + do { \ + if (unlikely (x)) { \ + const char *errstr = strerror (x); \ + fprintf (stderr, "%s (%s:%d)\n", errstr, __FILE__, __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort (errstr); \ + } \ + } while (false) + +// Provides convenient way to check for errors from getaddrinfo. +#define gai_assert(x) \ + do { \ + if (unlikely (x)) { \ + const char *errstr = gai_strerror (x); \ + fprintf (stderr, "%s (%s:%d)\n", errstr, __FILE__, __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort (errstr); \ + } \ + } while (false) + +// Provides convenient way to check whether memory allocation have succeeded. +#define alloc_assert(x) \ + do { \ + if (unlikely (!x)) { \ + fprintf (stderr, "FATAL ERROR: OUT OF MEMORY (%s:%d)\n", __FILE__, \ + __LINE__); \ + fflush (stderr); \ + zmq::zmq_abort ("FATAL ERROR: OUT OF MEMORY"); \ + } \ + } while (false) + +#endif diff --git a/3rd/libzmq/src/fd.hpp b/3rd/libzmq/src/fd.hpp new file mode 100644 index 00000000..21925ae6 --- /dev/null +++ b/3rd/libzmq/src/fd.hpp @@ -0,0 +1,63 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_FD_HPP_INCLUDED__ +#define __ZMQ_FD_HPP_INCLUDED__ + +#if defined _WIN32 +#include "windows.hpp" +#endif + +namespace zmq +{ +typedef zmq_fd_t fd_t; + +#ifdef ZMQ_HAVE_WINDOWS +#if defined _MSC_VER && _MSC_VER <= 1400 +enum +{ + retired_fd = (fd_t) (~0) +}; +#else +enum +#if _MSC_VER >= 1800 + : fd_t +#endif +{ + retired_fd = INVALID_SOCKET +}; +#endif +#else +enum +{ + retired_fd = -1 +}; +#endif +} +#endif diff --git a/3rd/libzmq/src/fq.cpp b/3rd/libzmq/src/fq.cpp new file mode 100644 index 00000000..2a700977 --- /dev/null +++ b/3rd/libzmq/src/fq.cpp @@ -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 . +*/ + +#include "precompiled.hpp" +#include "fq.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::fq_t::fq_t () : _active (0), _last_in (NULL), _current (0), _more (false) +{ +} + +zmq::fq_t::~fq_t () +{ + zmq_assert (_pipes.empty ()); +} + +void zmq::fq_t::attach (pipe_t *pipe_) +{ + _pipes.push_back (pipe_); + _pipes.swap (_active, _pipes.size () - 1); + _active++; +} + +void zmq::fq_t::pipe_terminated (pipe_t *pipe_) +{ + const pipes_t::size_type index = _pipes.index (pipe_); + + // Remove the pipe from the list; adjust number of active pipes + // accordingly. + if (index < _active) { + _active--; + _pipes.swap (index, _active); + if (_current == _active) + _current = 0; + } + _pipes.erase (pipe_); + + if (_last_in == pipe_) { + _last_in = NULL; + } +} + +void zmq::fq_t::activated (pipe_t *pipe_) +{ + // Move the pipe to the list of active pipes. + _pipes.swap (_pipes.index (pipe_), _active); + _active++; +} + +int zmq::fq_t::recv (msg_t *msg_) +{ + return recvpipe (msg_, NULL); +} + +int zmq::fq_t::recvpipe (msg_t *msg_, pipe_t **pipe_) +{ + // Deallocate old content of the message. + int rc = msg_->close (); + errno_assert (rc == 0); + + // Round-robin over the pipes to get the next message. + while (_active > 0) { + // Try to fetch new message. If we've already read part of the message + // subsequent part should be immediately available. + const bool fetched = _pipes[_current]->read (msg_); + + // Note that when message is not fetched, current pipe is deactivated + // and replaced by another active pipe. Thus we don't have to increase + // the 'current' pointer. + if (fetched) { + if (pipe_) + *pipe_ = _pipes[_current]; + _more = (msg_->flags () & msg_t::more) != 0; + if (!_more) { + _last_in = _pipes[_current]; + _current = (_current + 1) % _active; + } + return 0; + } + + // Check the atomicity of the message. + // If we've already received the first part of the message + // we should get the remaining parts without blocking. + zmq_assert (!_more); + + _active--; + _pipes.swap (_current, _active); + if (_current == _active) + _current = 0; + } + + // No message is available. Initialise the output parameter + // to be a 0-byte message. + rc = msg_->init (); + errno_assert (rc == 0); + errno = EAGAIN; + return -1; +} + +bool zmq::fq_t::has_in () +{ + // There are subsequent parts of the partly-read message available. + if (_more) + return true; + + // Note that messing with current doesn't break the fairness of fair + // queueing algorithm. If there are no messages available current will + // get back to its original value. Otherwise it'll point to the first + // pipe holding messages, skipping only pipes with no messages available. + while (_active > 0) { + if (_pipes[_current]->check_read ()) + return true; + + // Deactivate the pipe. + _active--; + _pipes.swap (_current, _active); + if (_current == _active) + _current = 0; + } + + return false; +} diff --git a/3rd/libzmq/src/fq.hpp b/3rd/libzmq/src/fq.hpp new file mode 100644 index 00000000..26724101 --- /dev/null +++ b/3rd/libzmq/src/fq.hpp @@ -0,0 +1,84 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_FQ_HPP_INCLUDED__ +#define __ZMQ_FQ_HPP_INCLUDED__ + +#include "array.hpp" +#include "blob.hpp" + +namespace zmq +{ +class msg_t; +class pipe_t; + +// Class manages a set of inbound pipes. On receive it performs fair +// queueing so that senders gone berserk won't cause denial of +// service for decent senders. + +class fq_t +{ + public: + fq_t (); + ~fq_t (); + + void attach (pipe_t *pipe_); + void activated (pipe_t *pipe_); + void pipe_terminated (pipe_t *pipe_); + + int recv (msg_t *msg_); + int recvpipe (msg_t *msg_, pipe_t **pipe_); + bool has_in (); + + private: + // Inbound pipes. + typedef array_t pipes_t; + pipes_t _pipes; + + // Number of active pipes. All the active pipes are located at the + // beginning of the pipes array. + pipes_t::size_type _active; + + // Pointer to the last pipe we received message from. + // NULL when no message has been received or the pipe + // has terminated. + pipe_t *_last_in; + + // Index of the next bound pipe to read a message from. + pipes_t::size_type _current; + + // If true, part of a multipart message was already received, but + // there are following parts still waiting in the current pipe. + bool _more; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (fq_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/gather.cpp b/3rd/libzmq/src/gather.cpp new file mode 100644 index 00000000..231e7253 --- /dev/null +++ b/3rd/libzmq/src/gather.cpp @@ -0,0 +1,91 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "gather.hpp" +#include "err.hpp" +#include "msg.hpp" +#include "pipe.hpp" + +zmq::gather_t::gather_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_, true) +{ + options.type = ZMQ_GATHER; +} + +zmq::gather_t::~gather_t () +{ +} + +void zmq::gather_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + _fq.attach (pipe_); +} + +void zmq::gather_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +void zmq::gather_t::xpipe_terminated (pipe_t *pipe_) +{ + _fq.pipe_terminated (pipe_); +} + +int zmq::gather_t::xrecv (msg_t *msg_) +{ + int rc = _fq.recvpipe (msg_, NULL); + + // Drop any messages with more flag + while (rc == 0 && msg_->flags () & msg_t::more) { + // drop all frames of the current multi-frame message + rc = _fq.recvpipe (msg_, NULL); + + while (rc == 0 && msg_->flags () & msg_t::more) + rc = _fq.recvpipe (msg_, NULL); + + // get the new message + if (rc == 0) + rc = _fq.recvpipe (msg_, NULL); + } + + return rc; +} + +bool zmq::gather_t::xhas_in () +{ + return _fq.has_in (); +} diff --git a/3rd/libzmq/src/gather.hpp b/3rd/libzmq/src/gather.hpp new file mode 100644 index 00000000..f2de0647 --- /dev/null +++ b/3rd/libzmq/src/gather.hpp @@ -0,0 +1,66 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_GATHER_HPP_INCLUDED__ +#define __ZMQ_GATHER_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "fq.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; +class msg_t; + +class gather_t ZMQ_FINAL : public socket_base_t +{ + public: + gather_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~gather_t (); + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + void xread_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + // Fair queueing object for inbound pipes. + fq_t _fq; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (gather_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/generic_mtrie.hpp b/3rd/libzmq/src/generic_mtrie.hpp new file mode 100644 index 00000000..15dfad63 --- /dev/null +++ b/3rd/libzmq/src/generic_mtrie.hpp @@ -0,0 +1,117 @@ +/* +Copyright (c) 2018 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 . +*/ + +#ifndef __ZMQ_GENERIC_MTRIE_HPP_INCLUDED__ +#define __ZMQ_GENERIC_MTRIE_HPP_INCLUDED__ + +#include +#include + +#include "macros.hpp" +#include "stdint.hpp" + +namespace zmq +{ +// Multi-trie (prefix tree). Each node in the trie is a set of pointers. +template class generic_mtrie_t +{ + public: + typedef T value_t; + typedef const unsigned char *prefix_t; + + enum rm_result + { + not_found, + last_value_removed, + values_remain + }; + + generic_mtrie_t (); + ~generic_mtrie_t (); + + // Add key to the trie. Returns true iff no entry with the same prefix_ + // and size_ existed before. + bool add (prefix_t prefix_, size_t size_, value_t *value_); + + // Remove all entries with a specific value from the trie. + // The call_on_uniq_ flag controls if the callback is invoked + // when there are no entries left on a prefix only (true) + // or on every removal (false). The arg_ argument is passed + // through to the callback function. + template + void rm (value_t *value_, + void (*func_) (const unsigned char *data_, size_t size_, Arg arg_), + Arg arg_, + bool call_on_uniq_); + + // Removes a specific entry from the trie. + // Returns the result of the operation. + rm_result rm (prefix_t prefix_, size_t size_, value_t *value_); + + // Calls a callback function for all matching entries, i.e. any node + // corresponding to data_ or a prefix of it. The arg_ argument + // is passed through to the callback function. + template + void match (prefix_t data_, + size_t size_, + void (*func_) (value_t *value_, Arg arg_), + Arg arg_); + + private: + bool is_redundant () const; + + typedef std::set pipes_t; + pipes_t *_pipes; + + unsigned char _min; + unsigned short _count; + unsigned short _live_nodes; + union _next_t + { + class generic_mtrie_t *node; + class generic_mtrie_t **table; + } _next; + + struct iter + { + generic_mtrie_t *node; + generic_mtrie_t *next_node; + prefix_t prefix; + size_t size; + unsigned short current_child; + unsigned char new_min; + unsigned char new_max; + bool processed_for_removal; + }; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (generic_mtrie_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/generic_mtrie_impl.hpp b/3rd/libzmq/src/generic_mtrie_impl.hpp new file mode 100644 index 00000000..35dae307 --- /dev/null +++ b/3rd/libzmq/src/generic_mtrie_impl.hpp @@ -0,0 +1,593 @@ +/* +Copyright (c) 2018 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 . +*/ + +#ifndef __ZMQ_GENERIC_MTRIE_IMPL_HPP_INCLUDED__ +#define __ZMQ_GENERIC_MTRIE_IMPL_HPP_INCLUDED__ + + +#include + +#include +#include +#include + +#include "err.hpp" +#include "macros.hpp" +#include "generic_mtrie.hpp" + +namespace zmq +{ +template +generic_mtrie_t::generic_mtrie_t () : + _pipes (0), + _min (0), + _count (0), + _live_nodes (0) +{ +} + +template generic_mtrie_t::~generic_mtrie_t () +{ + LIBZMQ_DELETE (_pipes); + + if (_count == 1) { + zmq_assert (_next.node); + LIBZMQ_DELETE (_next.node); + } else if (_count > 1) { + for (unsigned short i = 0; i != _count; ++i) { + LIBZMQ_DELETE (_next.table[i]); + } + free (_next.table); + } +} + +template +bool generic_mtrie_t::add (prefix_t prefix_, size_t size_, value_t *pipe_) +{ + generic_mtrie_t *it = this; + + while (size_) { + const unsigned char c = *prefix_; + + if (c < it->_min || c >= it->_min + it->_count) { + // The character is out of range of currently handled + // characters. We have to extend the table. + if (!it->_count) { + it->_min = c; + it->_count = 1; + it->_next.node = NULL; + } else if (it->_count == 1) { + const unsigned char oldc = it->_min; + generic_mtrie_t *oldp = it->_next.node; + it->_count = (it->_min < c ? c - it->_min : it->_min - c) + 1; + it->_next.table = static_cast ( + malloc (sizeof (generic_mtrie_t *) * it->_count)); + alloc_assert (it->_next.table); + for (unsigned short i = 0; i != it->_count; ++i) + it->_next.table[i] = 0; + it->_min = std::min (it->_min, c); + it->_next.table[oldc - it->_min] = oldp; + } else if (it->_min < c) { + // The new character is above the current character range. + const unsigned short old_count = it->_count; + it->_count = c - it->_min + 1; + it->_next.table = static_cast (realloc ( + it->_next.table, sizeof (generic_mtrie_t *) * it->_count)); + alloc_assert (it->_next.table); + for (unsigned short i = old_count; i != it->_count; i++) + it->_next.table[i] = NULL; + } else { + // The new character is below the current character range. + const unsigned short old_count = it->_count; + it->_count = (it->_min + old_count) - c; + it->_next.table = static_cast (realloc ( + it->_next.table, sizeof (generic_mtrie_t *) * it->_count)); + alloc_assert (it->_next.table); + memmove (it->_next.table + it->_min - c, it->_next.table, + old_count * sizeof (generic_mtrie_t *)); + for (unsigned short i = 0; i != it->_min - c; i++) + it->_next.table[i] = NULL; + it->_min = c; + } + } + + // If next node does not exist, create one. + if (it->_count == 1) { + if (!it->_next.node) { + it->_next.node = new (std::nothrow) generic_mtrie_t; + alloc_assert (it->_next.node); + ++(it->_live_nodes); + } + + ++prefix_; + --size_; + it = it->_next.node; + } else { + if (!it->_next.table[c - it->_min]) { + it->_next.table[c - it->_min] = + new (std::nothrow) generic_mtrie_t; + alloc_assert (it->_next.table[c - it->_min]); + ++(it->_live_nodes); + } + + ++prefix_; + --size_; + it = it->_next.table[c - it->_min]; + } + } + + // We are at the node corresponding to the prefix. We are done. + const bool result = !it->_pipes; + if (!it->_pipes) { + it->_pipes = new (std::nothrow) pipes_t; + alloc_assert (it->_pipes); + } + it->_pipes->insert (pipe_); + + return result; +} + +template +template +void generic_mtrie_t::rm (value_t *pipe_, + void (*func_) (prefix_t data_, + size_t size_, + Arg arg_), + Arg arg_, + bool call_on_uniq_) +{ + // This used to be implemented as a non-tail recursive travesal of the trie, + // which means remote clients controlled the depth of the recursion and the + // stack size. + // To simulate the non-tail recursion, with post-recursion changes depending on + // the result of the recursive call, a stack is used to re-visit the same node + // and operate on it again after children have been visisted. + // A boolean is used to record whether the node had already been visited and to + // determine if the pre- or post- children visit actions have to be taken. + // In the case of a node with (N > 1) children, the node has to be re-visited + // N times, in the correct order after each child visit. + std::list stack; + unsigned char *buff = NULL; + size_t maxbuffsize = 0; + struct iter it = {this, NULL, NULL, 0, 0, 0, 0, false}; + stack.push_back (it); + + while (!stack.empty ()) { + it = stack.back (); + stack.pop_back (); + + if (!it.processed_for_removal) { + // Remove the subscription from this node. + if (it.node->_pipes && it.node->_pipes->erase (pipe_)) { + if (!call_on_uniq_ || it.node->_pipes->empty ()) { + func_ (buff, it.size, arg_); + } + + if (it.node->_pipes->empty ()) { + LIBZMQ_DELETE (it.node->_pipes); + } + } + + // Adjust the buffer. + if (it.size >= maxbuffsize) { + maxbuffsize = it.size + 256; + buff = + static_cast (realloc (buff, maxbuffsize)); + alloc_assert (buff); + } + + switch (it.node->_count) { + case 0: + // If there are no subnodes in the trie, we are done with this node + // pre-processing. + break; + case 1: { + // If there's one subnode (optimisation). + + buff[it.size] = it.node->_min; + // Mark this node as pre-processed and push it, so that the next + // visit after the operation on the child can do the removals. + it.processed_for_removal = true; + stack.push_back (it); + struct iter next = {it.node->_next.node, + NULL, + NULL, + ++it.size, + 0, + 0, + 0, + false}; + stack.push_back (next); + break; + } + default: { + // If there are multiple subnodes. + // When first visiting this node, initialize the new_min/max parameters + // which will then be used after each child has been processed, on the + // post-children iterations. + if (it.current_child == 0) { + // New min non-null character in the node table after the removal + it.new_min = it.node->_min + it.node->_count - 1; + // New max non-null character in the node table after the removal + it.new_max = it.node->_min; + } + + // Mark this node as pre-processed and push it, so that the next + // visit after the operation on the child can do the removals. + buff[it.size] = it.node->_min + it.current_child; + it.processed_for_removal = true; + stack.push_back (it); + if (it.node->_next.table[it.current_child]) { + struct iter next = { + it.node->_next.table[it.current_child], + NULL, + NULL, + it.size + 1, + 0, + 0, + 0, + false}; + stack.push_back (next); + } + } + } + } else { + // Reset back for the next time, in case this node doesn't get deleted. + // This is done unconditionally, unlike when setting this variable to true. + it.processed_for_removal = false; + + switch (it.node->_count) { + case 0: + // If there are no subnodes in the trie, we are done with this node + // post-processing. + break; + case 1: + // If there's one subnode (optimisation). + + // Prune the node if it was made redundant by the removal + if (it.node->_next.node->is_redundant ()) { + LIBZMQ_DELETE (it.node->_next.node); + it.node->_count = 0; + --it.node->_live_nodes; + zmq_assert (it.node->_live_nodes == 0); + } + break; + default: + // If there are multiple subnodes. + { + if (it.node->_next.table[it.current_child]) { + // Prune redundant nodes from the mtrie + if (it.node->_next.table[it.current_child] + ->is_redundant ()) { + LIBZMQ_DELETE ( + it.node->_next.table[it.current_child]); + + zmq_assert (it.node->_live_nodes > 0); + --it.node->_live_nodes; + } else { + // The node is not redundant, so it's a candidate for being + // the new min/max node. + // + // We loop through the node array from left to right, so the + // first non-null, non-redundant node encountered is the new + // minimum index. Conversely, the last non-redundant, non-null + // node encountered is the new maximum index. + if (it.current_child + it.node->_min + < it.new_min) + it.new_min = + it.current_child + it.node->_min; + if (it.current_child + it.node->_min + > it.new_max) + it.new_max = + it.current_child + it.node->_min; + } + } + + // If there are more children to visit, push again the current + // node, so that pre-processing can happen on the next child. + // If we are done, reset the child index so that the ::rm is + // fully idempotent. + ++it.current_child; + if (it.current_child >= it.node->_count) + it.current_child = 0; + else { + stack.push_back (it); + continue; + } + + // All children have been visited and removed if needed, and + // all pre- and post-visit operations have been carried. + // Resize/free the node table if needed. + zmq_assert (it.node->_count > 1); + + // Free the node table if it's no longer used. + switch (it.node->_live_nodes) { + case 0: + free (it.node->_next.table); + it.node->_next.table = NULL; + it.node->_count = 0; + break; + case 1: + // Compact the node table if possible + + // If there's only one live node in the table we can + // switch to using the more compact single-node + // representation + zmq_assert (it.new_min == it.new_max); + zmq_assert (it.new_min >= it.node->_min); + zmq_assert (it.new_min + < it.node->_min + it.node->_count); + { + generic_mtrie_t *node = + it.node->_next + .table[it.new_min - it.node->_min]; + zmq_assert (node); + free (it.node->_next.table); + it.node->_next.node = node; + } + it.node->_count = 1; + it.node->_min = it.new_min; + break; + default: + if (it.new_min > it.node->_min + || it.new_max < it.node->_min + + it.node->_count - 1) { + zmq_assert (it.new_max - it.new_min + 1 + > 1); + + generic_mtrie_t **old_table = + it.node->_next.table; + zmq_assert (it.new_min > it.node->_min + || it.new_max + < it.node->_min + + it.node->_count - 1); + zmq_assert (it.new_min >= it.node->_min); + zmq_assert (it.new_max + <= it.node->_min + + it.node->_count - 1); + zmq_assert (it.new_max - it.new_min + 1 + < it.node->_count); + + it.node->_count = + it.new_max - it.new_min + 1; + it.node->_next.table = + static_cast ( + malloc (sizeof (generic_mtrie_t *) + * it.node->_count)); + alloc_assert (it.node->_next.table); + + memmove (it.node->_next.table, + old_table + + (it.new_min - it.node->_min), + sizeof (generic_mtrie_t *) + * it.node->_count); + free (old_table); + + it.node->_min = it.new_min; + } + } + } + } + } + } + + free (buff); +} + +template +typename generic_mtrie_t::rm_result +generic_mtrie_t::rm (prefix_t prefix_, size_t size_, value_t *pipe_) +{ + // This used to be implemented as a non-tail recursive travesal of the trie, + // which means remote clients controlled the depth of the recursion and the + // stack size. + // To simulate the non-tail recursion, with post-recursion changes depending on + // the result of the recursive call, a stack is used to re-visit the same node + // and operate on it again after children have been visisted. + // A boolean is used to record whether the node had already been visited and to + // determine if the pre- or post- children visit actions have to be taken. + rm_result ret = not_found; + std::list stack; + struct iter it = {this, NULL, prefix_, size_, 0, 0, 0, false}; + stack.push_back (it); + + while (!stack.empty ()) { + it = stack.back (); + stack.pop_back (); + + if (!it.processed_for_removal) { + if (!it.size) { + if (!it.node->_pipes) { + ret = not_found; + continue; + } + + typename pipes_t::size_type erased = + it.node->_pipes->erase (pipe_); + if (it.node->_pipes->empty ()) { + zmq_assert (erased == 1); + LIBZMQ_DELETE (it.node->_pipes); + ret = last_value_removed; + continue; + } + + ret = (erased == 1) ? values_remain : not_found; + continue; + } + + it.current_child = *it.prefix; + if (!it.node->_count || it.current_child < it.node->_min + || it.current_child >= it.node->_min + it.node->_count) { + ret = not_found; + continue; + } + + it.next_node = + it.node->_count == 1 + ? it.node->_next.node + : it.node->_next.table[it.current_child - it.node->_min]; + if (!it.next_node) { + ret = not_found; + continue; + } + + it.processed_for_removal = true; + stack.push_back (it); + struct iter next = { + it.next_node, NULL, it.prefix + 1, it.size - 1, 0, 0, 0, false}; + stack.push_back (next); + } else { + it.processed_for_removal = false; + + if (it.next_node->is_redundant ()) { + LIBZMQ_DELETE (it.next_node); + zmq_assert (it.node->_count > 0); + + if (it.node->_count == 1) { + it.node->_next.node = NULL; + it.node->_count = 0; + --it.node->_live_nodes; + zmq_assert (it.node->_live_nodes == 0); + } else { + it.node->_next.table[it.current_child - it.node->_min] = 0; + zmq_assert (it.node->_live_nodes > 1); + --it.node->_live_nodes; + + // Compact the table if possible + if (it.node->_live_nodes == 1) { + // If there's only one live node in the table we can + // switch to using the more compact single-node + // representation + unsigned short i; + for (i = 0; i < it.node->_count; ++i) + if (it.node->_next.table[i]) + break; + + zmq_assert (i < it.node->_count); + it.node->_min += i; + it.node->_count = 1; + generic_mtrie_t *oldp = it.node->_next.table[i]; + free (it.node->_next.table); + it.node->_next.table = NULL; + it.node->_next.node = oldp; + } else if (it.current_child == it.node->_min) { + // We can compact the table "from the left" + unsigned short i; + for (i = 1; i < it.node->_count; ++i) + if (it.node->_next.table[i]) + break; + + zmq_assert (i < it.node->_count); + it.node->_min += i; + it.node->_count -= i; + generic_mtrie_t **old_table = it.node->_next.table; + it.node->_next.table = + static_cast (malloc ( + sizeof (generic_mtrie_t *) * it.node->_count)); + alloc_assert (it.node->_next.table); + memmove (it.node->_next.table, old_table + i, + sizeof (generic_mtrie_t *) * it.node->_count); + free (old_table); + } else if (it.current_child + == it.node->_min + it.node->_count - 1) { + // We can compact the table "from the right" + unsigned short i; + for (i = 1; i < it.node->_count; ++i) + if (it.node->_next.table[it.node->_count - 1 - i]) + break; + + zmq_assert (i < it.node->_count); + it.node->_count -= i; + generic_mtrie_t **old_table = it.node->_next.table; + it.node->_next.table = + static_cast (malloc ( + sizeof (generic_mtrie_t *) * it.node->_count)); + alloc_assert (it.node->_next.table); + memmove (it.node->_next.table, old_table, + sizeof (generic_mtrie_t *) * it.node->_count); + free (old_table); + } + } + } + } + } + + return ret; +} + +template +template +void generic_mtrie_t::match (prefix_t data_, + size_t size_, + void (*func_) (value_t *pipe_, Arg arg_), + Arg arg_) +{ + for (generic_mtrie_t *current = this; current; data_++, size_--) { + // Signal the pipes attached to this node. + if (current->_pipes) { + for (typename pipes_t::iterator it = current->_pipes->begin (), + end = current->_pipes->end (); + it != end; ++it) { + func_ (*it, arg_); + } + } + + // If we are at the end of the message, there's nothing more to match. + if (!size_) + break; + + // If there are no subnodes in the trie, return. + if (current->_count == 0) + break; + + if (current->_count == 1) { + // If there's one subnode (optimisation). + if (data_[0] != current->_min) { + break; + } + current = current->_next.node; + } else { + // If there are multiple subnodes. + if (data_[0] < current->_min + || data_[0] >= current->_min + current->_count) { + break; + } + current = current->_next.table[data_[0] - current->_min]; + } + } +} + +template bool generic_mtrie_t::is_redundant () const +{ + return !_pipes && _live_nodes == 0; +} +} + + +#endif diff --git a/3rd/libzmq/src/gssapi_client.cpp b/3rd/libzmq/src/gssapi_client.cpp new file mode 100644 index 00000000..291e9f09 --- /dev/null +++ b/3rd/libzmq/src/gssapi_client.cpp @@ -0,0 +1,236 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#ifdef HAVE_LIBGSSAPI_KRB5 + +#include +#include + +#include "msg.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "gssapi_client.hpp" +#include "wire.hpp" + +zmq::gssapi_client_t::gssapi_client_t (session_base_t *session_, + const options_t &options_) : + mechanism_base_t (session_, options_), + gssapi_mechanism_base_t (session_, options_), + state (call_next_init), + token_ptr (GSS_C_NO_BUFFER), + mechs (), + security_context_established (false) +{ + const std::string::size_type service_size = + options_.gss_service_principal.size (); + service_name = static_cast (malloc (service_size + 1)); + assert (service_name); + memcpy (service_name, options_.gss_service_principal.c_str (), + service_size + 1); + + service_name_type = convert_nametype (options_.gss_service_principal_nt); + maj_stat = GSS_S_COMPLETE; + if (!options_.gss_principal.empty ()) { + const std::string::size_type principal_size = + options_.gss_principal.size (); + principal_name = static_cast (malloc (principal_size + 1)); + assert (principal_name); + memcpy (principal_name, options_.gss_principal.c_str (), + principal_size + 1); + + gss_OID name_type = convert_nametype (options_.gss_principal_nt); + if (acquire_credentials (principal_name, &cred, name_type) != 0) + maj_stat = GSS_S_FAILURE; + } + + mechs.elements = NULL; + mechs.count = 0; +} + +zmq::gssapi_client_t::~gssapi_client_t () +{ + if (service_name) + free (service_name); + if (cred) + gss_release_cred (&min_stat, &cred); +} + +int zmq::gssapi_client_t::next_handshake_command (msg_t *msg_) +{ + if (state == send_ready) { + int rc = produce_ready (msg_); + if (rc == 0) + state = connected; + + return rc; + } + + if (state != call_next_init) { + errno = EAGAIN; + return -1; + } + + if (initialize_context () < 0) + return -1; + + if (produce_next_token (msg_) < 0) + return -1; + + if (maj_stat != GSS_S_CONTINUE_NEEDED && maj_stat != GSS_S_COMPLETE) + return -1; + + if (maj_stat == GSS_S_COMPLETE) { + security_context_established = true; + state = recv_ready; + } else + state = recv_next_token; + + return 0; +} + +int zmq::gssapi_client_t::process_handshake_command (msg_t *msg_) +{ + if (state == recv_ready) { + int rc = process_ready (msg_); + if (rc == 0) + state = send_ready; + + return rc; + } + + if (state != recv_next_token) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + + if (process_next_token (msg_) < 0) + return -1; + + if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) + return -1; + + state = call_next_init; + + errno_assert (msg_->close () == 0); + errno_assert (msg_->init () == 0); + + return 0; +} + +int zmq::gssapi_client_t::encode (msg_t *msg_) +{ + zmq_assert (state == connected); + + if (do_encryption) + return encode_message (msg_); + + return 0; +} + +int zmq::gssapi_client_t::decode (msg_t *msg_) +{ + zmq_assert (state == connected); + + if (do_encryption) + return decode_message (msg_); + + return 0; +} + +zmq::mechanism_t::status_t zmq::gssapi_client_t::status () const +{ + return state == connected ? mechanism_t::ready : mechanism_t::handshaking; +} + +int zmq::gssapi_client_t::initialize_context () +{ + // principal was specified but credentials could not be acquired + if (principal_name != NULL && cred == NULL) + return -1; + + // First time through, import service_name into target_name + if (target_name == GSS_C_NO_NAME) { + send_tok.value = service_name; + send_tok.length = strlen (service_name) + 1; + OM_uint32 maj = gss_import_name (&min_stat, &send_tok, + service_name_type, &target_name); + + if (maj != GSS_S_COMPLETE) + return -1; + } + + maj_stat = gss_init_sec_context ( + &init_sec_min_stat, cred, &context, target_name, mechs.elements, + gss_flags, 0, NULL, token_ptr, NULL, &send_tok, &ret_flags, NULL); + + if (token_ptr != GSS_C_NO_BUFFER) + free (recv_tok.value); + + return 0; +} + +int zmq::gssapi_client_t::produce_next_token (msg_t *msg_) +{ + if (send_tok.length != 0) { // Server expects another token + if (produce_initiate (msg_, send_tok.value, send_tok.length) < 0) { + gss_release_buffer (&min_stat, &send_tok); + gss_release_name (&min_stat, &target_name); + return -1; + } + } + gss_release_buffer (&min_stat, &send_tok); + + if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { + gss_release_name (&min_stat, &target_name); + if (context != GSS_C_NO_CONTEXT) + gss_delete_sec_context (&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } + + return 0; +} + +int zmq::gssapi_client_t::process_next_token (msg_t *msg_) +{ + if (maj_stat == GSS_S_CONTINUE_NEEDED) { + if (process_initiate (msg_, &recv_tok.value, recv_tok.length) < 0) { + gss_release_name (&min_stat, &target_name); + return -1; + } + token_ptr = &recv_tok; + } + + return 0; +} + +#endif diff --git a/3rd/libzmq/src/gssapi_client.hpp b/3rd/libzmq/src/gssapi_client.hpp new file mode 100644 index 00000000..18d428c8 --- /dev/null +++ b/3rd/libzmq/src/gssapi_client.hpp @@ -0,0 +1,92 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_GSSAPI_CLIENT_HPP_INCLUDED__ +#define __ZMQ_GSSAPI_CLIENT_HPP_INCLUDED__ + +#ifdef HAVE_LIBGSSAPI_KRB5 + +#include "gssapi_mechanism_base.hpp" + +namespace zmq +{ +class msg_t; +class session_base_t; + +class gssapi_client_t ZMQ_FINAL : public gssapi_mechanism_base_t +{ + public: + gssapi_client_t (session_base_t *session_, const options_t &options_); + ~gssapi_client_t () ZMQ_FINAL; + + // mechanism implementation + int next_handshake_command (msg_t *msg_) ZMQ_FINAL; + int process_handshake_command (msg_t *msg_) ZMQ_FINAL; + int encode (msg_t *msg_) ZMQ_FINAL; + int decode (msg_t *msg_) ZMQ_FINAL; + status_t status () const ZMQ_FINAL; + + private: + enum state_t + { + call_next_init, + send_next_token, + recv_next_token, + send_ready, + recv_ready, + connected + }; + + // Human-readable principal name of the service we are connecting to + char *service_name; + + gss_OID service_name_type; + + // Current FSM state + state_t state; + + // Points to either send_tok or recv_tok + // during context initialization + gss_buffer_desc *token_ptr; + + // The desired underlying mechanism + gss_OID_set_desc mechs; + + // True iff client considers the server authenticated + bool security_context_established; + + int initialize_context (); + int produce_next_token (msg_t *msg_); + int process_next_token (msg_t *msg_); +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/gssapi_mechanism_base.cpp b/3rd/libzmq/src/gssapi_mechanism_base.cpp new file mode 100644 index 00000000..b9ffeec5 --- /dev/null +++ b/3rd/libzmq/src/gssapi_mechanism_base.cpp @@ -0,0 +1,402 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#ifdef HAVE_LIBGSSAPI_KRB5 + +#include +#include + +#include "msg.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "gssapi_mechanism_base.hpp" +#include "wire.hpp" + +zmq::gssapi_mechanism_base_t::gssapi_mechanism_base_t ( + session_base_t *session_, const options_t &options_) : + mechanism_base_t (session_, options_), + send_tok (), + recv_tok (), + /// FIXME remove? in_buf (), + target_name (GSS_C_NO_NAME), + principal_name (NULL), + maj_stat (GSS_S_COMPLETE), + min_stat (0), + init_sec_min_stat (0), + ret_flags (0), + gss_flags (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG), + cred (GSS_C_NO_CREDENTIAL), + context (GSS_C_NO_CONTEXT), + do_encryption (!options_.gss_plaintext) +{ +} + +zmq::gssapi_mechanism_base_t::~gssapi_mechanism_base_t () +{ + if (target_name) + gss_release_name (&min_stat, &target_name); + if (context) + gss_delete_sec_context (&min_stat, &context, GSS_C_NO_BUFFER); +} + +int zmq::gssapi_mechanism_base_t::encode_message (msg_t *msg_) +{ + // Wrap the token value + int state; + gss_buffer_desc plaintext; + gss_buffer_desc wrapped; + + uint8_t flags = 0; + if (msg_->flags () & msg_t::more) + flags |= 0x01; + if (msg_->flags () & msg_t::command) + flags |= 0x02; + + uint8_t *plaintext_buffer = + static_cast (malloc (msg_->size () + 1)); + alloc_assert (plaintext_buffer); + + plaintext_buffer[0] = flags; + memcpy (plaintext_buffer + 1, msg_->data (), msg_->size ()); + + plaintext.value = plaintext_buffer; + plaintext.length = msg_->size () + 1; + + maj_stat = gss_wrap (&min_stat, context, 1, GSS_C_QOP_DEFAULT, &plaintext, + &state, &wrapped); + + zmq_assert (maj_stat == GSS_S_COMPLETE); + zmq_assert (state); + + // Re-initialize msg_ for wrapped text + int rc = msg_->close (); + zmq_assert (rc == 0); + + rc = msg_->init_size (8 + 4 + wrapped.length); + zmq_assert (rc == 0); + + uint8_t *ptr = static_cast (msg_->data ()); + + // Add command string + memcpy (ptr, "\x07MESSAGE", 8); + ptr += 8; + + // Add token length + put_uint32 (ptr, static_cast (wrapped.length)); + ptr += 4; + + // Add wrapped token value + memcpy (ptr, wrapped.value, wrapped.length); + ptr += wrapped.length; + + gss_release_buffer (&min_stat, &wrapped); + + return 0; +} + +int zmq::gssapi_mechanism_base_t::decode_message (msg_t *msg_) +{ + const uint8_t *ptr = static_cast (msg_->data ()); + size_t bytes_left = msg_->size (); + + int rc = check_basic_command_structure (msg_); + if (rc == -1) + return rc; + + // Get command string + if (bytes_left < 8 || memcmp (ptr, "\x07MESSAGE", 8)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + ptr += 8; + bytes_left -= 8; + + // Get token length + if (bytes_left < 4) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE); + errno = EPROTO; + return -1; + } + gss_buffer_desc wrapped; + wrapped.length = get_uint32 (ptr); + ptr += 4; + bytes_left -= 4; + + // Get token value + if (bytes_left < wrapped.length) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE); + errno = EPROTO; + return -1; + } + // TODO: instead of malloc/memcpy, can we just do: wrapped.value = ptr; + const size_t alloc_length = wrapped.length ? wrapped.length : 1; + wrapped.value = static_cast (malloc (alloc_length)); + alloc_assert (wrapped.value); + + if (wrapped.length) { + memcpy (wrapped.value, ptr, wrapped.length); + ptr += wrapped.length; + bytes_left -= wrapped.length; + } + + // Unwrap the token value + int state; + gss_buffer_desc plaintext; + maj_stat = gss_unwrap (&min_stat, context, &wrapped, &plaintext, &state, + (gss_qop_t *) NULL); + + if (maj_stat != GSS_S_COMPLETE) { + gss_release_buffer (&min_stat, &plaintext); + free (wrapped.value); + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); + errno = EPROTO; + return -1; + } + zmq_assert (state); + + // Re-initialize msg_ for plaintext + rc = msg_->close (); + zmq_assert (rc == 0); + + rc = msg_->init_size (plaintext.length - 1); + zmq_assert (rc == 0); + + const uint8_t flags = static_cast (plaintext.value)[0]; + if (flags & 0x01) + msg_->set_flags (msg_t::more); + if (flags & 0x02) + msg_->set_flags (msg_t::command); + + memcpy (msg_->data (), static_cast (plaintext.value) + 1, + plaintext.length - 1); + + gss_release_buffer (&min_stat, &plaintext); + free (wrapped.value); + + if (bytes_left > 0) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE); + errno = EPROTO; + return -1; + } + + return 0; +} + +int zmq::gssapi_mechanism_base_t::produce_initiate (msg_t *msg_, + void *token_value_, + size_t token_length_) +{ + zmq_assert (token_value_); + zmq_assert (token_length_ <= 0xFFFFFFFFUL); + + const size_t command_size = 9 + 4 + token_length_; + + const int rc = msg_->init_size (command_size); + errno_assert (rc == 0); + + uint8_t *ptr = static_cast (msg_->data ()); + + // Add command string + memcpy (ptr, "\x08INITIATE", 9); + ptr += 9; + + // Add token length + put_uint32 (ptr, static_cast (token_length_)); + ptr += 4; + + // Add token value + memcpy (ptr, token_value_, token_length_); + ptr += token_length_; + + return 0; +} + +int zmq::gssapi_mechanism_base_t::process_initiate (msg_t *msg_, + void **token_value_, + size_t &token_length_) +{ + zmq_assert (token_value_); + + const uint8_t *ptr = static_cast (msg_->data ()); + size_t bytes_left = msg_->size (); + + int rc = check_basic_command_structure (msg_); + if (rc == -1) + return rc; + + // Get command string + if (bytes_left < 9 || memcmp (ptr, "\x08INITIATE", 9)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + ptr += 9; + bytes_left -= 9; + + // Get token length + if (bytes_left < 4) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE); + errno = EPROTO; + return -1; + } + token_length_ = get_uint32 (ptr); + ptr += 4; + bytes_left -= 4; + + // Get token value + if (bytes_left < token_length_) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE); + errno = EPROTO; + return -1; + } + + *token_value_ = + static_cast (malloc (token_length_ ? token_length_ : 1)); + alloc_assert (*token_value_); + + if (token_length_) { + memcpy (*token_value_, ptr, token_length_); + ptr += token_length_; + bytes_left -= token_length_; + } + + if (bytes_left > 0) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE); + errno = EPROTO; + return -1; + } + + return 0; +} + +int zmq::gssapi_mechanism_base_t::produce_ready (msg_t *msg_) +{ + make_command_with_basic_properties (msg_, "\5READY", 6); + + if (do_encryption) + return encode_message (msg_); + + return 0; +} + +int zmq::gssapi_mechanism_base_t::process_ready (msg_t *msg_) +{ + if (do_encryption) { + const int rc = decode_message (msg_); + if (rc != 0) + return rc; + } + + const unsigned char *ptr = static_cast (msg_->data ()); + size_t bytes_left = msg_->size (); + + int rc = check_basic_command_structure (msg_); + if (rc == -1) + return rc; + + if (bytes_left < 6 || memcmp (ptr, "\x05READY", 6)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + ptr += 6; + bytes_left -= 6; + rc = parse_metadata (ptr, bytes_left); + if (rc == -1) + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA); + + return rc; +} + +const gss_OID zmq::gssapi_mechanism_base_t::convert_nametype (int zmq_nametype) +{ + switch (zmq_nametype) { + case ZMQ_GSSAPI_NT_HOSTBASED: + return GSS_C_NT_HOSTBASED_SERVICE; + case ZMQ_GSSAPI_NT_USER_NAME: + return GSS_C_NT_USER_NAME; + case ZMQ_GSSAPI_NT_KRB5_PRINCIPAL: +#ifdef GSS_KRB5_NT_PRINCIPAL_NAME + return (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME; +#else + return GSS_C_NT_USER_NAME; +#endif + } + return NULL; +} + +int zmq::gssapi_mechanism_base_t::acquire_credentials (char *service_name_, + gss_cred_id_t *cred_, + gss_OID name_type_) +{ + OM_uint32 maj_stat; + OM_uint32 min_stat; + gss_name_t server_name; + + gss_buffer_desc name_buf; + name_buf.value = service_name_; + name_buf.length = strlen ((char *) name_buf.value) + 1; + + maj_stat = gss_import_name (&min_stat, &name_buf, name_type_, &server_name); + + if (maj_stat != GSS_S_COMPLETE) + return -1; + + maj_stat = gss_acquire_cred (&min_stat, server_name, 0, GSS_C_NO_OID_SET, + GSS_C_BOTH, cred_, NULL, NULL); + + if (maj_stat != GSS_S_COMPLETE) + return -1; + + gss_release_name (&min_stat, &server_name); + + return 0; +} + +#endif diff --git a/3rd/libzmq/src/gssapi_mechanism_base.hpp b/3rd/libzmq/src/gssapi_mechanism_base.hpp new file mode 100644 index 00000000..dec53d0b --- /dev/null +++ b/3rd/libzmq/src/gssapi_mechanism_base.hpp @@ -0,0 +1,132 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_GSSAPI_MECHANISM_BASE_HPP_INCLUDED__ +#define __ZMQ_GSSAPI_MECHANISM_BASE_HPP_INCLUDED__ + +#ifdef HAVE_LIBGSSAPI_KRB5 + +#if HAVE_GSSAPI_GSSAPI_GENERIC_H +#include +#endif +#include + +#include "mechanism_base.hpp" +#include "options.hpp" + +namespace zmq +{ +class msg_t; + +/// Commonalities between clients and servers are captured here. +/// For example, clients and servers both need to produce and +/// process context-level GSSAPI tokens (via INITIATE commands) +/// and per-message GSSAPI tokens (via MESSAGE commands). +class gssapi_mechanism_base_t : public virtual mechanism_base_t +{ + public: + gssapi_mechanism_base_t (session_base_t *session_, + const options_t &options_); + ~gssapi_mechanism_base_t () ZMQ_OVERRIDE = 0; + + protected: + // Produce a context-level GSSAPI token (INITIATE command) + // during security context initialization. + int produce_initiate (msg_t *msg_, void *data_, size_t data_len_); + + // Process a context-level GSSAPI token (INITIATE command) + // during security context initialization. + int process_initiate (msg_t *msg_, void **data_, size_t &data_len_); + + // Produce a metadata ready msg (READY) to conclude handshake + int produce_ready (msg_t *msg_); + + // Process a metadata ready msg (READY) + int process_ready (msg_t *msg_); + + // Encode a per-message GSSAPI token (MESSAGE command) using + // the established security context. + int encode_message (msg_t *msg_); + + // Decode a per-message GSSAPI token (MESSAGE command) using + // the established security context. + int decode_message (msg_t *msg_); + + // Convert ZMQ_GSSAPI_NT values to GSSAPI name_type + static const gss_OID convert_nametype (int zmq_name_type_); + + // Acquire security context credentials from the + // underlying mechanism. + static int acquire_credentials (char *principal_name_, + gss_cred_id_t *cred_, + gss_OID name_type_); + + protected: + // Opaque GSSAPI token for outgoing data + gss_buffer_desc send_tok; + + // Opaque GSSAPI token for incoming data + gss_buffer_desc recv_tok; + + // Opaque GSSAPI representation of principal + gss_name_t target_name; + + // Human-readable principal name + char *principal_name; + + // Status code returned by GSSAPI functions + OM_uint32 maj_stat; + + // Status code returned by the underlying mechanism + OM_uint32 min_stat; + + // Status code returned by the underlying mechanism + // during context initialization + OM_uint32 init_sec_min_stat; + + // Flags returned by GSSAPI (ignored) + OM_uint32 ret_flags; + + // Flags returned by GSSAPI (ignored) + OM_uint32 gss_flags; + + // Credentials used to establish security context + gss_cred_id_t cred; + + // Opaque GSSAPI representation of the security context + gss_ctx_id_t context; + + // If true, use gss to encrypt messages. If false, only utilize gss for auth. + bool do_encryption; +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/gssapi_server.cpp b/3rd/libzmq/src/gssapi_server.cpp new file mode 100644 index 00000000..0a43349a --- /dev/null +++ b/3rd/libzmq/src/gssapi_server.cpp @@ -0,0 +1,248 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#ifdef HAVE_LIBGSSAPI_KRB5 + +#include +#include + +#include "msg.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "gssapi_server.hpp" +#include "wire.hpp" + +#include + +zmq::gssapi_server_t::gssapi_server_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_) : + mechanism_base_t (session_, options_), + gssapi_mechanism_base_t (session_, options_), + zap_client_t (session_, peer_address_, options_), + session (session_), + peer_address (peer_address_), + state (recv_next_token), + security_context_established (false) +{ + maj_stat = GSS_S_CONTINUE_NEEDED; + if (!options_.gss_principal.empty ()) { + const std::string::size_type principal_size = + options_.gss_principal.size (); + principal_name = static_cast (malloc (principal_size + 1)); + assert (principal_name); + memcpy (principal_name, options_.gss_principal.c_str (), + principal_size + 1); + gss_OID name_type = convert_nametype (options_.gss_principal_nt); + if (acquire_credentials (principal_name, &cred, name_type) != 0) + maj_stat = GSS_S_FAILURE; + } +} + +zmq::gssapi_server_t::~gssapi_server_t () +{ + if (cred) + gss_release_cred (&min_stat, &cred); + + if (target_name) + gss_release_name (&min_stat, &target_name); +} + +int zmq::gssapi_server_t::next_handshake_command (msg_t *msg_) +{ + if (state == send_ready) { + int rc = produce_ready (msg_); + if (rc == 0) + state = recv_ready; + + return rc; + } + + if (state != send_next_token) { + errno = EAGAIN; + return -1; + } + + if (produce_next_token (msg_) < 0) + return -1; + + if (maj_stat != GSS_S_CONTINUE_NEEDED && maj_stat != GSS_S_COMPLETE) + return -1; + + if (maj_stat == GSS_S_COMPLETE) { + security_context_established = true; + } + + state = recv_next_token; + + return 0; +} + +int zmq::gssapi_server_t::process_handshake_command (msg_t *msg_) +{ + if (state == recv_ready) { + int rc = process_ready (msg_); + if (rc == 0) + state = connected; + + return rc; + } + + if (state != recv_next_token) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + + if (security_context_established) { + // Use ZAP protocol (RFC 27) to authenticate the user. + // Note that rc will be -1 only if ZAP is not set up, but if it was + // requested and it does not work properly the program will abort. + bool expecting_zap_reply = false; + int rc = session->zap_connect (); + if (rc == 0) { + send_zap_request (); + rc = receive_and_process_zap_reply (); + if (rc != 0) { + if (rc == -1) + return -1; + expecting_zap_reply = true; + } + } + state = expecting_zap_reply ? expect_zap_reply : send_ready; + return 0; + } + + if (process_next_token (msg_) < 0) + return -1; + + accept_context (); + state = send_next_token; + + errno_assert (msg_->close () == 0); + errno_assert (msg_->init () == 0); + + return 0; +} + +void zmq::gssapi_server_t::send_zap_request () +{ + gss_buffer_desc principal; + gss_display_name (&min_stat, target_name, &principal, NULL); + zap_client_t::send_zap_request ( + "GSSAPI", 6, reinterpret_cast (principal.value), + principal.length); + + gss_release_buffer (&min_stat, &principal); +} + +int zmq::gssapi_server_t::encode (msg_t *msg_) +{ + zmq_assert (state == connected); + + if (do_encryption) + return encode_message (msg_); + + return 0; +} + +int zmq::gssapi_server_t::decode (msg_t *msg_) +{ + zmq_assert (state == connected); + + if (do_encryption) + return decode_message (msg_); + + return 0; +} + +int zmq::gssapi_server_t::zap_msg_available () +{ + if (state != expect_zap_reply) { + errno = EFSM; + return -1; + } + const int rc = receive_and_process_zap_reply (); + if (rc == 0) + state = send_ready; + return rc == -1 ? -1 : 0; +} + +zmq::mechanism_t::status_t zmq::gssapi_server_t::status () const +{ + return state == connected ? mechanism_t::ready : mechanism_t::handshaking; +} + +int zmq::gssapi_server_t::produce_next_token (msg_t *msg_) +{ + if (send_tok.length != 0) { // Client expects another token + if (produce_initiate (msg_, send_tok.value, send_tok.length) < 0) + return -1; + gss_release_buffer (&min_stat, &send_tok); + } + + if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { + gss_release_name (&min_stat, &target_name); + if (context != GSS_C_NO_CONTEXT) + gss_delete_sec_context (&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } + + return 0; +} + +int zmq::gssapi_server_t::process_next_token (msg_t *msg_) +{ + if (maj_stat == GSS_S_CONTINUE_NEEDED) { + if (process_initiate (msg_, &recv_tok.value, recv_tok.length) < 0) { + if (target_name != GSS_C_NO_NAME) + gss_release_name (&min_stat, &target_name); + return -1; + } + } + + return 0; +} + +void zmq::gssapi_server_t::accept_context () +{ + maj_stat = gss_accept_sec_context ( + &init_sec_min_stat, &context, cred, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, + &target_name, &doid, &send_tok, &ret_flags, NULL, NULL); + + if (recv_tok.value) { + free (recv_tok.value); + recv_tok.value = NULL; + } +} + +#endif diff --git a/3rd/libzmq/src/gssapi_server.hpp b/3rd/libzmq/src/gssapi_server.hpp new file mode 100644 index 00000000..26130115 --- /dev/null +++ b/3rd/libzmq/src/gssapi_server.hpp @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_GSSAPI_SERVER_HPP_INCLUDED__ +#define __ZMQ_GSSAPI_SERVER_HPP_INCLUDED__ + +#ifdef HAVE_LIBGSSAPI_KRB5 + +#include "gssapi_mechanism_base.hpp" +#include "zap_client.hpp" + +namespace zmq +{ +class msg_t; +class session_base_t; + +class gssapi_server_t ZMQ_FINAL : public gssapi_mechanism_base_t, + public zap_client_t +{ + public: + gssapi_server_t (session_base_t *session_, + const std::string &peer_address, + const options_t &options_); + ~gssapi_server_t () ZMQ_FINAL; + + // mechanism implementation + int next_handshake_command (msg_t *msg_) ZMQ_FINAL; + int process_handshake_command (msg_t *msg_) ZMQ_FINAL; + int encode (msg_t *msg_) ZMQ_FINAL; + int decode (msg_t *msg_) ZMQ_FINAL; + int zap_msg_available () ZMQ_FINAL; + status_t status () const ZMQ_FINAL; + + private: + enum state_t + { + send_next_token, + recv_next_token, + expect_zap_reply, + send_ready, + recv_ready, + connected + }; + + session_base_t *const session; + + const std::string peer_address; + + // Current FSM state + state_t state; + + // True iff server considers the client authenticated + bool security_context_established; + + // The underlying mechanism type (ignored) + gss_OID doid; + + void accept_context (); + int produce_next_token (msg_t *msg_); + int process_next_token (msg_t *msg_); + void send_zap_request (); +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/i_decoder.hpp b/3rd/libzmq/src/i_decoder.hpp new file mode 100644 index 00000000..e0bcf074 --- /dev/null +++ b/3rd/libzmq/src/i_decoder.hpp @@ -0,0 +1,61 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_I_DECODER_HPP_INCLUDED__ +#define __ZMQ_I_DECODER_HPP_INCLUDED__ + +#include "macros.hpp" +#include "stdint.hpp" + +namespace zmq +{ +class msg_t; + +// Interface to be implemented by message decoder. + +class i_decoder +{ + public: + virtual ~i_decoder () ZMQ_DEFAULT; + + virtual void get_buffer (unsigned char **data_, size_t *size_) = 0; + + virtual void resize_buffer (size_t) = 0; + // Decodes data pointed to by data_. + // When a message is decoded, 1 is returned. + // When the decoder needs more data, 0 is returned. + // On error, -1 is returned and errno is set accordingly. + virtual int + decode (const unsigned char *data_, size_t size_, size_t &processed_) = 0; + + virtual msg_t *msg () = 0; +}; +} + +#endif diff --git a/3rd/libzmq/src/i_encoder.hpp b/3rd/libzmq/src/i_encoder.hpp new file mode 100644 index 00000000..33ef9dd8 --- /dev/null +++ b/3rd/libzmq/src/i_encoder.hpp @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_I_ENCODER_HPP_INCLUDED__ +#define __ZMQ_I_ENCODER_HPP_INCLUDED__ + +#include "macros.hpp" +#include "stdint.hpp" + +namespace zmq +{ +// Forward declaration +class msg_t; + +// Interface to be implemented by message encoder. + +struct i_encoder +{ + virtual ~i_encoder () ZMQ_DEFAULT; + + // The function returns a batch of binary data. The data + // are filled to a supplied buffer. If no buffer is supplied (data_ + // is NULL) encoder will provide buffer of its own. + // Function returns 0 when a new message is required. + virtual size_t encode (unsigned char **data_, size_t size_) = 0; + + // Load a new message into encoder. + virtual void load_msg (msg_t *msg_) = 0; +}; +} + +#endif diff --git a/3rd/libzmq/src/i_engine.hpp b/3rd/libzmq/src/i_engine.hpp new file mode 100644 index 00000000..7d05df8a --- /dev/null +++ b/3rd/libzmq/src/i_engine.hpp @@ -0,0 +1,82 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_I_ENGINE_HPP_INCLUDED__ +#define __ZMQ_I_ENGINE_HPP_INCLUDED__ + +#include "endpoint.hpp" +#include "macros.hpp" + +namespace zmq +{ +class io_thread_t; + +// Abstract interface to be implemented by various engines. + +struct i_engine +{ + enum error_reason_t + { + protocol_error, + connection_error, + timeout_error + }; + + virtual ~i_engine () ZMQ_DEFAULT; + + // Indicate if the engine has an handshake stage. + // If engine has handshake stage, engine must call session.engine_ready when the handshake is complete. + virtual bool has_handshake_stage () = 0; + + // Plug the engine to the session. + virtual void plug (zmq::io_thread_t *io_thread_, + class session_base_t *session_) = 0; + + // Terminate and deallocate the engine. Note that 'detached' + // events are not fired on termination. + virtual void terminate () = 0; + + // This method is called by the session to signalise that more + // messages can be written to the pipe. + // Returns false if the engine was deleted due to an error. + // TODO it is probably better to change the design such that the engine + // does not delete itself + virtual bool restart_input () = 0; + + // This method is called by the session to signalise that there + // are messages to send available. + virtual void restart_output () = 0; + + virtual void zap_msg_available () = 0; + + virtual const endpoint_uri_pair_t &get_endpoint () const = 0; +}; +} + +#endif diff --git a/3rd/libzmq/src/i_mailbox.hpp b/3rd/libzmq/src/i_mailbox.hpp new file mode 100644 index 00000000..7402a219 --- /dev/null +++ b/3rd/libzmq/src/i_mailbox.hpp @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_I_MAILBOX_HPP_INCLUDED__ +#define __ZMQ_I_MAILBOX_HPP_INCLUDED__ + +#include "macros.hpp" +#include "stdint.hpp" + +namespace zmq +{ +// Interface to be implemented by mailbox. + +class i_mailbox +{ + public: + virtual ~i_mailbox () ZMQ_DEFAULT; + + virtual void send (const command_t &cmd_) = 0; + virtual int recv (command_t *cmd_, int timeout_) = 0; + + +#ifdef HAVE_FORK + // close the file descriptors in the signaller. This is used in a forked + // child process to close the file descriptors so that they do not interfere + // with the context in the parent process. + virtual void forked () = 0; +#endif +}; +} + +#endif diff --git a/3rd/libzmq/src/i_poll_events.hpp b/3rd/libzmq/src/i_poll_events.hpp new file mode 100644 index 00000000..5e70162c --- /dev/null +++ b/3rd/libzmq/src/i_poll_events.hpp @@ -0,0 +1,55 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_I_POLL_EVENTS_HPP_INCLUDED__ +#define __ZMQ_I_POLL_EVENTS_HPP_INCLUDED__ + +#include "macros.hpp" + +namespace zmq +{ +// Virtual interface to be exposed by object that want to be notified +// about events on file descriptors. + +struct i_poll_events +{ + virtual ~i_poll_events () ZMQ_DEFAULT; + + // Called by I/O thread when file descriptor is ready for reading. + virtual void in_event () = 0; + + // Called by I/O thread when file descriptor is ready for writing. + virtual void out_event () = 0; + + // Called when timer expires. + virtual void timer_event (int id_) = 0; +}; +} + +#endif diff --git a/3rd/libzmq/src/io_object.cpp b/3rd/libzmq/src/io_object.cpp new file mode 100644 index 00000000..aa466c08 --- /dev/null +++ b/3rd/libzmq/src/io_object.cpp @@ -0,0 +1,116 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "io_object.hpp" +#include "io_thread.hpp" +#include "err.hpp" + +zmq::io_object_t::io_object_t (io_thread_t *io_thread_) : _poller (NULL) +{ + if (io_thread_) + plug (io_thread_); +} + +zmq::io_object_t::~io_object_t () +{ +} + +void zmq::io_object_t::plug (io_thread_t *io_thread_) +{ + zmq_assert (io_thread_); + zmq_assert (!_poller); + + // Retrieve the poller from the thread we are running in. + _poller = io_thread_->get_poller (); +} + +void zmq::io_object_t::unplug () +{ + zmq_assert (_poller); + + // Forget about old poller in preparation to be migrated + // to a different I/O thread. + _poller = NULL; +} + +zmq::io_object_t::handle_t zmq::io_object_t::add_fd (fd_t fd_) +{ + return _poller->add_fd (fd_, this); +} + +void zmq::io_object_t::rm_fd (handle_t handle_) +{ + _poller->rm_fd (handle_); +} + +void zmq::io_object_t::set_pollin (handle_t handle_) +{ + _poller->set_pollin (handle_); +} + +void zmq::io_object_t::reset_pollin (handle_t handle_) +{ + _poller->reset_pollin (handle_); +} + +void zmq::io_object_t::set_pollout (handle_t handle_) +{ + _poller->set_pollout (handle_); +} + +void zmq::io_object_t::reset_pollout (handle_t handle_) +{ + _poller->reset_pollout (handle_); +} + +void zmq::io_object_t::add_timer (int timeout_, int id_) +{ + _poller->add_timer (timeout_, this, id_); +} + +void zmq::io_object_t::cancel_timer (int id_) +{ + _poller->cancel_timer (this, id_); +} + +void zmq::io_object_t::in_event () +{ + zmq_assert (false); +} + +void zmq::io_object_t::out_event () +{ + zmq_assert (false); +} + +void zmq::io_object_t::timer_event (int) +{ + zmq_assert (false); +} diff --git a/3rd/libzmq/src/io_object.hpp b/3rd/libzmq/src/io_object.hpp new file mode 100644 index 00000000..703af7ef --- /dev/null +++ b/3rd/libzmq/src/io_object.hpp @@ -0,0 +1,83 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_IO_OBJECT_HPP_INCLUDED__ +#define __ZMQ_IO_OBJECT_HPP_INCLUDED__ + +#include + +#include "stdint.hpp" +#include "poller.hpp" +#include "i_poll_events.hpp" + +namespace zmq +{ +class io_thread_t; + +// Simple base class for objects that live in I/O threads. +// It makes communication with the poller object easier and +// makes defining unneeded event handlers unnecessary. + +class io_object_t : public i_poll_events +{ + public: + io_object_t (zmq::io_thread_t *io_thread_ = NULL); + ~io_object_t () ZMQ_OVERRIDE; + + // When migrating an object from one I/O thread to another, first + // unplug it, then migrate it, then plug it to the new thread. + void plug (zmq::io_thread_t *io_thread_); + void unplug (); + + protected: + typedef poller_t::handle_t handle_t; + + // Methods to access underlying poller object. + handle_t add_fd (fd_t fd_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void add_timer (int timeout_, int id_); + void cancel_timer (int id_); + + // i_poll_events interface implementation. + void in_event () ZMQ_OVERRIDE; + void out_event () ZMQ_OVERRIDE; + void timer_event (int id_) ZMQ_OVERRIDE; + + private: + poller_t *_poller; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (io_object_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/io_thread.cpp b/3rd/libzmq/src/io_thread.cpp new file mode 100644 index 00000000..a2ca950a --- /dev/null +++ b/3rd/libzmq/src/io_thread.cpp @@ -0,0 +1,121 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include + +#include "macros.hpp" +#include "io_thread.hpp" +#include "err.hpp" +#include "ctx.hpp" + +zmq::io_thread_t::io_thread_t (ctx_t *ctx_, uint32_t tid_) : + object_t (ctx_, tid_), + _mailbox_handle (static_cast (NULL)) +{ + _poller = new (std::nothrow) poller_t (*ctx_); + alloc_assert (_poller); + + if (_mailbox.get_fd () != retired_fd) { + _mailbox_handle = _poller->add_fd (_mailbox.get_fd (), this); + _poller->set_pollin (_mailbox_handle); + } +} + +zmq::io_thread_t::~io_thread_t () +{ + LIBZMQ_DELETE (_poller); +} + +void zmq::io_thread_t::start () +{ + char name[16] = ""; + snprintf (name, sizeof (name), "IO/%u", + get_tid () - zmq::ctx_t::reaper_tid - 1); + // Start the underlying I/O thread. + _poller->start (name); +} + +void zmq::io_thread_t::stop () +{ + send_stop (); +} + +zmq::mailbox_t *zmq::io_thread_t::get_mailbox () +{ + return &_mailbox; +} + +int zmq::io_thread_t::get_load () const +{ + return _poller->get_load (); +} + +void zmq::io_thread_t::in_event () +{ + // TODO: Do we want to limit number of commands I/O thread can + // process in a single go? + + command_t cmd; + int rc = _mailbox.recv (&cmd, 0); + + while (rc == 0 || errno == EINTR) { + if (rc == 0) + cmd.destination->process_command (cmd); + rc = _mailbox.recv (&cmd, 0); + } + + errno_assert (rc != 0 && errno == EAGAIN); +} + +void zmq::io_thread_t::out_event () +{ + // We are never polling for POLLOUT here. This function is never called. + zmq_assert (false); +} + +void zmq::io_thread_t::timer_event (int) +{ + // No timers here. This function is never called. + zmq_assert (false); +} + +zmq::poller_t *zmq::io_thread_t::get_poller () const +{ + zmq_assert (_poller); + return _poller; +} + +void zmq::io_thread_t::process_stop () +{ + zmq_assert (_mailbox_handle); + _poller->rm_fd (_mailbox_handle); + _poller->stop (); +} diff --git a/3rd/libzmq/src/io_thread.hpp b/3rd/libzmq/src/io_thread.hpp new file mode 100644 index 00000000..65e276d9 --- /dev/null +++ b/3rd/libzmq/src/io_thread.hpp @@ -0,0 +1,92 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_IO_THREAD_HPP_INCLUDED__ +#define __ZMQ_IO_THREAD_HPP_INCLUDED__ + +#include "stdint.hpp" +#include "object.hpp" +#include "poller.hpp" +#include "i_poll_events.hpp" +#include "mailbox.hpp" + +namespace zmq +{ +class ctx_t; + +// Generic part of the I/O thread. Polling-mechanism-specific features +// are implemented in separate "polling objects". + +class io_thread_t ZMQ_FINAL : public object_t, public i_poll_events +{ + public: + io_thread_t (zmq::ctx_t *ctx_, uint32_t tid_); + + // Clean-up. If the thread was started, it's necessary to call 'stop' + // before invoking destructor. Otherwise the destructor would hang up. + ~io_thread_t (); + + // Launch the physical thread. + void start (); + + // Ask underlying thread to stop. + void stop (); + + // Returns mailbox associated with this I/O thread. + mailbox_t *get_mailbox (); + + // i_poll_events implementation. + void in_event (); + void out_event (); + void timer_event (int id_); + + // Used by io_objects to retrieve the associated poller object. + poller_t *get_poller () const; + + // Command handlers. + void process_stop (); + + // Returns load experienced by the I/O thread. + int get_load () const; + + private: + // I/O thread accesses incoming commands via this mailbox. + mailbox_t _mailbox; + + // Handle associated with mailbox' file descriptor. + poller_t::handle_t _mailbox_handle; + + // I/O multiplexing is performed using a poller object. + poller_t *_poller; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (io_thread_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/ip.cpp b/3rd/libzmq/src/ip.cpp new file mode 100644 index 00000000..d150da76 --- /dev/null +++ b/3rd/libzmq/src/ip.cpp @@ -0,0 +1,945 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "ip.hpp" +#include "err.hpp" +#include "macros.hpp" +#include "config.hpp" +#include "address.hpp" + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#else +#include "tcp.hpp" +#ifdef ZMQ_HAVE_IPC +#include "ipc_address.hpp" +#endif + +#include + +#define rmdir _rmdir +#define unlink _unlink +#endif + +#if defined ZMQ_HAVE_OPENVMS || defined ZMQ_HAVE_VXWORKS +#include +#endif + +#if defined ZMQ_HAVE_VXWORKS +#include +#include +#include +#endif + +#if defined ZMQ_HAVE_EVENTFD +#include +#endif + +#if defined ZMQ_HAVE_OPENPGM +#ifdef ZMQ_HAVE_WINDOWS +#define __PGM_WININT_H__ +#endif + +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#ifndef ZMQ_HAVE_WINDOWS +// Acceptable temporary directory environment variables +static const char *tmp_env_vars[] = { + "TMPDIR", "TEMPDIR", "TMP", + 0 // Sentinel +}; +#endif + +zmq::fd_t zmq::open_socket (int domain_, int type_, int protocol_) +{ + int rc; + + // Setting this option result in sane behaviour when exec() functions + // are used. Old sockets are closed and don't block TCP ports etc. +#if defined ZMQ_HAVE_SOCK_CLOEXEC + type_ |= SOCK_CLOEXEC; +#endif + +#if defined ZMQ_HAVE_WINDOWS && defined WSA_FLAG_NO_HANDLE_INHERIT + // if supported, create socket with WSA_FLAG_NO_HANDLE_INHERIT, such that + // the race condition in making it non-inheritable later is avoided + const fd_t s = WSASocket (domain_, type_, protocol_, NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); +#else + const fd_t s = socket (domain_, type_, protocol_); +#endif + if (s == retired_fd) { +#ifdef ZMQ_HAVE_WINDOWS + errno = wsa_error_to_errno (WSAGetLastError ()); +#endif + return retired_fd; + } + + make_socket_noninheritable (s); + + // Socket is not yet connected so EINVAL is not a valid networking error + rc = zmq::set_nosigpipe (s); + errno_assert (rc == 0); + + return s; +} + +void zmq::unblock_socket (fd_t s_) +{ +#if defined ZMQ_HAVE_WINDOWS + u_long nonblock = 1; + const int rc = ioctlsocket (s_, FIONBIO, &nonblock); + wsa_assert (rc != SOCKET_ERROR); +#elif defined ZMQ_HAVE_OPENVMS || defined ZMQ_HAVE_VXWORKS + int nonblock = 1; + int rc = ioctl (s_, FIONBIO, &nonblock); + errno_assert (rc != -1); +#else + int flags = fcntl (s_, F_GETFL, 0); + if (flags == -1) + flags = 0; + int rc = fcntl (s_, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); +#endif +} + +void zmq::enable_ipv4_mapping (fd_t s_) +{ + LIBZMQ_UNUSED (s_); + +#if defined IPV6_V6ONLY && !defined ZMQ_HAVE_OPENBSD \ + && !defined ZMQ_HAVE_DRAGONFLY +#ifdef ZMQ_HAVE_WINDOWS + DWORD flag = 0; +#else + int flag = 0; +#endif + const int rc = setsockopt (s_, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast (&flag), sizeof (flag)); +#ifdef ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + errno_assert (rc == 0); +#endif +#endif +} + +int zmq::get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_) +{ + struct sockaddr_storage ss; + + const zmq_socklen_t addrlen = + get_socket_address (sockfd_, socket_end_remote, &ss); + + if (addrlen == 0) { +#ifdef ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + wsa_assert (last_error != WSANOTINITIALISED && last_error != WSAEFAULT + && last_error != WSAEINPROGRESS + && last_error != WSAENOTSOCK); +#elif !defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE + errno_assert (errno != EBADF && errno != EFAULT && errno != ENOTSOCK); +#else + errno_assert (errno != EFAULT && errno != ENOTSOCK); +#endif + return 0; + } + + char host[NI_MAXHOST]; + const int rc = + getnameinfo (reinterpret_cast (&ss), addrlen, host, + sizeof host, NULL, 0, NI_NUMERICHOST); + if (rc != 0) + return 0; + + ip_addr_ = host; + + union + { + struct sockaddr sa; + struct sockaddr_storage sa_stor; + } u; + + u.sa_stor = ss; + return static_cast (u.sa.sa_family); +} + +void zmq::set_ip_type_of_service (fd_t s_, int iptos_) +{ + int rc = setsockopt (s_, IPPROTO_IP, IP_TOS, + reinterpret_cast (&iptos_), sizeof (iptos_)); + +#ifdef ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + errno_assert (rc == 0); +#endif + + // Windows and Hurd do not support IPV6_TCLASS +#if !defined(ZMQ_HAVE_WINDOWS) && defined(IPV6_TCLASS) + rc = setsockopt (s_, IPPROTO_IPV6, IPV6_TCLASS, + reinterpret_cast (&iptos_), sizeof (iptos_)); + + // If IPv6 is not enabled ENOPROTOOPT will be returned on Linux and + // EINVAL on OSX + if (rc == -1) { + errno_assert (errno == ENOPROTOOPT || errno == EINVAL); + } +#endif +} + +void zmq::set_socket_priority (fd_t s_, int priority_) +{ +#ifdef ZMQ_HAVE_SO_PRIORITY + int rc = + setsockopt (s_, SOL_SOCKET, SO_PRIORITY, + reinterpret_cast (&priority_), sizeof (priority_)); + errno_assert (rc == 0); +#endif +} + +int zmq::set_nosigpipe (fd_t s_) +{ +#ifdef SO_NOSIGPIPE + // Make sure that SIGPIPE signal is not generated when writing to a + // connection that was already closed by the peer. + // As per POSIX spec, EINVAL will be returned if the socket was valid but + // the connection has been reset by the peer. Return an error so that the + // socket can be closed and the connection retried if necessary. + int set = 1; + int rc = setsockopt (s_, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof (int)); + if (rc != 0 && errno == EINVAL) + return -1; + errno_assert (rc == 0); +#else + LIBZMQ_UNUSED (s_); +#endif + + return 0; +} + +int zmq::bind_to_device (fd_t s_, const std::string &bound_device_) +{ +#ifdef ZMQ_HAVE_SO_BINDTODEVICE + int rc = setsockopt (s_, SOL_SOCKET, SO_BINDTODEVICE, + bound_device_.c_str (), bound_device_.length ()); + if (rc != 0) { + assert_success_or_recoverable (s_, rc); + return -1; + } + return 0; + +#else + LIBZMQ_UNUSED (s_); + LIBZMQ_UNUSED (bound_device_); + + errno = ENOTSUP; + return -1; +#endif +} + +bool zmq::initialize_network () +{ +#if defined ZMQ_HAVE_OPENPGM + + // Init PGM transport. Ensure threading and timer are enabled. Find PGM + // protocol ID. Note that if you want to use gettimeofday and sleep for + // openPGM timing, set environment variables PGM_TIMER to "GTOD" and + // PGM_SLEEP to "USLEEP". + pgm_error_t *pgm_error = NULL; + const bool ok = pgm_init (&pgm_error); + if (ok != TRUE) { + // Invalid parameters don't set pgm_error_t + zmq_assert (pgm_error != NULL); + if (pgm_error->domain == PGM_ERROR_DOMAIN_TIME + && (pgm_error->code == PGM_ERROR_FAILED)) { + // Failed to access RTC or HPET device. + pgm_error_free (pgm_error); + errno = EINVAL; + return false; + } + + // PGM_ERROR_DOMAIN_ENGINE: WSAStartup errors or missing WSARecvMsg. + zmq_assert (false); + } +#endif + +#ifdef ZMQ_HAVE_WINDOWS + // Intialise Windows sockets. Note that WSAStartup can be called multiple + // times given that WSACleanup will be called for each WSAStartup. + + const WORD version_requested = MAKEWORD (2, 2); + WSADATA wsa_data; + const int rc = WSAStartup (version_requested, &wsa_data); + zmq_assert (rc == 0); + zmq_assert (LOBYTE (wsa_data.wVersion) == 2 + && HIBYTE (wsa_data.wVersion) == 2); +#endif + + return true; +} + +void zmq::shutdown_network () +{ +#ifdef ZMQ_HAVE_WINDOWS + // On Windows, uninitialise socket layer. + const int rc = WSACleanup (); + wsa_assert (rc != SOCKET_ERROR); +#endif + +#if defined ZMQ_HAVE_OPENPGM + // Shut down the OpenPGM library. + if (pgm_shutdown () != TRUE) + zmq_assert (false); +#endif +} + +#if defined ZMQ_HAVE_WINDOWS +static void tune_socket (const SOCKET socket_) +{ + BOOL tcp_nodelay = 1; + const int rc = + setsockopt (socket_, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast (&tcp_nodelay), sizeof tcp_nodelay); + wsa_assert (rc != SOCKET_ERROR); + + zmq::tcp_tune_loopback_fast_path (socket_); +} + +static int make_fdpair_tcpip (zmq::fd_t *r_, zmq::fd_t *w_) +{ +#if !defined _WIN32_WCE && !defined ZMQ_HAVE_WINDOWS_UWP + // Windows CE does not manage security attributes + SECURITY_DESCRIPTOR sd; + SECURITY_ATTRIBUTES sa; + memset (&sd, 0, sizeof sd); + memset (&sa, 0, sizeof sa); + + InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE); + + sa.nLength = sizeof (SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = &sd; +#endif + + // This function has to be in a system-wide critical section so that + // two instances of the library don't accidentally create signaler + // crossing the process boundary. + // We'll use named event object to implement the critical section. + // Note that if the event object already exists, the CreateEvent requests + // EVENT_ALL_ACCESS access right. If this fails, we try to open + // the event object asking for SYNCHRONIZE access only. + HANDLE sync = NULL; + + // Create critical section only if using fixed signaler port + // Use problematic Event implementation for compatibility if using old port 5905. + // Otherwise use Mutex implementation. + const int event_signaler_port = 5905; + + if (zmq::signaler_port == event_signaler_port) { +#if !defined _WIN32_WCE && !defined ZMQ_HAVE_WINDOWS_UWP + sync = + CreateEventW (&sa, FALSE, TRUE, L"Global\\zmq-signaler-port-sync"); +#else + sync = + CreateEventW (NULL, FALSE, TRUE, L"Global\\zmq-signaler-port-sync"); +#endif + if (sync == NULL && GetLastError () == ERROR_ACCESS_DENIED) + sync = OpenEventW (SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, + L"Global\\zmq-signaler-port-sync"); + + win_assert (sync != NULL); + } else if (zmq::signaler_port != 0) { + wchar_t mutex_name[MAX_PATH]; +#ifdef __MINGW32__ + _snwprintf (mutex_name, MAX_PATH, L"Global\\zmq-signaler-port-%d", + zmq::signaler_port); +#else + swprintf (mutex_name, MAX_PATH, L"Global\\zmq-signaler-port-%d", + zmq::signaler_port); +#endif + +#if !defined _WIN32_WCE && !defined ZMQ_HAVE_WINDOWS_UWP + sync = CreateMutexW (&sa, FALSE, mutex_name); +#else + sync = CreateMutexW (NULL, FALSE, mutex_name); +#endif + if (sync == NULL && GetLastError () == ERROR_ACCESS_DENIED) + sync = OpenMutexW (SYNCHRONIZE, FALSE, mutex_name); + + win_assert (sync != NULL); + } + + // Windows has no 'socketpair' function. CreatePipe is no good as pipe + // handles cannot be polled on. Here we create the socketpair by hand. + *w_ = INVALID_SOCKET; + *r_ = INVALID_SOCKET; + + // Create listening socket. + SOCKET listener; + listener = zmq::open_socket (AF_INET, SOCK_STREAM, 0); + wsa_assert (listener != INVALID_SOCKET); + + // Set SO_REUSEADDR and TCP_NODELAY on listening socket. + BOOL so_reuseaddr = 1; + int rc = setsockopt (listener, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast (&so_reuseaddr), + sizeof so_reuseaddr); + wsa_assert (rc != SOCKET_ERROR); + + tune_socket (listener); + + // Init sockaddr to signaler port. + struct sockaddr_in addr; + memset (&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + addr.sin_port = htons (zmq::signaler_port); + + // Create the writer socket. + *w_ = zmq::open_socket (AF_INET, SOCK_STREAM, 0); + wsa_assert (*w_ != INVALID_SOCKET); + + if (sync != NULL) { + // Enter the critical section. + const DWORD dwrc = WaitForSingleObject (sync, INFINITE); + zmq_assert (dwrc == WAIT_OBJECT_0 || dwrc == WAIT_ABANDONED); + } + + // Bind listening socket to signaler port. + rc = bind (listener, reinterpret_cast (&addr), + sizeof addr); + + if (rc != SOCKET_ERROR && zmq::signaler_port == 0) { + // Retrieve ephemeral port number + int addrlen = sizeof addr; + rc = getsockname (listener, reinterpret_cast (&addr), + &addrlen); + } + + // Listen for incoming connections. + if (rc != SOCKET_ERROR) { + rc = listen (listener, 1); + } + + // Connect writer to the listener. + if (rc != SOCKET_ERROR) { + rc = connect (*w_, reinterpret_cast (&addr), + sizeof addr); + } + + // Accept connection from writer. + if (rc != SOCKET_ERROR) { + // Set TCP_NODELAY on writer socket. + tune_socket (*w_); + + *r_ = accept (listener, NULL, NULL); + } + + // Send/receive large chunk to work around TCP slow start + // This code is a workaround for #1608 + if (*r_ != INVALID_SOCKET) { + const size_t dummy_size = + 1024 * 1024; // 1M to overload default receive buffer + unsigned char *dummy = + static_cast (malloc (dummy_size)); + wsa_assert (dummy); + + int still_to_send = static_cast (dummy_size); + int still_to_recv = static_cast (dummy_size); + while (still_to_send || still_to_recv) { + int nbytes; + if (still_to_send > 0) { + nbytes = ::send ( + *w_, + reinterpret_cast (dummy + dummy_size - still_to_send), + still_to_send, 0); + wsa_assert (nbytes != SOCKET_ERROR); + still_to_send -= nbytes; + } + nbytes = ::recv ( + *r_, + reinterpret_cast (dummy + dummy_size - still_to_recv), + still_to_recv, 0); + wsa_assert (nbytes != SOCKET_ERROR); + still_to_recv -= nbytes; + } + free (dummy); + } + + // Save errno if error occurred in bind/listen/connect/accept. + int saved_errno = 0; + if (*r_ == INVALID_SOCKET) + saved_errno = WSAGetLastError (); + + // We don't need the listening socket anymore. Close it. + rc = closesocket (listener); + wsa_assert (rc != SOCKET_ERROR); + + if (sync != NULL) { + // Exit the critical section. + BOOL brc; + if (zmq::signaler_port == event_signaler_port) + brc = SetEvent (sync); + else + brc = ReleaseMutex (sync); + win_assert (brc != 0); + + // Release the kernel object + brc = CloseHandle (sync); + win_assert (brc != 0); + } + + if (*r_ != INVALID_SOCKET) { + zmq::make_socket_noninheritable (*r_); + return 0; + } + // Cleanup writer if connection failed + if (*w_ != INVALID_SOCKET) { + rc = closesocket (*w_); + wsa_assert (rc != SOCKET_ERROR); + *w_ = INVALID_SOCKET; + } + // Set errno from saved value + errno = zmq::wsa_error_to_errno (saved_errno); + return -1; +} +#endif + +int zmq::make_fdpair (fd_t *r_, fd_t *w_) +{ +#if defined ZMQ_HAVE_EVENTFD + int flags = 0; +#if defined ZMQ_HAVE_EVENTFD_CLOEXEC + // Setting this option result in sane behaviour when exec() functions + // are used. Old sockets are closed and don't block TCP ports, avoid + // leaks, etc. + flags |= EFD_CLOEXEC; +#endif + fd_t fd = eventfd (0, flags); + if (fd == -1) { + errno_assert (errno == ENFILE || errno == EMFILE); + *w_ = *r_ = -1; + return -1; + } + *w_ = *r_ = fd; + return 0; + + +#elif defined ZMQ_HAVE_WINDOWS +#ifdef ZMQ_HAVE_IPC + ipc_address_t address; + std::string dirname, filename; + sockaddr_un lcladdr; + socklen_t lcladdr_len = sizeof lcladdr; + int rc = 0; + int saved_errno = 0; + + // Create a listening socket. + const SOCKET listener = open_socket (AF_UNIX, SOCK_STREAM, 0); + if (listener == retired_fd) { + // This may happen if the library was built on a system supporting AF_UNIX, but the system running doesn't support it. + goto try_tcpip; + } + + create_ipc_wildcard_address (dirname, filename); + + // Initialise the address structure. + rc = address.resolve (filename.c_str ()); + if (rc != 0) { + goto error_closelistener; + } + + // Bind the socket to the file path. + rc = bind (listener, const_cast (address.addr ()), + address.addrlen ()); + if (rc != 0) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error_closelistener; + } + + // Listen for incoming connections. + rc = listen (listener, 1); + if (rc != 0) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error_closelistener; + } + + rc = getsockname (listener, reinterpret_cast (&lcladdr), + &lcladdr_len); + wsa_assert (rc != -1); + + // Create the client socket. + *w_ = open_socket (AF_UNIX, SOCK_STREAM, 0); + if (*w_ == -1) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error_closelistener; + } + + // Connect to the remote peer. + rc = ::connect (*w_, reinterpret_cast (&lcladdr), + lcladdr_len); + if (rc == -1) { + goto error_closeclient; + } + + *r_ = accept (listener, NULL, NULL); + errno_assert (*r_ != -1); + + // Close the listener socket, we don't need it anymore. + rc = closesocket (listener); + wsa_assert (rc == 0); + + // Cleanup temporary socket file descriptor + if (!filename.empty ()) { + rc = ::unlink (filename.c_str ()); + if ((rc == 0) && !dirname.empty ()) { + rc = ::rmdir (dirname.c_str ()); + dirname.clear (); + } + filename.clear (); + } + + return 0; + +error_closeclient: + saved_errno = errno; + rc = closesocket (*w_); + wsa_assert (rc == 0); + errno = saved_errno; + +error_closelistener: + saved_errno = errno; + rc = closesocket (listener); + wsa_assert (rc == 0); + + // Cleanup temporary socket file descriptor + if (!filename.empty ()) { + rc = ::unlink (filename.c_str ()); + if ((rc == 0) && !dirname.empty ()) { + rc = ::rmdir (dirname.c_str ()); + dirname.clear (); + } + filename.clear (); + } + + errno = saved_errno; + + return -1; + +try_tcpip: + // try to fallback to TCP/IP + // TODO: maybe remember this decision permanently? +#endif + + return make_fdpair_tcpip (r_, w_); +#elif defined ZMQ_HAVE_OPENVMS + + // Whilst OpenVMS supports socketpair - it maps to AF_INET only. Further, + // it does not set the socket options TCP_NODELAY and TCP_NODELACK which + // can lead to performance problems. + // + // The bug will be fixed in V5.6 ECO4 and beyond. In the meantime, we'll + // create the socket pair manually. + struct sockaddr_in lcladdr; + memset (&lcladdr, 0, sizeof lcladdr); + lcladdr.sin_family = AF_INET; + lcladdr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + lcladdr.sin_port = 0; + + int listener = open_socket (AF_INET, SOCK_STREAM, 0); + errno_assert (listener != -1); + + int on = 1; + int rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); + errno_assert (rc != -1); + + rc = setsockopt (listener, IPPROTO_TCP, TCP_NODELACK, &on, sizeof on); + errno_assert (rc != -1); + + rc = bind (listener, (struct sockaddr *) &lcladdr, sizeof lcladdr); + errno_assert (rc != -1); + + socklen_t lcladdr_len = sizeof lcladdr; + + rc = getsockname (listener, (struct sockaddr *) &lcladdr, &lcladdr_len); + errno_assert (rc != -1); + + rc = listen (listener, 1); + errno_assert (rc != -1); + + *w_ = open_socket (AF_INET, SOCK_STREAM, 0); + errno_assert (*w_ != -1); + + rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); + errno_assert (rc != -1); + + rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELACK, &on, sizeof on); + errno_assert (rc != -1); + + rc = connect (*w_, (struct sockaddr *) &lcladdr, sizeof lcladdr); + errno_assert (rc != -1); + + *r_ = accept (listener, NULL, NULL); + errno_assert (*r_ != -1); + + close (listener); + + return 0; +#elif defined ZMQ_HAVE_VXWORKS + struct sockaddr_in lcladdr; + memset (&lcladdr, 0, sizeof lcladdr); + lcladdr.sin_family = AF_INET; + lcladdr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + lcladdr.sin_port = 0; + + int listener = open_socket (AF_INET, SOCK_STREAM, 0); + errno_assert (listener != -1); + + int on = 1; + int rc = + setsockopt (listener, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof on); + errno_assert (rc != -1); + + rc = bind (listener, (struct sockaddr *) &lcladdr, sizeof lcladdr); + errno_assert (rc != -1); + + socklen_t lcladdr_len = sizeof lcladdr; + + rc = getsockname (listener, (struct sockaddr *) &lcladdr, + (int *) &lcladdr_len); + errno_assert (rc != -1); + + rc = listen (listener, 1); + errno_assert (rc != -1); + + *w_ = open_socket (AF_INET, SOCK_STREAM, 0); + errno_assert (*w_ != -1); + + rc = setsockopt (*w_, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof on); + errno_assert (rc != -1); + + rc = connect (*w_, (struct sockaddr *) &lcladdr, sizeof lcladdr); + errno_assert (rc != -1); + + *r_ = accept (listener, NULL, NULL); + errno_assert (*r_ != -1); + + close (listener); + + return 0; +#else + // All other implementations support socketpair() + int sv[2]; + int type = SOCK_STREAM; + // Setting this option result in sane behaviour when exec() functions + // are used. Old sockets are closed and don't block TCP ports, avoid + // leaks, etc. +#if defined ZMQ_HAVE_SOCK_CLOEXEC + type |= SOCK_CLOEXEC; +#endif + int rc = socketpair (AF_UNIX, type, 0, sv); + if (rc == -1) { + errno_assert (errno == ENFILE || errno == EMFILE); + *w_ = *r_ = -1; + return -1; + } else { + make_socket_noninheritable (sv[0]); + make_socket_noninheritable (sv[1]); + + *w_ = sv[0]; + *r_ = sv[1]; + return 0; + } +#endif +} + +void zmq::make_socket_noninheritable (fd_t sock_) +{ +#if defined ZMQ_HAVE_WINDOWS && !defined _WIN32_WCE \ + && !defined ZMQ_HAVE_WINDOWS_UWP + // On Windows, preventing sockets to be inherited by child processes. + const BOOL brc = SetHandleInformation (reinterpret_cast (sock_), + HANDLE_FLAG_INHERIT, 0); + win_assert (brc); +#elif (!defined ZMQ_HAVE_SOCK_CLOEXEC || !defined HAVE_ACCEPT4) \ + && defined FD_CLOEXEC + // If there 's no SOCK_CLOEXEC, let's try the second best option. + // Race condition can cause socket not to be closed (if fork happens + // between accept and this point). + const int rc = fcntl (sock_, F_SETFD, FD_CLOEXEC); + errno_assert (rc != -1); +#else + LIBZMQ_UNUSED (sock_); +#endif +} + +void zmq::assert_success_or_recoverable (zmq::fd_t s_, int rc_) +{ +#ifdef ZMQ_HAVE_WINDOWS + if (rc_ != SOCKET_ERROR) { + return; + } +#else + if (rc_ != -1) { + return; + } +#endif + + // Check whether an error occurred + int err = 0; +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int len = sizeof err; +#else + socklen_t len = sizeof err; +#endif + + const int rc = getsockopt (s_, SOL_SOCKET, SO_ERROR, + reinterpret_cast (&err), &len); + + // Assert if the error was caused by 0MQ bug. + // Networking problems are OK. No need to assert. +#ifdef ZMQ_HAVE_WINDOWS + zmq_assert (rc == 0); + if (err != 0) { + wsa_assert (err == WSAECONNREFUSED || err == WSAECONNRESET + || err == WSAECONNABORTED || err == WSAEINTR + || err == WSAETIMEDOUT || err == WSAEHOSTUNREACH + || err == WSAENETUNREACH || err == WSAENETDOWN + || err == WSAENETRESET || err == WSAEACCES + || err == WSAEINVAL || err == WSAEADDRINUSE); + } +#else + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + if (rc == -1) + err = errno; + if (err != 0) { + errno = err; + errno_assert (errno == ECONNREFUSED || errno == ECONNRESET + || errno == ECONNABORTED || errno == EINTR + || errno == ETIMEDOUT || errno == EHOSTUNREACH + || errno == ENETUNREACH || errno == ENETDOWN + || errno == ENETRESET || errno == EINVAL); + } +#endif +} + +#ifdef ZMQ_HAVE_IPC +int zmq::create_ipc_wildcard_address (std::string &path_, std::string &file_) +{ +#if defined ZMQ_HAVE_WINDOWS + char buffer[MAX_PATH]; + + { + const errno_t rc = tmpnam_s (buffer); + errno_assert (rc == 0); + } + + // TODO or use CreateDirectoryA and specify permissions? + const int rc = _mkdir (buffer); + if (rc != 0) { + return -1; + } + + path_.assign (buffer); + file_ = path_ + "/socket"; +#else + std::string tmp_path; + + // If TMPDIR, TEMPDIR, or TMP are available and are directories, create + // the socket directory there. + const char **tmp_env = tmp_env_vars; + while (tmp_path.empty () && *tmp_env != 0) { + const char *const tmpdir = getenv (*tmp_env); + struct stat statbuf; + + // Confirm it is actually a directory before trying to use + if (tmpdir != 0 && ::stat (tmpdir, &statbuf) == 0 + && S_ISDIR (statbuf.st_mode)) { + tmp_path.assign (tmpdir); + if (*(tmp_path.rbegin ()) != '/') { + tmp_path.push_back ('/'); + } + } + + // Try the next environment variable + ++tmp_env; + } + + // Append a directory name + tmp_path.append ("tmpXXXXXX"); + + // We need room for tmp_path + trailing NUL + std::vector buffer (tmp_path.length () + 1); + memcpy (&buffer[0], tmp_path.c_str (), tmp_path.length () + 1); + +#if defined HAVE_MKDTEMP + // Create the directory. POSIX requires that mkdtemp() creates the + // directory with 0700 permissions, meaning the only possible race + // with socket creation could be the same user. However, since + // each socket is created in a directory created by mkdtemp(), and + // mkdtemp() guarantees a unique directory name, there will be no + // collision. + if (mkdtemp (&buffer[0]) == 0) { + return -1; + } + + path_.assign (&buffer[0]); + file_ = path_ + "/socket"; +#else + LIBZMQ_UNUSED (path_); + int fd = mkstemp (&buffer[0]); + if (fd == -1) + return -1; + ::close (fd); + + file_.assign (&buffer[0]); +#endif +#endif + + return 0; +} +#endif diff --git a/3rd/libzmq/src/ip.hpp b/3rd/libzmq/src/ip.hpp new file mode 100644 index 00000000..34cc06c4 --- /dev/null +++ b/3rd/libzmq/src/ip.hpp @@ -0,0 +1,89 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_IP_HPP_INCLUDED__ +#define __ZMQ_IP_HPP_INCLUDED__ + +#include +#include "fd.hpp" + +namespace zmq +{ +// Same as socket(2), but allows for transparent tweaking the options. +fd_t open_socket (int domain_, int type_, int protocol_); + +// Sets the socket into non-blocking mode. +void unblock_socket (fd_t s_); + +// Enable IPv4-mapping of addresses in case it is disabled by default. +void enable_ipv4_mapping (fd_t s_); + +// Returns string representation of peer's address. +// Socket sockfd_ must be connected. Returns true iff successful. +int get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_); + +// Sets the IP Type-Of-Service for the underlying socket +void set_ip_type_of_service (fd_t s_, int iptos_); + +// Sets the protocol-defined priority for the underlying socket +void set_socket_priority (fd_t s_, int priority_); + +// Sets the SO_NOSIGPIPE option for the underlying socket. +// Return 0 on success, -1 if the connection has been closed by the peer +int set_nosigpipe (fd_t s_); + +// Binds the underlying socket to the given device, eg. VRF or interface +int bind_to_device (fd_t s_, const std::string &bound_device_); + +// Initialize network subsystem. May be called multiple times. Each call must be matched by a call to shutdown_network. +bool initialize_network (); + +// Shutdown network subsystem. Must be called once for each call to initialize_network before terminating. +void shutdown_network (); + +// Creates a pair of sockets (using signaler_port on OS using TCP sockets). +// Returns -1 if we could not make the socket pair successfully +int make_fdpair (fd_t *r_, fd_t *w_); + +// Makes a socket non-inheritable to child processes. +// Asserts on any failure. +void make_socket_noninheritable (fd_t sock_); + +// Asserts that: +// - an internal 0MQ error did not occur, +// - and, if a socket error occured, it can be recovered from. +void assert_success_or_recoverable (fd_t s_, int rc_); + +#ifdef ZMQ_HAVE_IPC +// Create an IPC wildcard path address +int create_ipc_wildcard_address (std::string &path_, std::string &file_); +#endif +} + +#endif diff --git a/3rd/libzmq/src/ip_resolver.cpp b/3rd/libzmq/src/ip_resolver.cpp new file mode 100644 index 00000000..97d87b21 --- /dev/null +++ b/3rd/libzmq/src/ip_resolver.cpp @@ -0,0 +1,743 @@ +#include "precompiled.hpp" +#include +#include + +#include "macros.hpp" +#include "stdint.hpp" +#include "err.hpp" +#include "ip.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "ip_resolver.hpp" + +int zmq::ip_addr_t::family () const +{ + return generic.sa_family; +} + +bool zmq::ip_addr_t::is_multicast () const +{ + if (family () == AF_INET) { + // IPv4 Multicast: address MSBs are 1110 + // Range: 224.0.0.0 - 239.255.255.255 + return IN_MULTICAST (ntohl (ipv4.sin_addr.s_addr)); + } + // IPv6 Multicast: ff00::/8 + return IN6_IS_ADDR_MULTICAST (&ipv6.sin6_addr) != 0; +} + +uint16_t zmq::ip_addr_t::port () const +{ + if (family () == AF_INET6) { + return ntohs (ipv6.sin6_port); + } + return ntohs (ipv4.sin_port); +} + +const struct sockaddr *zmq::ip_addr_t::as_sockaddr () const +{ + return &generic; +} + +zmq::zmq_socklen_t zmq::ip_addr_t::sockaddr_len () const +{ + return static_cast (family () == AF_INET6 ? sizeof (ipv6) + : sizeof (ipv4)); +} + +void zmq::ip_addr_t::set_port (uint16_t port_) +{ + if (family () == AF_INET6) { + ipv6.sin6_port = htons (port_); + } else { + ipv4.sin_port = htons (port_); + } +} + +// Construct an "ANY" address for the given family +zmq::ip_addr_t zmq::ip_addr_t::any (int family_) +{ + ip_addr_t addr; + + if (family_ == AF_INET) { + sockaddr_in *ip4_addr = &addr.ipv4; + memset (ip4_addr, 0, sizeof (*ip4_addr)); + ip4_addr->sin_family = AF_INET; + ip4_addr->sin_addr.s_addr = htonl (INADDR_ANY); + } else if (family_ == AF_INET6) { + sockaddr_in6 *ip6_addr = &addr.ipv6; + + memset (ip6_addr, 0, sizeof (*ip6_addr)); + ip6_addr->sin6_family = AF_INET6; +#ifdef ZMQ_HAVE_VXWORKS + struct in6_addr newaddr = IN6ADDR_ANY_INIT; + memcpy (&ip6_addr->sin6_addr, &newaddr, sizeof (in6_addr)); +#else + memcpy (&ip6_addr->sin6_addr, &in6addr_any, sizeof (in6addr_any)); +#endif + } else { + assert (0 == "unsupported address family"); + } + + return addr; +} + +zmq::ip_resolver_options_t::ip_resolver_options_t () : + _bindable_wanted (false), + _nic_name_allowed (false), + _ipv6_wanted (false), + _port_expected (false), + _dns_allowed (false), + _path_allowed (false) +{ +} + +zmq::ip_resolver_options_t & +zmq::ip_resolver_options_t::bindable (bool bindable_) +{ + _bindable_wanted = bindable_; + + return *this; +} + +zmq::ip_resolver_options_t & +zmq::ip_resolver_options_t::allow_nic_name (bool allow_) +{ + _nic_name_allowed = allow_; + + return *this; +} + +zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::ipv6 (bool ipv6_) +{ + _ipv6_wanted = ipv6_; + + return *this; +} + +// If true we expect that the host will be followed by a colon and a port +// number or service name +zmq::ip_resolver_options_t & +zmq::ip_resolver_options_t::expect_port (bool expect_) +{ + _port_expected = expect_; + + return *this; +} + +zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::allow_dns (bool allow_) +{ + _dns_allowed = allow_; + + return *this; +} + +zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::allow_path (bool allow_) +{ + _path_allowed = allow_; + + return *this; +} + +bool zmq::ip_resolver_options_t::bindable () +{ + return _bindable_wanted; +} + +bool zmq::ip_resolver_options_t::allow_nic_name () +{ + return _nic_name_allowed; +} + +bool zmq::ip_resolver_options_t::ipv6 () +{ + return _ipv6_wanted; +} + +bool zmq::ip_resolver_options_t::expect_port () +{ + return _port_expected; +} + +bool zmq::ip_resolver_options_t::allow_dns () +{ + return _dns_allowed; +} + +bool zmq::ip_resolver_options_t::allow_path () +{ + return _path_allowed; +} + +zmq::ip_resolver_t::ip_resolver_t (ip_resolver_options_t opts_) : + _options (opts_) +{ +} + +int zmq::ip_resolver_t::resolve (ip_addr_t *ip_addr_, const char *name_) +{ + std::string addr; + uint16_t port; + + if (_options.expect_port ()) { + // We expect 'addr:port'. It's important to use str*r*chr to only get + // the latest colon since IPv6 addresses use colons as delemiters. + const char *delim = strrchr (name_, ':'); + + if (delim == NULL) { + errno = EINVAL; + return -1; + } + + addr = std::string (name_, delim - name_); + const std::string port_str = std::string (delim + 1); + + if (port_str == "*") { + if (_options.bindable ()) { + // Resolve wildcard to 0 to allow autoselection of port + port = 0; + } else { + errno = EINVAL; + return -1; + } + } else if (port_str == "0") { + // Using "0" for a bind address is equivalent to using "*". For a + // connectable address it could be used to connect to port 0. + port = 0; + } else { + // Parse the port number (0 is not a valid port). + port = static_cast (atoi (port_str.c_str ())); + if (port == 0) { + errno = EINVAL; + return -1; + } + } + } else { + addr = std::string (name_); + port = 0; + } + + // Check if path is allowed in ip address, if allowed it must be truncated + if (_options.allow_path ()) { + const size_t pos = addr.find ('/'); + if (pos != std::string::npos) + addr = addr.substr (0, pos); + } + + // Trim any square brackets surrounding the address. Used for + // IPv6 addresses to remove the confusion with the port + // delimiter. + // TODO Should we validate that the brackets are present if + // 'addr' contains ':' ? + const size_t brackets_length = 2; + if (addr.size () >= brackets_length && addr[0] == '[' + && addr[addr.size () - 1] == ']') { + addr = addr.substr (1, addr.size () - brackets_length); + } + + // Look for an interface name / zone_id in the address + // Reference: https://tools.ietf.org/html/rfc4007 + const std::size_t pos = addr.rfind ('%'); + uint32_t zone_id = 0; + + if (pos != std::string::npos) { + std::string if_str = addr.substr (pos + 1); + if (if_str.empty ()) { + errno = EINVAL; + return -1; + } + addr = addr.substr (0, pos); + + if (isalpha (if_str.at (0))) { + zone_id = do_if_nametoindex (if_str.c_str ()); + } else { + zone_id = static_cast (atoi (if_str.c_str ())); + } + + if (zone_id == 0) { + errno = EINVAL; + return -1; + } + } + + bool resolved = false; + const char *addr_str = addr.c_str (); + + if (_options.bindable () && addr == "*") { + // Return an ANY address + *ip_addr_ = ip_addr_t::any (_options.ipv6 () ? AF_INET6 : AF_INET); + resolved = true; + } + + if (!resolved && _options.allow_nic_name ()) { + // Try to resolve the string as a NIC name. + const int rc = resolve_nic_name (ip_addr_, addr_str); + + if (rc == 0) { + resolved = true; + } else if (errno != ENODEV) { + return rc; + } + } + + if (!resolved) { + const int rc = resolve_getaddrinfo (ip_addr_, addr_str); + + if (rc != 0) { + return rc; + } + resolved = true; + } + + // Store the port into the structure. We could get 'getaddrinfo' to do it + // for us but since we don't resolve service names it's a bit overkill and + // we'd still have to do it manually when the address is resolved by + // 'resolve_nic_name' + ip_addr_->set_port (port); + + if (ip_addr_->family () == AF_INET6) { + ip_addr_->ipv6.sin6_scope_id = zone_id; + } + + assert (resolved == true); + return 0; +} + +int zmq::ip_resolver_t::resolve_getaddrinfo (ip_addr_t *ip_addr_, + const char *addr_) +{ +#if defined ZMQ_HAVE_OPENVMS && defined __ia64 + __addrinfo64 *res = NULL; + __addrinfo64 req; +#else + addrinfo *res = NULL; + addrinfo req; +#endif + + memset (&req, 0, sizeof (req)); + + // Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for + // IPv4-in-IPv6 addresses. + req.ai_family = _options.ipv6 () ? AF_INET6 : AF_INET; + + // Arbitrary, not used in the output, but avoids duplicate results. + req.ai_socktype = SOCK_STREAM; + + req.ai_flags = 0; + + if (_options.bindable ()) { + req.ai_flags |= AI_PASSIVE; + } + + if (!_options.allow_dns ()) { + req.ai_flags |= AI_NUMERICHOST; + } + +#if defined AI_V4MAPPED + // In this API we only require IPv4-mapped addresses when + // no native IPv6 interfaces are available (~AI_ALL). + // This saves an additional DNS roundtrip for IPv4 addresses. + if (req.ai_family == AF_INET6) { + req.ai_flags |= AI_V4MAPPED; + } +#endif + + // Resolve the literal address. Some of the error info is lost in case + // of error, however, there's no way to report EAI errors via errno. + int rc = do_getaddrinfo (addr_, NULL, &req, &res); + +#if defined AI_V4MAPPED + // Some OS do have AI_V4MAPPED defined but it is not supported in getaddrinfo() + // returning EAI_BADFLAGS. Detect this and retry + if (rc == EAI_BADFLAGS && (req.ai_flags & AI_V4MAPPED)) { + req.ai_flags &= ~AI_V4MAPPED; + rc = do_getaddrinfo (addr_, NULL, &req, &res); + } +#endif + +#if defined ZMQ_HAVE_WINDOWS + // Resolve specific case on Windows platform when using IPv4 address + // with ZMQ_IPv6 socket option. + if ((req.ai_family == AF_INET6) && (rc == WSAHOST_NOT_FOUND)) { + req.ai_family = AF_INET; + rc = do_getaddrinfo (addr_, NULL, &req, &res); + } +#endif + + if (rc) { + switch (rc) { + case EAI_MEMORY: + errno = ENOMEM; + break; + default: + if (_options.bindable ()) { + errno = ENODEV; + } else { + errno = EINVAL; + } + break; + } + return -1; + } + + // Use the first result. + zmq_assert (res != NULL); + zmq_assert (static_cast (res->ai_addrlen) <= sizeof (*ip_addr_)); + memcpy (ip_addr_, res->ai_addr, res->ai_addrlen); + + // Cleanup getaddrinfo after copying the possibly referenced result. + do_freeaddrinfo (res); + + return 0; +} + +#ifdef ZMQ_HAVE_SOLARIS +#include + +// On Solaris platform, network interface name can be queried by ioctl. +int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_) +{ + // Create a socket. + const int fd = open_socket (AF_INET, SOCK_DGRAM, 0); + errno_assert (fd != -1); + + // Retrieve number of interfaces. + lifnum ifn; + ifn.lifn_family = AF_INET; + ifn.lifn_flags = 0; + int rc = ioctl (fd, SIOCGLIFNUM, (char *) &ifn); + errno_assert (rc != -1); + + // Allocate memory to get interface names. + const size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count; + char *ifr = (char *) malloc (ifr_size); + alloc_assert (ifr); + + // Retrieve interface names. + lifconf ifc; + ifc.lifc_family = AF_INET; + ifc.lifc_flags = 0; + ifc.lifc_len = ifr_size; + ifc.lifc_buf = ifr; + rc = ioctl (fd, SIOCGLIFCONF, (char *) &ifc); + errno_assert (rc != -1); + + // Find the interface with the specified name and AF_INET family. + bool found = false; + lifreq *ifrp = ifc.lifc_req; + for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq)); n++, ifrp++) { + if (!strcmp (nic_, ifrp->lifr_name)) { + rc = ioctl (fd, SIOCGLIFADDR, (char *) ifrp); + errno_assert (rc != -1); + if (ifrp->lifr_addr.ss_family == AF_INET) { + ip_addr_->ipv4 = *(sockaddr_in *) &ifrp->lifr_addr; + found = true; + break; + } + } + } + + // Clean-up. + free (ifr); + close (fd); + + if (!found) { + errno = ENODEV; + return -1; + } + return 0; +} + +#elif defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX \ + || defined ZMQ_HAVE_ANDROID || defined ZMQ_HAVE_VXWORKS +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif + +int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_) +{ +#if defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX + // IPv6 support not implemented for AIX or HP/UX. + if (_options.ipv6 ()) { + errno = ENODEV; + return -1; + } +#endif + + // Create a socket. + const int sd = + open_socket (_options.ipv6 () ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); + errno_assert (sd != -1); + + struct ifreq ifr; + + // Copy interface name for ioctl get. + strncpy (ifr.ifr_name, nic_, sizeof (ifr.ifr_name)); + + // Fetch interface address. + const int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (ifr)); + + // Clean up. + close (sd); + + if (rc == -1) { + errno = ENODEV; + return -1; + } + + const int family = ifr.ifr_addr.sa_family; + if (family == (_options.ipv6 () ? AF_INET6 : AF_INET) + && !strcmp (nic_, ifr.ifr_name)) { + memcpy (ip_addr_, &ifr.ifr_addr, + (family == AF_INET) ? sizeof (struct sockaddr_in) + : sizeof (struct sockaddr_in6)); + } else { + errno = ENODEV; + return -1; + } + + return 0; +} + +#elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD \ + || defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD \ + || defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD \ + || defined ZMQ_HAVE_DRAGONFLY || defined ZMQ_HAVE_GNU) \ + && defined ZMQ_HAVE_IFADDRS) + +#include + +// On these platforms, network interface name can be queried +// using getifaddrs function. +int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_) +{ + // Get the addresses. + ifaddrs *ifa = NULL; + int rc = 0; + const int max_attempts = 10; + const int backoff_msec = 1; + for (int i = 0; i < max_attempts; i++) { + rc = getifaddrs (&ifa); + if (rc == 0 || (rc < 0 && errno != ECONNREFUSED)) + break; + usleep ((backoff_msec << i) * 1000); + } + + if (rc != 0 && ((errno == EINVAL) || (errno == EOPNOTSUPP))) { + // Windows Subsystem for Linux compatibility + errno = ENODEV; + return -1; + } + errno_assert (rc == 0); + zmq_assert (ifa != NULL); + + // Find the corresponding network interface. + bool found = false; + for (const ifaddrs *ifp = ifa; ifp != NULL; ifp = ifp->ifa_next) { + if (ifp->ifa_addr == NULL) + continue; + + const int family = ifp->ifa_addr->sa_family; + if (family == (_options.ipv6 () ? AF_INET6 : AF_INET) + && !strcmp (nic_, ifp->ifa_name)) { + memcpy (ip_addr_, ifp->ifa_addr, + (family == AF_INET) ? sizeof (struct sockaddr_in) + : sizeof (struct sockaddr_in6)); + found = true; + break; + } + } + + // Clean-up; + freeifaddrs (ifa); + + if (!found) { + errno = ENODEV; + return -1; + } + return 0; +} + +#elif (defined ZMQ_HAVE_WINDOWS) + +#include + +int zmq::ip_resolver_t::get_interface_name (unsigned long index_, + char **dest_) const +{ +#ifdef ZMQ_HAVE_WINDOWS_UWP + char *buffer = (char *) malloc (1024); +#else + char *buffer = static_cast (malloc (IF_MAX_STRING_SIZE)); +#endif + alloc_assert (buffer); + + char *if_name_result = NULL; + +#if _WIN32_WINNT > _WIN32_WINNT_WINXP && !defined ZMQ_HAVE_WINDOWS_UWP + if_name_result = if_indextoname (index_, buffer); +#endif + + if (if_name_result == NULL) { + free (buffer); + return -1; + } + + *dest_ = buffer; + return 0; +} + +int zmq::ip_resolver_t::wchar_to_utf8 (const WCHAR *src_, char **dest_) const +{ + int rc; + const int buffer_len = + WideCharToMultiByte (CP_UTF8, 0, src_, -1, NULL, 0, NULL, 0); + + char *buffer = static_cast (malloc (buffer_len)); + alloc_assert (buffer); + + rc = + WideCharToMultiByte (CP_UTF8, 0, src_, -1, buffer, buffer_len, NULL, 0); + + if (rc == 0) { + free (buffer); + return -1; + } + + *dest_ = buffer; + return 0; +} + +int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_) +{ + int rc; + bool found = false; + const int max_attempts = 10; + + int iterations = 0; + IP_ADAPTER_ADDRESSES *addresses; + unsigned long out_buf_len = sizeof (IP_ADAPTER_ADDRESSES); + + do { + addresses = static_cast (malloc (out_buf_len)); + alloc_assert (addresses); + + rc = + GetAdaptersAddresses (AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST + | GAA_FLAG_SKIP_DNS_SERVER, + NULL, addresses, &out_buf_len); + if (rc == ERROR_BUFFER_OVERFLOW) { + free (addresses); + addresses = NULL; + } else { + break; + } + iterations++; + } while ((rc == ERROR_BUFFER_OVERFLOW) && (iterations < max_attempts)); + + if (rc == 0) { + for (const IP_ADAPTER_ADDRESSES *current_addresses = addresses; + current_addresses; current_addresses = current_addresses->Next) { + char *if_name = NULL; + char *if_friendly_name = NULL; + + const int str_rc1 = + get_interface_name (current_addresses->IfIndex, &if_name); + const int str_rc2 = wchar_to_utf8 (current_addresses->FriendlyName, + &if_friendly_name); + + // Find a network adapter by its "name" or "friendly name" + if (((str_rc1 == 0) && (!strcmp (nic_, if_name))) + || ((str_rc2 == 0) && (!strcmp (nic_, if_friendly_name)))) { + // Iterate over all unicast addresses bound to the current network interface + for (const IP_ADAPTER_UNICAST_ADDRESS *current_unicast_address = + current_addresses->FirstUnicastAddress; + current_unicast_address; + current_unicast_address = current_unicast_address->Next) { + const ADDRESS_FAMILY family = + current_unicast_address->Address.lpSockaddr->sa_family; + + if (family == (_options.ipv6 () ? AF_INET6 : AF_INET)) { + memcpy ( + ip_addr_, current_unicast_address->Address.lpSockaddr, + (family == AF_INET) ? sizeof (struct sockaddr_in) + : sizeof (struct sockaddr_in6)); + found = true; + break; + } + } + + if (found) + break; + } + + if (str_rc1 == 0) + free (if_name); + if (str_rc2 == 0) + free (if_friendly_name); + } + + free (addresses); + } + + if (!found) { + errno = ENODEV; + return -1; + } + return 0; +} + +#else + +// On other platforms we assume there are no sane interface names. +int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_) +{ + LIBZMQ_UNUSED (ip_addr_); + LIBZMQ_UNUSED (nic_); + + errno = ENODEV; + return -1; +} + +#endif + +int zmq::ip_resolver_t::do_getaddrinfo (const char *node_, + const char *service_, + const struct addrinfo *hints_, + struct addrinfo **res_) +{ + return getaddrinfo (node_, service_, hints_, res_); +} + +void zmq::ip_resolver_t::do_freeaddrinfo (struct addrinfo *res_) +{ + freeaddrinfo (res_); +} + + +unsigned int zmq::ip_resolver_t::do_if_nametoindex (const char *ifname_) +{ +#ifdef HAVE_IF_NAMETOINDEX + return if_nametoindex (ifname_); +#else + LIBZMQ_UNUSED (ifname_); + // The function 'if_nametoindex' is not supported on Windows XP. + // If we are targeting XP using a vxxx_xp toolset then fail. + // This is brutal as this code could be run on later windows clients + // meaning the IPv6 zone_id cannot have an interface name. + // This could be fixed with a runtime check. + return 0; +#endif +} diff --git a/3rd/libzmq/src/ip_resolver.hpp b/3rd/libzmq/src/ip_resolver.hpp new file mode 100644 index 00000000..e60f6356 --- /dev/null +++ b/3rd/libzmq/src/ip_resolver.hpp @@ -0,0 +1,121 @@ +/* + Copyright (c) 2007-2018 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 . +*/ + +#ifndef __ZMQ_IP_RESOLVER_HPP_INCLUDED__ +#define __ZMQ_IP_RESOLVER_HPP_INCLUDED__ + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#endif + +#include "address.hpp" + +namespace zmq +{ +union ip_addr_t +{ + sockaddr generic; + sockaddr_in ipv4; + sockaddr_in6 ipv6; + + int family () const; + bool is_multicast () const; + uint16_t port () const; + + const struct sockaddr *as_sockaddr () const; + zmq_socklen_t sockaddr_len () const; + + void set_port (uint16_t); + + static ip_addr_t any (int family_); +}; + +class ip_resolver_options_t +{ + public: + ip_resolver_options_t (); + + ip_resolver_options_t &bindable (bool bindable_); + ip_resolver_options_t &allow_nic_name (bool allow_); + ip_resolver_options_t &ipv6 (bool ipv6_); + ip_resolver_options_t &expect_port (bool expect_); + ip_resolver_options_t &allow_dns (bool allow_); + ip_resolver_options_t &allow_path (bool allow_); + + bool bindable (); + bool allow_nic_name (); + bool ipv6 (); + bool expect_port (); + bool allow_dns (); + bool allow_path (); + + private: + bool _bindable_wanted; + bool _nic_name_allowed; + bool _ipv6_wanted; + bool _port_expected; + bool _dns_allowed; + bool _path_allowed; +}; + +class ip_resolver_t +{ + public: + ip_resolver_t (ip_resolver_options_t opts_); + virtual ~ip_resolver_t (){}; + + int resolve (ip_addr_t *ip_addr_, const char *name_); + + protected: + // Virtual functions that are overridden in tests + virtual int do_getaddrinfo (const char *node_, + const char *service_, + const struct addrinfo *hints_, + struct addrinfo **res_); + + virtual void do_freeaddrinfo (struct addrinfo *res_); + + virtual unsigned int do_if_nametoindex (const char *ifname_); + + private: + ip_resolver_options_t _options; + + int resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_); + int resolve_getaddrinfo (ip_addr_t *ip_addr_, const char *addr_); + +#if defined ZMQ_HAVE_WINDOWS + int get_interface_name (unsigned long index_, char **dest_) const; + int wchar_to_utf8 (const WCHAR *src_, char **dest_) const; +#endif +}; +} + +#endif diff --git a/3rd/libzmq/src/ipc_address.cpp b/3rd/libzmq/src/ipc_address.cpp new file mode 100644 index 00000000..f1f56516 --- /dev/null +++ b/3rd/libzmq/src/ipc_address.cpp @@ -0,0 +1,120 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "compat.hpp" +#include "ipc_address.hpp" + +#if defined ZMQ_HAVE_IPC + +#include "err.hpp" + +#include + +zmq::ipc_address_t::ipc_address_t () +{ + memset (&_address, 0, sizeof _address); +} + +zmq::ipc_address_t::ipc_address_t (const sockaddr *sa_, socklen_t sa_len_) : + _addrlen (sa_len_) +{ + zmq_assert (sa_ && sa_len_ > 0); + + memset (&_address, 0, sizeof _address); + if (sa_->sa_family == AF_UNIX) + memcpy (&_address, sa_, sa_len_); +} + +zmq::ipc_address_t::~ipc_address_t () +{ +} + +int zmq::ipc_address_t::resolve (const char *path_) +{ + const size_t path_len = strlen (path_); + if (path_len >= sizeof _address.sun_path) { + errno = ENAMETOOLONG; + return -1; + } + if (path_[0] == '@' && !path_[1]) { + errno = EINVAL; + return -1; + } + + _address.sun_family = AF_UNIX; + memcpy (_address.sun_path, path_, path_len + 1); + /* Abstract sockets start with '\0' */ + if (path_[0] == '@') + *_address.sun_path = '\0'; + + _addrlen = + static_cast (offsetof (sockaddr_un, sun_path) + path_len); + return 0; +} + +int zmq::ipc_address_t::to_string (std::string &addr_) const +{ + if (_address.sun_family != AF_UNIX) { + addr_.clear (); + return -1; + } + + const char prefix[] = "ipc://"; + char buf[sizeof prefix + sizeof _address.sun_path]; + char *pos = buf; + memcpy (pos, prefix, sizeof prefix - 1); + pos += sizeof prefix - 1; + const char *src_pos = _address.sun_path; + if (!_address.sun_path[0] && _address.sun_path[1]) { + *pos++ = '@'; + src_pos++; + } + // according to http://man7.org/linux/man-pages/man7/unix.7.html, NOTES + // section, address.sun_path might not always be null-terminated; therefore, + // we calculate the length based of addrlen + const size_t src_len = + strnlen (src_pos, _addrlen - offsetof (sockaddr_un, sun_path) + - (src_pos - _address.sun_path)); + memcpy (pos, src_pos, src_len); + addr_.assign (buf, pos - buf + src_len); + return 0; +} + +const sockaddr *zmq::ipc_address_t::addr () const +{ + return reinterpret_cast (&_address); +} + +socklen_t zmq::ipc_address_t::addrlen () const +{ + return _addrlen; +} + +#endif diff --git a/3rd/libzmq/src/ipc_address.hpp b/3rd/libzmq/src/ipc_address.hpp new file mode 100644 index 00000000..4c2277d3 --- /dev/null +++ b/3rd/libzmq/src/ipc_address.hpp @@ -0,0 +1,74 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_IPC_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_IPC_ADDRESS_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_IPC + +#include + +#if defined _MSC_VER +#include +#else +#include +#include +#endif + +#include "macros.hpp" + +namespace zmq +{ +class ipc_address_t +{ + public: + ipc_address_t (); + ipc_address_t (const sockaddr *sa_, socklen_t sa_len_); + ~ipc_address_t (); + + // This function sets up the address for UNIX domain transport. + int resolve (const char *path_); + + // The opposite to resolve() + int to_string (std::string &addr_) const; + + const sockaddr *addr () const; + socklen_t addrlen () const; + + private: + struct sockaddr_un _address; + socklen_t _addrlen; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ipc_address_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/ipc_connecter.cpp b/3rd/libzmq/src/ipc_connecter.cpp new file mode 100644 index 00000000..8dd01f02 --- /dev/null +++ b/3rd/libzmq/src/ipc_connecter.cpp @@ -0,0 +1,186 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "ipc_connecter.hpp" + +#if defined ZMQ_HAVE_IPC + +#include +#include + +#include "io_thread.hpp" +#include "random.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "address.hpp" +#include "ipc_address.hpp" +#include "session_base.hpp" + +#ifdef _MSC_VER +#include +#else +#include +#include +#include +#include +#endif + +zmq::ipc_connecter_t::ipc_connecter_t (class io_thread_t *io_thread_, + class session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_) : + stream_connecter_base_t ( + io_thread_, session_, options_, addr_, delayed_start_) +{ + zmq_assert (_addr->protocol == protocol_name::ipc); +} + +void zmq::ipc_connecter_t::out_event () +{ + const fd_t fd = connect (); + rm_handle (); + + // Handle the error condition by attempt to reconnect. + if (fd == retired_fd) { + close (); + add_reconnect_timer (); + return; + } + + create_engine (fd, get_socket_name (fd, socket_end_local)); +} + +void zmq::ipc_connecter_t::start_connecting () +{ + // Open the connecting socket. + const int rc = open (); + + // Connect may succeed in synchronous manner. + if (rc == 0) { + _handle = add_fd (_s); + out_event (); + } + + // Connection establishment may be delayed. Poll for its completion. + else if (rc == -1 && errno == EINPROGRESS) { + _handle = add_fd (_s); + set_pollout (_handle); + _socket->event_connect_delayed ( + make_unconnected_connect_endpoint_pair (_endpoint), zmq_errno ()); + + // TODO, tcp_connecter_t adds a connect timer in this case; maybe this + // should be done here as well (and then this could be pulled up to + // stream_connecter_base_t). + } + //stop connecting after called zmq_disconnect + else if (rc == -1 + && (options.reconnect_stop & ZMQ_RECONNECT_STOP_AFTER_DISCONNECT) + && errno == ECONNREFUSED && _socket->is_disconnected ()) { + if (_s != retired_fd) + close (); + } + + // Handle any other error condition by eventual reconnect. + else { + if (_s != retired_fd) + close (); + add_reconnect_timer (); + } +} + +int zmq::ipc_connecter_t::open () +{ + zmq_assert (_s == retired_fd); + + // Create the socket. + _s = open_socket (AF_UNIX, SOCK_STREAM, 0); + if (_s == retired_fd) + return -1; + + // Set the non-blocking flag. + unblock_socket (_s); + + // Connect to the remote peer. + const int rc = ::connect (_s, _addr->resolved.ipc_addr->addr (), + _addr->resolved.ipc_addr->addrlen ()); + + // Connect was successful immediately. + if (rc == 0) + return 0; + + // Translate other error codes indicating asynchronous connect has been + // launched to a uniform EINPROGRESS. +#ifdef ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + if (last_error == WSAEINPROGRESS || last_error == WSAEWOULDBLOCK) + errno = EINPROGRESS; + else + errno = wsa_error_to_errno (last_error); +#else + if (rc == -1 && errno == EINTR) { + errno = EINPROGRESS; + } +#endif + + // Forward the error. + return -1; +} + +zmq::fd_t zmq::ipc_connecter_t::connect () +{ + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + int err = 0; + zmq_socklen_t len = static_cast (sizeof (err)); + const int rc = getsockopt (_s, SOL_SOCKET, SO_ERROR, + reinterpret_cast (&err), &len); + if (rc == -1) { + if (errno == ENOPROTOOPT) + errno = 0; + err = errno; + } + if (err != 0) { + // Assert if the error was caused by 0MQ bug. + // Networking problems are OK. No need to assert. + errno = err; + errno_assert (errno == ECONNREFUSED || errno == ECONNRESET + || errno == ETIMEDOUT || errno == EHOSTUNREACH + || errno == ENETUNREACH || errno == ENETDOWN); + + return retired_fd; + } + + const fd_t result = _s; + _s = retired_fd; + return result; +} + +#endif diff --git a/3rd/libzmq/src/ipc_connecter.hpp b/3rd/libzmq/src/ipc_connecter.hpp new file mode 100644 index 00000000..1f85c893 --- /dev/null +++ b/3rd/libzmq/src/ipc_connecter.hpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ + +#ifndef __IPC_CONNECTER_HPP_INCLUDED__ +#define __IPC_CONNECTER_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_IPC + +#include "fd.hpp" +#include "stream_connecter_base.hpp" + +namespace zmq +{ +class ipc_connecter_t ZMQ_FINAL : public stream_connecter_base_t +{ + public: + // If 'delayed_start' is true connecter first waits for a while, + // then starts connection process. + ipc_connecter_t (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_); + + private: + // Handlers for I/O events. + void out_event (); + + // Internal function to start the actual connection establishment. + void start_connecting (); + + // Open IPC connecting socket. Returns -1 in case of error, + // 0 if connect was successful immediately. Returns -1 with + // EAGAIN errno if async connect was launched. + int open (); + + // Get the file descriptor of newly created connection. Returns + // retired_fd if the connection was unsuccessful. + fd_t connect (); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ipc_connecter_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/ipc_listener.cpp b/3rd/libzmq/src/ipc_listener.cpp new file mode 100644 index 00000000..fc5bc51a --- /dev/null +++ b/3rd/libzmq/src/ipc_listener.cpp @@ -0,0 +1,357 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "ipc_listener.hpp" + +#if defined ZMQ_HAVE_IPC + +#include + +#include + +#include "ipc_address.hpp" +#include "io_thread.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "socket_base.hpp" +#include "address.hpp" + +#ifdef _MSC_VER +#ifdef ZMQ_IOTHREAD_POLLER_USE_SELECT +#error On Windows, IPC does not work with POLLER=select, use POLLER=epoll instead, or disable IPC transport +#endif + +#include +#include + +#define rmdir _rmdir +#define unlink _unlink + +#else +#include +#include +#include +#include +#endif + +#ifdef ZMQ_HAVE_LOCAL_PEERCRED +#include +#include +#endif +#ifdef ZMQ_HAVE_SO_PEERCRED +#include +#include +#include +#if defined ZMQ_HAVE_OPENBSD +#define ucred sockpeercred +#endif +#endif + +zmq::ipc_listener_t::ipc_listener_t (io_thread_t *io_thread_, + socket_base_t *socket_, + const options_t &options_) : + stream_listener_base_t (io_thread_, socket_, options_), + _has_file (false) +{ +} + +void zmq::ipc_listener_t::in_event () +{ + const fd_t fd = accept (); + + // If connection was reset by the peer in the meantime, just ignore it. + // TODO: Handle specific errors like ENFILE/EMFILE etc. + if (fd == retired_fd) { + _socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); + return; + } + + // Create the engine object for this connection. + create_engine (fd); +} + +std::string +zmq::ipc_listener_t::get_socket_name (zmq::fd_t fd_, + socket_end_t socket_end_) const +{ + return zmq::get_socket_name (fd_, socket_end_); +} + +int zmq::ipc_listener_t::set_local_address (const char *addr_) +{ + // Create addr on stack for auto-cleanup + std::string addr (addr_); + + // Allow wildcard file + if (options.use_fd == -1 && addr[0] == '*') { + if (create_ipc_wildcard_address (_tmp_socket_dirname, addr) < 0) { + return -1; + } + } + + // Get rid of the file associated with the UNIX domain socket that + // may have been left behind by the previous run of the application. + // MUST NOT unlink if the FD is managed by the user, or it will stop + // working after the first client connects. The user will take care of + // cleaning up the file after the service is stopped. + if (options.use_fd == -1) { + ::unlink (addr.c_str ()); + } + _filename.clear (); + + // Initialise the address structure. + ipc_address_t address; + int rc = address.resolve (addr.c_str ()); + if (rc != 0) { + if (!_tmp_socket_dirname.empty ()) { + // We need to preserve errno to return to the user + const int tmp_errno = errno; + ::rmdir (_tmp_socket_dirname.c_str ()); + _tmp_socket_dirname.clear (); + errno = tmp_errno; + } + return -1; + } + + address.to_string (_endpoint); + + if (options.use_fd != -1) { + _s = options.use_fd; + } else { + // Create a listening socket. + _s = open_socket (AF_UNIX, SOCK_STREAM, 0); + if (_s == retired_fd) { + if (!_tmp_socket_dirname.empty ()) { + // We need to preserve errno to return to the user + const int tmp_errno = errno; + ::rmdir (_tmp_socket_dirname.c_str ()); + _tmp_socket_dirname.clear (); + errno = tmp_errno; + } + return -1; + } + + // Bind the socket to the file path. + rc = bind (_s, const_cast (address.addr ()), + address.addrlen ()); + if (rc != 0) + goto error; + + // Listen for incoming connections. + rc = listen (_s, options.backlog); + if (rc != 0) + goto error; + } + + _filename = ZMQ_MOVE (addr); + _has_file = true; + + _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint), + _s); + return 0; + +error: + const int err = errno; + close (); + errno = err; + return -1; +} + +int zmq::ipc_listener_t::close () +{ + zmq_assert (_s != retired_fd); + const fd_t fd_for_event = _s; +#ifdef ZMQ_HAVE_WINDOWS + int rc = closesocket (_s); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = ::close (_s); + errno_assert (rc == 0); +#endif + + _s = retired_fd; + + if (_has_file && options.use_fd == -1) { + if (!_tmp_socket_dirname.empty ()) { + // TODO review this behaviour, it is inconsistent with the use of + // unlink in open since 656cdb959a7482c45db979c1d08ede585d12e315; + // however, we must at least remove the file before removing the + // directory, otherwise it will always fail + rc = ::unlink (_filename.c_str ()); + + if (rc == 0) { + rc = ::rmdir (_tmp_socket_dirname.c_str ()); + _tmp_socket_dirname.clear (); + } + } + + if (rc != 0) { + _socket->event_close_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); + return -1; + } + } + + _socket->event_closed (make_unconnected_bind_endpoint_pair (_endpoint), + fd_for_event); + return 0; +} + +#if defined ZMQ_HAVE_SO_PEERCRED + +bool zmq::ipc_listener_t::filter (fd_t sock_) +{ + if (options.ipc_uid_accept_filters.empty () + && options.ipc_pid_accept_filters.empty () + && options.ipc_gid_accept_filters.empty ()) + return true; + + struct ucred cred; + socklen_t size = sizeof (cred); + + if (getsockopt (sock_, SOL_SOCKET, SO_PEERCRED, &cred, &size)) + return false; + if (options.ipc_uid_accept_filters.find (cred.uid) + != options.ipc_uid_accept_filters.end () + || options.ipc_gid_accept_filters.find (cred.gid) + != options.ipc_gid_accept_filters.end () + || options.ipc_pid_accept_filters.find (cred.pid) + != options.ipc_pid_accept_filters.end ()) + return true; + + const struct passwd *pw; + const struct group *gr; + + if (!(pw = getpwuid (cred.uid))) + return false; + for (options_t::ipc_gid_accept_filters_t::const_iterator + it = options.ipc_gid_accept_filters.begin (), + end = options.ipc_gid_accept_filters.end (); + it != end; it++) { + if (!(gr = getgrgid (*it))) + continue; + for (char **mem = gr->gr_mem; *mem; mem++) { + if (!strcmp (*mem, pw->pw_name)) + return true; + } + } + return false; +} + +#elif defined ZMQ_HAVE_LOCAL_PEERCRED + +bool zmq::ipc_listener_t::filter (fd_t sock_) +{ + if (options.ipc_uid_accept_filters.empty () + && options.ipc_gid_accept_filters.empty ()) + return true; + + struct xucred cred; + socklen_t size = sizeof (cred); + + if (getsockopt (sock_, 0, LOCAL_PEERCRED, &cred, &size)) + return false; + if (cred.cr_version != XUCRED_VERSION) + return false; + if (options.ipc_uid_accept_filters.find (cred.cr_uid) + != options.ipc_uid_accept_filters.end ()) + return true; + for (int i = 0; i < cred.cr_ngroups; i++) { + if (options.ipc_gid_accept_filters.find (cred.cr_groups[i]) + != options.ipc_gid_accept_filters.end ()) + return true; + } + + return false; +} + +#endif + +zmq::fd_t zmq::ipc_listener_t::accept () +{ + // Accept one connection and deal with different failure modes. + // The situation where connection cannot be accepted due to insufficient + // resources is considered valid and treated by ignoring the connection. + zmq_assert (_s != retired_fd); +#if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4 + fd_t sock = ::accept4 (_s, NULL, NULL, SOCK_CLOEXEC); +#else + struct sockaddr_storage ss; + memset (&ss, 0, sizeof (ss)); +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int ss_len = sizeof (ss); +#else + socklen_t ss_len = sizeof (ss); +#endif + + const fd_t sock = + ::accept (_s, reinterpret_cast (&ss), &ss_len); +#endif + if (sock == retired_fd) { +#if defined ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET + || last_error == WSAEMFILE || last_error == WSAENOBUFS); +#else + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR + || errno == ECONNABORTED || errno == EPROTO + || errno == ENFILE); +#endif + return retired_fd; + } + + make_socket_noninheritable (sock); + + // IPC accept() filters +#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED + if (!filter (sock)) { + int rc = ::close (sock); + errno_assert (rc == 0); + return retired_fd; + } +#endif + + if (zmq::set_nosigpipe (sock)) { +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (sock); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = ::close (sock); + errno_assert (rc == 0); +#endif + return retired_fd; + } + + return sock; +} + +#endif diff --git a/3rd/libzmq/src/ipc_listener.hpp b/3rd/libzmq/src/ipc_listener.hpp new file mode 100644 index 00000000..85c7d41e --- /dev/null +++ b/3rd/libzmq/src/ipc_listener.hpp @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_IPC_LISTENER_HPP_INCLUDED__ +#define __ZMQ_IPC_LISTENER_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_IPC + +#include + +#include "fd.hpp" +#include "stream_listener_base.hpp" + +namespace zmq +{ +class ipc_listener_t ZMQ_FINAL : public stream_listener_base_t +{ + public: + ipc_listener_t (zmq::io_thread_t *io_thread_, + zmq::socket_base_t *socket_, + const options_t &options_); + + // Set address to listen on. + int set_local_address (const char *addr_); + + protected: + std::string get_socket_name (fd_t fd_, socket_end_t socket_end_) const; + + private: + // Handlers for I/O events. + void in_event (); + + // Filter new connections if the OS provides a mechanism to get + // the credentials of the peer process. Called from accept(). +#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED + bool filter (fd_t sock_); +#endif + + int close (); + + // Accept the new connection. Returns the file descriptor of the + // newly created connection. The function may return retired_fd + // if the connection was dropped while waiting in the listen backlog. + fd_t accept (); + + // True, if the underlying file for UNIX domain socket exists. + bool _has_file; + + // Name of the temporary directory (if any) that has the + // UNIX domain socket + std::string _tmp_socket_dirname; + + // Name of the file associated with the UNIX domain address. + std::string _filename; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ipc_listener_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/kqueue.cpp b/3rd/libzmq/src/kqueue.cpp new file mode 100644 index 00000000..53d82ac4 --- /dev/null +++ b/3rd/libzmq/src/kqueue.cpp @@ -0,0 +1,230 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "kqueue.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_KQUEUE + +#include +#include +#include +#include +#include +#include +#include + +#include "macros.hpp" +#include "kqueue.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" +#include "likely.hpp" + +// NetBSD defines (struct kevent).udata as intptr_t, everyone else +// as void *. +#if defined ZMQ_HAVE_NETBSD +#define kevent_udata_t intptr_t +#else +#define kevent_udata_t void * +#endif + +zmq::kqueue_t::kqueue_t (const zmq::thread_ctx_t &ctx_) : + worker_poller_base_t (ctx_) +{ + // Create event queue + kqueue_fd = kqueue (); + errno_assert (kqueue_fd != -1); +#ifdef HAVE_FORK + pid = getpid (); +#endif +} + +zmq::kqueue_t::~kqueue_t () +{ + stop_worker (); + close (kqueue_fd); +} + +void zmq::kqueue_t::kevent_add (fd_t fd_, short filter_, void *udata_) +{ + check_thread (); + struct kevent ev; + + EV_SET (&ev, fd_, filter_, EV_ADD, 0, 0, (kevent_udata_t) udata_); + int rc = kevent (kqueue_fd, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); +} + +void zmq::kqueue_t::kevent_delete (fd_t fd_, short filter_) +{ + struct kevent ev; + + EV_SET (&ev, fd_, filter_, EV_DELETE, 0, 0, 0); + int rc = kevent (kqueue_fd, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); +} + +zmq::kqueue_t::handle_t zmq::kqueue_t::add_fd (fd_t fd_, + i_poll_events *reactor_) +{ + check_thread (); + poll_entry_t *pe = new (std::nothrow) poll_entry_t; + alloc_assert (pe); + + pe->fd = fd_; + pe->flag_pollin = 0; + pe->flag_pollout = 0; + pe->reactor = reactor_; + + adjust_load (1); + + return pe; +} + +void zmq::kqueue_t::rm_fd (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = (poll_entry_t *) handle_; + if (pe->flag_pollin) + kevent_delete (pe->fd, EVFILT_READ); + if (pe->flag_pollout) + kevent_delete (pe->fd, EVFILT_WRITE); + pe->fd = retired_fd; + retired.push_back (pe); + + adjust_load (-1); +} + +void zmq::kqueue_t::set_pollin (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = (poll_entry_t *) handle_; + if (likely (!pe->flag_pollin)) { + pe->flag_pollin = true; + kevent_add (pe->fd, EVFILT_READ, pe); + } +} + +void zmq::kqueue_t::reset_pollin (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = (poll_entry_t *) handle_; + if (likely (pe->flag_pollin)) { + pe->flag_pollin = false; + kevent_delete (pe->fd, EVFILT_READ); + } +} + +void zmq::kqueue_t::set_pollout (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = (poll_entry_t *) handle_; + if (likely (!pe->flag_pollout)) { + pe->flag_pollout = true; + kevent_add (pe->fd, EVFILT_WRITE, pe); + } +} + +void zmq::kqueue_t::reset_pollout (handle_t handle_) +{ + check_thread (); + poll_entry_t *pe = (poll_entry_t *) handle_; + if (likely (pe->flag_pollout)) { + pe->flag_pollout = false; + kevent_delete (pe->fd, EVFILT_WRITE); + } +} + +void zmq::kqueue_t::stop () +{ +} + +int zmq::kqueue_t::max_fds () +{ + return -1; +} + +void zmq::kqueue_t::loop () +{ + while (true) { + // Execute any due timers. + int timeout = (int) execute_timers (); + + if (get_load () == 0) { + if (timeout == 0) + break; + + // TODO sleep for timeout + continue; + } + + // Wait for events. + struct kevent ev_buf[max_io_events]; + timespec ts = {timeout / 1000, (timeout % 1000) * 1000000}; + int n = kevent (kqueue_fd, NULL, 0, &ev_buf[0], max_io_events, + timeout ? &ts : NULL); +#ifdef HAVE_FORK + if (unlikely (pid != getpid ())) { + //printf("zmq::kqueue_t::loop aborting on forked child %d\n", (int)getpid()); + // simply exit the loop in a forked process. + return; + } +#endif + if (n == -1) { + errno_assert (errno == EINTR); + continue; + } + + for (int i = 0; i < n; i++) { + poll_entry_t *pe = (poll_entry_t *) ev_buf[i].udata; + + if (pe->fd == retired_fd) + continue; + if (ev_buf[i].flags & EV_EOF) + pe->reactor->in_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf[i].filter == EVFILT_WRITE) + pe->reactor->out_event (); + if (pe->fd == retired_fd) + continue; + if (ev_buf[i].filter == EVFILT_READ) + pe->reactor->in_event (); + } + + // Destroy retired event sources. + for (retired_t::iterator it = retired.begin (); it != retired.end (); + ++it) { + LIBZMQ_DELETE (*it); + } + retired.clear (); + } +} + +#endif diff --git a/3rd/libzmq/src/kqueue.hpp b/3rd/libzmq/src/kqueue.hpp new file mode 100644 index 00000000..002fbab3 --- /dev/null +++ b/3rd/libzmq/src/kqueue.hpp @@ -0,0 +1,109 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_KQUEUE_HPP_INCLUDED__ +#define __ZMQ_KQUEUE_HPP_INCLUDED__ + +// poller.hpp decides which polling mechanism to use. +#include "poller.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_KQUEUE + +#include +#include + +#include "ctx.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "poller_base.hpp" + +namespace zmq +{ +struct i_poll_events; + +// Implements socket polling mechanism using the BSD-specific +// kqueue interface. + +class kqueue_t ZMQ_FINAL : public worker_poller_base_t +{ + public: + typedef void *handle_t; + + kqueue_t (const thread_ctx_t &ctx_); + ~kqueue_t () ZMQ_FINAL; + + // "poller" concept. + handle_t add_fd (fd_t fd_, zmq::i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void stop (); + + static int max_fds (); + + private: + // Main event loop. + void loop () ZMQ_FINAL; + + // File descriptor referring to the kernel event queue. + fd_t kqueue_fd; + + // Adds the event to the kqueue. + void kevent_add (fd_t fd_, short filter_, void *udata_); + + // Deletes the event from the kqueue. + void kevent_delete (fd_t fd_, short filter_); + + struct poll_entry_t + { + fd_t fd; + bool flag_pollin; + bool flag_pollout; + zmq::i_poll_events *reactor; + }; + + // List of retired event sources. + typedef std::vector retired_t; + retired_t retired; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (kqueue_t) + +#ifdef HAVE_FORK + // the process that created this context. Used to detect forking. + pid_t pid; +#endif +}; + +typedef kqueue_t poller_t; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/lb.cpp b/3rd/libzmq/src/lb.cpp new file mode 100644 index 00000000..e766b4f2 --- /dev/null +++ b/3rd/libzmq/src/lb.cpp @@ -0,0 +1,180 @@ +/* + Copyright (c) 2007-2018 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 . +*/ + +#include "precompiled.hpp" +#include "lb.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::lb_t::lb_t () : _active (0), _current (0), _more (false), _dropping (false) +{ +} + +zmq::lb_t::~lb_t () +{ + zmq_assert (_pipes.empty ()); +} + +void zmq::lb_t::attach (pipe_t *pipe_) +{ + _pipes.push_back (pipe_); + activated (pipe_); +} + +void zmq::lb_t::pipe_terminated (pipe_t *pipe_) +{ + const pipes_t::size_type index = _pipes.index (pipe_); + + // If we are in the middle of multipart message and current pipe + // have disconnected, we have to drop the remainder of the message. + if (index == _current && _more) + _dropping = true; + + // Remove the pipe from the list; adjust number of active pipes + // accordingly. + if (index < _active) { + _active--; + _pipes.swap (index, _active); + if (_current == _active) + _current = 0; + } + _pipes.erase (pipe_); +} + +void zmq::lb_t::activated (pipe_t *pipe_) +{ + // Move the pipe to the list of active pipes. + _pipes.swap (_pipes.index (pipe_), _active); + _active++; +} + +int zmq::lb_t::send (msg_t *msg_) +{ + return sendpipe (msg_, NULL); +} + +int zmq::lb_t::sendpipe (msg_t *msg_, pipe_t **pipe_) +{ + // Drop the message if required. If we are at the end of the message + // switch back to non-dropping mode. + if (_dropping) { + _more = (msg_->flags () & msg_t::more) != 0; + _dropping = _more; + + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + return 0; + } + + while (_active > 0) { + if (_pipes[_current]->write (msg_)) { + if (pipe_) + *pipe_ = _pipes[_current]; + break; + } + + // If send fails for multi-part msg rollback other + // parts sent earlier and return EAGAIN. + // Application should handle this as suitable + if (_more) { + _pipes[_current]->rollback (); + // At this point the pipe is already being deallocated + // and the first N frames are unreachable (_outpipe is + // most likely already NULL so rollback won't actually do + // anything and they can't be un-written to deliver later). + // Return EFAULT to socket_base caller to drop current message + // and any other subsequent frames to avoid them being + // "stuck" and received when a new client reconnects, which + // would break atomicity of multi-part messages (in blocking mode + // socket_base just tries again and again to send the same message) + // Note that given dropping mode returns 0, the user will + // never know that the message could not be delivered, but + // can't really fix it without breaking backward compatibility. + // -2/EAGAIN will make sure socket_base caller does not re-enter + // immediately or after a short sleep in blocking mode. + _dropping = (msg_->flags () & msg_t::more) != 0; + _more = false; + errno = EAGAIN; + return -2; + } + + _active--; + if (_current < _active) + _pipes.swap (_current, _active); + else + _current = 0; + } + + // If there are no pipes we cannot send the message. + if (_active == 0) { + errno = EAGAIN; + return -1; + } + + // If it's final part of the message we can flush it downstream and + // continue round-robining (load balance). + _more = (msg_->flags () & msg_t::more) != 0; + if (!_more) { + _pipes[_current]->flush (); + + if (++_current >= _active) + _current = 0; + } + + // Detach the message from the data buffer. + const int rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +bool zmq::lb_t::has_out () +{ + // If one part of the message was already written we can definitely + // write the rest of the message. + if (_more) + return true; + + while (_active > 0) { + // Check whether a pipe has room for another message. + if (_pipes[_current]->check_write ()) + return true; + + // Deactivate the pipe. + _active--; + _pipes.swap (_current, _active); + if (_current == _active) + _current = 0; + } + + return false; +} diff --git a/3rd/libzmq/src/lb.hpp b/3rd/libzmq/src/lb.hpp new file mode 100644 index 00000000..b206967b --- /dev/null +++ b/3rd/libzmq/src/lb.hpp @@ -0,0 +1,85 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_LB_HPP_INCLUDED__ +#define __ZMQ_LB_HPP_INCLUDED__ + +#include "array.hpp" + +namespace zmq +{ +class msg_t; +class pipe_t; + +// This class manages a set of outbound pipes. On send it load balances +// messages fairly among the pipes. + +class lb_t +{ + public: + lb_t (); + ~lb_t (); + + void attach (pipe_t *pipe_); + void activated (pipe_t *pipe_); + void pipe_terminated (pipe_t *pipe_); + + int send (msg_t *msg_); + + // Sends a message and stores the pipe that was used in pipe_. + // It is possible for this function to return success but keep pipe_ + // unset if the rest of a multipart message to a terminated pipe is + // being dropped. For the first frame, this will never happen. + int sendpipe (msg_t *msg_, pipe_t **pipe_); + + bool has_out (); + + private: + // List of outbound pipes. + typedef array_t pipes_t; + pipes_t _pipes; + + // Number of active pipes. All the active pipes are located at the + // beginning of the pipes array. + pipes_t::size_type _active; + + // Points to the last pipe that the most recent message was sent to. + pipes_t::size_type _current; + + // True if last we are in the middle of a multipart message. + bool _more; + + // True if we are dropping current message. + bool _dropping; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (lb_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/libzmq.pc.in b/3rd/libzmq/src/libzmq.pc.in new file mode 100644 index 00000000..233bc3a0 --- /dev/null +++ b/3rd/libzmq/src/libzmq.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libzmq +Description: 0MQ c++ library +Version: @VERSION@ +Libs: -L${libdir} -lzmq +Libs.private: -lstdc++ @pkg_config_libs_private@ +Requires.private: @pkg_config_names_private@ +Cflags: -I${includedir} @pkg_config_defines@ diff --git a/3rd/libzmq/src/libzmq.vers b/3rd/libzmq/src/libzmq.vers new file mode 100644 index 00000000..9a2d4154 --- /dev/null +++ b/3rd/libzmq/src/libzmq.vers @@ -0,0 +1,4 @@ +{ + global: zmq_*; + local: *; +}; diff --git a/3rd/libzmq/src/likely.hpp b/3rd/libzmq/src/likely.hpp new file mode 100644 index 00000000..d6cb14d7 --- /dev/null +++ b/3rd/libzmq/src/likely.hpp @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_LIKELY_HPP_INCLUDED__ +#define __ZMQ_LIKELY_HPP_INCLUDED__ + +#if defined __GNUC__ +#define likely(x) __builtin_expect ((x), 1) +#define unlikely(x) __builtin_expect ((x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + + +#endif diff --git a/3rd/libzmq/src/macros.hpp b/3rd/libzmq/src/macros.hpp new file mode 100644 index 00000000..427a9e25 --- /dev/null +++ b/3rd/libzmq/src/macros.hpp @@ -0,0 +1,63 @@ + +/******************************************************************************/ +/* 0MQ Internal Use */ +/******************************************************************************/ + +#define LIBZMQ_UNUSED(object) (void) object +#define LIBZMQ_DELETE(p_object) \ + { \ + delete p_object; \ + p_object = 0; \ + } + +/******************************************************************************/ + +#if !defined ZMQ_NOEXCEPT +#if defined ZMQ_HAVE_NOEXCEPT +#define ZMQ_NOEXCEPT noexcept +#else +#define ZMQ_NOEXCEPT +#endif +#endif + +#if !defined ZMQ_OVERRIDE +#if defined ZMQ_HAVE_NOEXCEPT +#define ZMQ_OVERRIDE override +#else +#define ZMQ_OVERRIDE +#endif +#endif + +#if !defined ZMQ_FINAL +#if defined ZMQ_HAVE_NOEXCEPT +#define ZMQ_FINAL final +#else +#define ZMQ_FINAL +#endif +#endif + +#if !defined ZMQ_DEFAULT +#if defined ZMQ_HAVE_NOEXCEPT +#define ZMQ_DEFAULT = default; +#else +#define ZMQ_DEFAULT \ + { \ + } +#endif +#endif + +#if !defined ZMQ_NON_COPYABLE_NOR_MOVABLE +#if defined ZMQ_HAVE_NOEXCEPT +#define ZMQ_NON_COPYABLE_NOR_MOVABLE(classname) \ + public: \ + classname (const classname &) = delete; \ + classname &operator= (const classname &) = delete; \ + classname (classname &&) = delete; \ + classname &operator= (classname &&) = delete; +#else +#define ZMQ_NON_COPYABLE_NOR_MOVABLE(classname) \ + private: \ + classname (const classname &); \ + classname &operator= (const classname &); +#endif +#endif diff --git a/3rd/libzmq/src/mailbox.cpp b/3rd/libzmq/src/mailbox.cpp new file mode 100644 index 00000000..2fb0fe0c --- /dev/null +++ b/3rd/libzmq/src/mailbox.cpp @@ -0,0 +1,106 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "mailbox.hpp" +#include "err.hpp" + +zmq::mailbox_t::mailbox_t () +{ + // Get the pipe into passive state. That way, if the users starts by + // polling on the associated file descriptor it will get woken up when + // new command is posted. + const bool ok = _cpipe.check_read (); + zmq_assert (!ok); + _active = false; +} + +zmq::mailbox_t::~mailbox_t () +{ + // TODO: Retrieve and deallocate commands inside the _cpipe. + + // Work around problem that other threads might still be in our + // send() method, by waiting on the mutex before disappearing. + _sync.lock (); + _sync.unlock (); +} + +zmq::fd_t zmq::mailbox_t::get_fd () const +{ + return _signaler.get_fd (); +} + +void zmq::mailbox_t::send (const command_t &cmd_) +{ + _sync.lock (); + _cpipe.write (cmd_, false); + const bool ok = _cpipe.flush (); + _sync.unlock (); + if (!ok) + _signaler.send (); +} + +int zmq::mailbox_t::recv (command_t *cmd_, int timeout_) +{ + // Try to get the command straight away. + if (_active) { + if (_cpipe.read (cmd_)) + return 0; + + // If there are no more commands available, switch into passive state. + _active = false; + } + + // Wait for signal from the command sender. + int rc = _signaler.wait (timeout_); + if (rc == -1) { + errno_assert (errno == EAGAIN || errno == EINTR); + return -1; + } + + // Receive the signal. + rc = _signaler.recv_failable (); + if (rc == -1) { + errno_assert (errno == EAGAIN); + return -1; + } + + // Switch into active state. + _active = true; + + // Get a command. + const bool ok = _cpipe.read (cmd_); + zmq_assert (ok); + return 0; +} + +bool zmq::mailbox_t::valid () const +{ + return _signaler.valid (); +} diff --git a/3rd/libzmq/src/mailbox.hpp b/3rd/libzmq/src/mailbox.hpp new file mode 100644 index 00000000..0cd92d20 --- /dev/null +++ b/3rd/libzmq/src/mailbox.hpp @@ -0,0 +1,86 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_MAILBOX_HPP_INCLUDED__ +#define __ZMQ_MAILBOX_HPP_INCLUDED__ + +#include + +#include "signaler.hpp" +#include "fd.hpp" +#include "config.hpp" +#include "command.hpp" +#include "ypipe.hpp" +#include "mutex.hpp" +#include "i_mailbox.hpp" + +namespace zmq +{ +class mailbox_t ZMQ_FINAL : public i_mailbox +{ + public: + mailbox_t (); + ~mailbox_t (); + + fd_t get_fd () const; + void send (const command_t &cmd_); + int recv (command_t *cmd_, int timeout_); + + bool valid () const; + +#ifdef HAVE_FORK + // close the file descriptors in the signaller. This is used in a forked + // child process to close the file descriptors so that they do not interfere + // with the context in the parent process. + void forked () ZMQ_FINAL { _signaler.forked (); } +#endif + + private: + // The pipe to store actual commands. + typedef ypipe_t cpipe_t; + cpipe_t _cpipe; + + // Signaler to pass signals from writer thread to reader thread. + signaler_t _signaler; + + // There's only one thread receiving from the mailbox, but there + // is arbitrary number of threads sending. Given that ypipe requires + // synchronised access on both of its endpoints, we have to synchronise + // the sending side. + mutex_t _sync; + + // True if the underlying pipe is active, ie. when we are allowed to + // read commands from it. + bool _active; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (mailbox_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/mailbox_safe.cpp b/3rd/libzmq/src/mailbox_safe.cpp new file mode 100644 index 00000000..f49fa957 --- /dev/null +++ b/3rd/libzmq/src/mailbox_safe.cpp @@ -0,0 +1,125 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "mailbox_safe.hpp" +#include "clock.hpp" +#include "err.hpp" + +#include + +zmq::mailbox_safe_t::mailbox_safe_t (mutex_t *sync_) : _sync (sync_) +{ + // Get the pipe into passive state. That way, if the users starts by + // polling on the associated file descriptor it will get woken up when + // new command is posted. + const bool ok = _cpipe.check_read (); + zmq_assert (!ok); +} + +zmq::mailbox_safe_t::~mailbox_safe_t () +{ + // TODO: Retrieve and deallocate commands inside the cpipe. + + // Work around problem that other threads might still be in our + // send() method, by waiting on the mutex before disappearing. + _sync->lock (); + _sync->unlock (); +} + +void zmq::mailbox_safe_t::add_signaler (signaler_t *signaler_) +{ + _signalers.push_back (signaler_); +} + +void zmq::mailbox_safe_t::remove_signaler (signaler_t *signaler_) +{ + // TODO: make a copy of array and signal outside the lock + const std::vector::iterator end = _signalers.end (); + const std::vector::iterator it = + std::find (_signalers.begin (), end, signaler_); + + if (it != end) + _signalers.erase (it); +} + +void zmq::mailbox_safe_t::clear_signalers () +{ + _signalers.clear (); +} + +void zmq::mailbox_safe_t::send (const command_t &cmd_) +{ + _sync->lock (); + _cpipe.write (cmd_, false); + const bool ok = _cpipe.flush (); + + if (!ok) { + _cond_var.broadcast (); + + for (std::vector::iterator it = _signalers.begin (), + end = _signalers.end (); + it != end; ++it) { + (*it)->send (); + } + } + + _sync->unlock (); +} + +int zmq::mailbox_safe_t::recv (command_t *cmd_, int timeout_) +{ + // Try to get the command straight away. + if (_cpipe.read (cmd_)) + return 0; + + // If the timeout is zero, it will be quicker to release the lock, giving other a chance to send a command + // and immediately relock it. + if (timeout_ == 0) { + _sync->unlock (); + _sync->lock (); + } else { + // Wait for signal from the command sender. + const int rc = _cond_var.wait (_sync, timeout_); + if (rc == -1) { + errno_assert (errno == EAGAIN || errno == EINTR); + return -1; + } + } + + // Another thread may already fetch the command + const bool ok = _cpipe.read (cmd_); + + if (!ok) { + errno = EAGAIN; + return -1; + } + + return 0; +} diff --git a/3rd/libzmq/src/mailbox_safe.hpp b/3rd/libzmq/src/mailbox_safe.hpp new file mode 100644 index 00000000..04dbf140 --- /dev/null +++ b/3rd/libzmq/src/mailbox_safe.hpp @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_MAILBOX_SAFE_HPP_INCLUDED__ +#define __ZMQ_MAILBOX_SAFE_HPP_INCLUDED__ + +#include +#include + +#include "signaler.hpp" +#include "fd.hpp" +#include "config.hpp" +#include "command.hpp" +#include "ypipe.hpp" +#include "mutex.hpp" +#include "i_mailbox.hpp" +#include "condition_variable.hpp" + +namespace zmq +{ +class mailbox_safe_t ZMQ_FINAL : public i_mailbox +{ + public: + mailbox_safe_t (mutex_t *sync_); + ~mailbox_safe_t (); + + void send (const command_t &cmd_); + int recv (command_t *cmd_, int timeout_); + + // Add signaler to mailbox which will be called when a message is ready + void add_signaler (signaler_t *signaler_); + void remove_signaler (signaler_t *signaler_); + void clear_signalers (); + +#ifdef HAVE_FORK + // close the file descriptors in the signaller. This is used in a forked + // child process to close the file descriptors so that they do not interfere + // with the context in the parent process. + void forked () ZMQ_FINAL + { + // TODO: call fork on the condition variable + } +#endif + + private: + // The pipe to store actual commands. + typedef ypipe_t cpipe_t; + cpipe_t _cpipe; + + // Condition variable to pass signals from writer thread to reader thread. + condition_variable_t _cond_var; + + // Synchronize access to the mailbox from receivers and senders + mutex_t *const _sync; + + std::vector _signalers; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (mailbox_safe_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/mechanism.cpp b/3rd/libzmq/src/mechanism.cpp new file mode 100644 index 00000000..0d46c11e --- /dev/null +++ b/3rd/libzmq/src/mechanism.cpp @@ -0,0 +1,370 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "mechanism.hpp" +#include "options.hpp" +#include "msg.hpp" +#include "err.hpp" +#include "wire.hpp" +#include "session_base.hpp" + +zmq::mechanism_t::mechanism_t (const options_t &options_) : options (options_) +{ +} + +zmq::mechanism_t::~mechanism_t () +{ +} + +void zmq::mechanism_t::set_peer_routing_id (const void *id_ptr_, + size_t id_size_) +{ + _routing_id.set (static_cast (id_ptr_), id_size_); +} + +void zmq::mechanism_t::peer_routing_id (msg_t *msg_) +{ + const int rc = msg_->init_size (_routing_id.size ()); + errno_assert (rc == 0); + memcpy (msg_->data (), _routing_id.data (), _routing_id.size ()); + msg_->set_flags (msg_t::routing_id); +} + +void zmq::mechanism_t::set_user_id (const void *user_id_, size_t size_) +{ + _user_id.set (static_cast (user_id_), size_); + _zap_properties.ZMQ_MAP_INSERT_OR_EMPLACE ( + std::string (ZMQ_MSG_PROPERTY_USER_ID), + std::string (reinterpret_cast (user_id_), size_)); +} + +const zmq::blob_t &zmq::mechanism_t::get_user_id () const +{ + return _user_id; +} + +const char socket_type_pair[] = "PAIR"; +const char socket_type_pub[] = "PUB"; +const char socket_type_sub[] = "SUB"; +const char socket_type_req[] = "REQ"; +const char socket_type_rep[] = "REP"; +const char socket_type_dealer[] = "DEALER"; +const char socket_type_router[] = "ROUTER"; +const char socket_type_pull[] = "PULL"; +const char socket_type_push[] = "PUSH"; +const char socket_type_xpub[] = "XPUB"; +const char socket_type_xsub[] = "XSUB"; +const char socket_type_stream[] = "STREAM"; +#ifdef ZMQ_BUILD_DRAFT_API +const char socket_type_server[] = "SERVER"; +const char socket_type_client[] = "CLIENT"; +const char socket_type_radio[] = "RADIO"; +const char socket_type_dish[] = "DISH"; +const char socket_type_gather[] = "GATHER"; +const char socket_type_scatter[] = "SCATTER"; +const char socket_type_dgram[] = "DGRAM"; +const char socket_type_peer[] = "PEER"; +const char socket_type_channel[] = "CHANNEL"; +#endif + +const char *zmq::mechanism_t::socket_type_string (int socket_type_) +{ + // TODO the order must of the names must correspond to the values resp. order of ZMQ_* socket type definitions in zmq.h! + static const char *names[] = {socket_type_pair, socket_type_pub, + socket_type_sub, socket_type_req, + socket_type_rep, socket_type_dealer, + socket_type_router, socket_type_pull, + socket_type_push, socket_type_xpub, + socket_type_xsub, socket_type_stream, +#ifdef ZMQ_BUILD_DRAFT_API + socket_type_server, socket_type_client, + socket_type_radio, socket_type_dish, + socket_type_gather, socket_type_scatter, + socket_type_dgram, socket_type_peer, + socket_type_channel +#endif + }; + static const size_t names_count = sizeof (names) / sizeof (names[0]); + zmq_assert (socket_type_ >= 0 + && socket_type_ < static_cast (names_count)); + return names[socket_type_]; +} + +const size_t name_len_size = sizeof (unsigned char); +const size_t value_len_size = sizeof (uint32_t); + +static size_t property_len (size_t name_len_, size_t value_len_) +{ + return name_len_size + name_len_ + value_len_size + value_len_; +} + +static size_t name_len (const char *name_) +{ + const size_t name_len = strlen (name_); + zmq_assert (name_len <= UCHAR_MAX); + return name_len; +} + +size_t zmq::mechanism_t::add_property (unsigned char *ptr_, + size_t ptr_capacity_, + const char *name_, + const void *value_, + size_t value_len_) +{ + const size_t name_len = ::name_len (name_); + const size_t total_len = ::property_len (name_len, value_len_); + zmq_assert (total_len <= ptr_capacity_); + + *ptr_ = static_cast (name_len); + ptr_ += name_len_size; + memcpy (ptr_, name_, name_len); + ptr_ += name_len; + zmq_assert (value_len_ <= 0x7FFFFFFF); + put_uint32 (ptr_, static_cast (value_len_)); + ptr_ += value_len_size; + memcpy (ptr_, value_, value_len_); + + return total_len; +} + +size_t zmq::mechanism_t::property_len (const char *name_, size_t value_len_) +{ + return ::property_len (name_len (name_), value_len_); +} + +#define ZMTP_PROPERTY_SOCKET_TYPE "Socket-Type" +#define ZMTP_PROPERTY_IDENTITY "Identity" + +size_t zmq::mechanism_t::add_basic_properties (unsigned char *ptr_, + size_t ptr_capacity_) const +{ + unsigned char *ptr = ptr_; + + // Add socket type property + const char *socket_type = socket_type_string (options.type); + ptr += add_property (ptr, ptr_capacity_, ZMTP_PROPERTY_SOCKET_TYPE, + socket_type, strlen (socket_type)); + + // Add identity (aka routing id) property + if (options.type == ZMQ_REQ || options.type == ZMQ_DEALER + || options.type == ZMQ_ROUTER) { + ptr += add_property (ptr, ptr_capacity_ - (ptr - ptr_), + ZMTP_PROPERTY_IDENTITY, options.routing_id, + options.routing_id_size); + } + + + for (std::map::const_iterator + it = options.app_metadata.begin (), + end = options.app_metadata.end (); + it != end; ++it) { + ptr += + add_property (ptr, ptr_capacity_ - (ptr - ptr_), it->first.c_str (), + it->second.c_str (), strlen (it->second.c_str ())); + } + + return ptr - ptr_; +} + +size_t zmq::mechanism_t::basic_properties_len () const +{ + const char *socket_type = socket_type_string (options.type); + size_t meta_len = 0; + + for (std::map::const_iterator + it = options.app_metadata.begin (), + end = options.app_metadata.end (); + it != end; ++it) { + meta_len += + property_len (it->first.c_str (), strlen (it->second.c_str ())); + } + + return property_len (ZMTP_PROPERTY_SOCKET_TYPE, strlen (socket_type)) + + meta_len + + ((options.type == ZMQ_REQ || options.type == ZMQ_DEALER + || options.type == ZMQ_ROUTER) + ? property_len (ZMTP_PROPERTY_IDENTITY, options.routing_id_size) + : 0); +} + +void zmq::mechanism_t::make_command_with_basic_properties ( + msg_t *msg_, const char *prefix_, size_t prefix_len_) const +{ + const size_t command_size = prefix_len_ + basic_properties_len (); + const int rc = msg_->init_size (command_size); + errno_assert (rc == 0); + + unsigned char *ptr = static_cast (msg_->data ()); + + // Add prefix + memcpy (ptr, prefix_, prefix_len_); + ptr += prefix_len_; + + add_basic_properties ( + ptr, command_size - (ptr - static_cast (msg_->data ()))); +} + +int zmq::mechanism_t::parse_metadata (const unsigned char *ptr_, + size_t length_, + bool zap_flag_) +{ + size_t bytes_left = length_; + + while (bytes_left > 1) { + const size_t name_length = static_cast (*ptr_); + ptr_ += name_len_size; + bytes_left -= name_len_size; + if (bytes_left < name_length) + break; + + const std::string name = + std::string (reinterpret_cast (ptr_), name_length); + ptr_ += name_length; + bytes_left -= name_length; + if (bytes_left < value_len_size) + break; + + const size_t value_length = static_cast (get_uint32 (ptr_)); + ptr_ += value_len_size; + bytes_left -= value_len_size; + if (bytes_left < value_length) + break; + + const uint8_t *value = ptr_; + ptr_ += value_length; + bytes_left -= value_length; + + if (name == ZMTP_PROPERTY_IDENTITY && options.recv_routing_id) + set_peer_routing_id (value, value_length); + else if (name == ZMTP_PROPERTY_SOCKET_TYPE) { + if (!check_socket_type (reinterpret_cast (value), + value_length)) { + errno = EINVAL; + return -1; + } + } else { + const int rc = property (name, value, value_length); + if (rc == -1) + return -1; + } + (zap_flag_ ? _zap_properties : _zmtp_properties) + .ZMQ_MAP_INSERT_OR_EMPLACE ( + name, + std::string (reinterpret_cast (value), value_length)); + } + if (bytes_left > 0) { + errno = EPROTO; + return -1; + } + return 0; +} + +int zmq::mechanism_t::property (const std::string & /* name_ */, + const void * /* value_ */, + size_t /* length_ */) +{ + // Default implementation does not check + // property values and returns 0 to signal success. + return 0; +} + +template +static bool strequals (const char *actual_type_, + const size_t actual_len_, + const char (&expected_type_)[N]) +{ + return actual_len_ == N - 1 + && memcmp (actual_type_, expected_type_, N - 1) == 0; +} + +bool zmq::mechanism_t::check_socket_type (const char *type_, + const size_t len_) const +{ + switch (options.type) { + case ZMQ_REQ: + return strequals (type_, len_, socket_type_rep) + || strequals (type_, len_, socket_type_router); + case ZMQ_REP: + return strequals (type_, len_, socket_type_req) + || strequals (type_, len_, socket_type_dealer); + case ZMQ_DEALER: + return strequals (type_, len_, socket_type_rep) + || strequals (type_, len_, socket_type_dealer) + || strequals (type_, len_, socket_type_router); + case ZMQ_ROUTER: + return strequals (type_, len_, socket_type_req) + || strequals (type_, len_, socket_type_dealer) + || strequals (type_, len_, socket_type_router); + case ZMQ_PUSH: + return strequals (type_, len_, socket_type_pull); + case ZMQ_PULL: + return strequals (type_, len_, socket_type_push); + case ZMQ_PUB: + return strequals (type_, len_, socket_type_sub) + || strequals (type_, len_, socket_type_xsub); + case ZMQ_SUB: + return strequals (type_, len_, socket_type_pub) + || strequals (type_, len_, socket_type_xpub); + case ZMQ_XPUB: + return strequals (type_, len_, socket_type_sub) + || strequals (type_, len_, socket_type_xsub); + case ZMQ_XSUB: + return strequals (type_, len_, socket_type_pub) + || strequals (type_, len_, socket_type_xpub); + case ZMQ_PAIR: + return strequals (type_, len_, socket_type_pair); +#ifdef ZMQ_BUILD_DRAFT_API + case ZMQ_SERVER: + return strequals (type_, len_, socket_type_client); + case ZMQ_CLIENT: + return strequals (type_, len_, socket_type_server); + case ZMQ_RADIO: + return strequals (type_, len_, socket_type_dish); + case ZMQ_DISH: + return strequals (type_, len_, socket_type_radio); + case ZMQ_GATHER: + return strequals (type_, len_, socket_type_scatter); + case ZMQ_SCATTER: + return strequals (type_, len_, socket_type_gather); + case ZMQ_DGRAM: + return strequals (type_, len_, socket_type_dgram); + case ZMQ_PEER: + return strequals (type_, len_, socket_type_peer); + case ZMQ_CHANNEL: + return strequals (type_, len_, socket_type_channel); +#endif + default: + break; + } + return false; +} diff --git a/3rd/libzmq/src/mechanism.hpp b/3rd/libzmq/src/mechanism.hpp new file mode 100644 index 00000000..13f0ffbe --- /dev/null +++ b/3rd/libzmq/src/mechanism.hpp @@ -0,0 +1,151 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_MECHANISM_HPP_INCLUDED__ +#define __ZMQ_MECHANISM_HPP_INCLUDED__ + +#include "stdint.hpp" +#include "options.hpp" +#include "blob.hpp" +#include "metadata.hpp" + +namespace zmq +{ +class msg_t; +class session_base_t; + +// Abstract class representing security mechanism. +// Different mechanism extends this class. + +class mechanism_t +{ + public: + enum status_t + { + handshaking, + ready, + error + }; + + mechanism_t (const options_t &options_); + + virtual ~mechanism_t (); + + // Prepare next handshake command that is to be sent to the peer. + virtual int next_handshake_command (msg_t *msg_) = 0; + + // Process the handshake command received from the peer. + virtual int process_handshake_command (msg_t *msg_) = 0; + + virtual int encode (msg_t *) { return 0; } + + virtual int decode (msg_t *) { return 0; } + + // Notifies mechanism about availability of ZAP message. + virtual int zap_msg_available () { return 0; } + + // Returns the status of this mechanism. + virtual status_t status () const = 0; + + void set_peer_routing_id (const void *id_ptr_, size_t id_size_); + + void peer_routing_id (msg_t *msg_); + + void set_user_id (const void *user_id_, size_t size_); + + const blob_t &get_user_id () const; + + const metadata_t::dict_t &get_zmtp_properties () const + { + return _zmtp_properties; + } + + const metadata_t::dict_t &get_zap_properties () const + { + return _zap_properties; + } + + protected: + // Only used to identify the socket for the Socket-Type + // property in the wire protocol. + static const char *socket_type_string (int socket_type_); + + static size_t add_property (unsigned char *ptr_, + size_t ptr_capacity_, + const char *name_, + const void *value_, + size_t value_len_); + static size_t property_len (const char *name_, size_t value_len_); + + size_t add_basic_properties (unsigned char *ptr_, + size_t ptr_capacity_) const; + size_t basic_properties_len () const; + + void make_command_with_basic_properties (msg_t *msg_, + const char *prefix_, + size_t prefix_len_) const; + + // Parses a metadata. + // Metadata consists of a list of properties consisting of + // name and value as size-specified strings. + // Returns 0 on success and -1 on error, in which case errno is set. + int parse_metadata (const unsigned char *ptr_, + size_t length_, + bool zap_flag_ = false); + + // This is called by parse_property method whenever it + // parses a new property. The function should return 0 + // on success and -1 on error, in which case it should + // set errno. Signaling error prevents parser from + // parsing remaining data. + // Derived classes are supposed to override this + // method to handle custom processing. + virtual int + property (const std::string &name_, const void *value_, size_t length_); + + const options_t options; + + private: + // Properties received from ZMTP peer. + metadata_t::dict_t _zmtp_properties; + + // Properties received from ZAP server. + metadata_t::dict_t _zap_properties; + + blob_t _routing_id; + + blob_t _user_id; + + // Returns true iff socket associated with the mechanism + // is compatible with a given socket type 'type_'. + bool check_socket_type (const char *type_, size_t len_) const; +}; +} + +#endif diff --git a/3rd/libzmq/src/mechanism_base.cpp b/3rd/libzmq/src/mechanism_base.cpp new file mode 100644 index 00000000..d51b3695 --- /dev/null +++ b/3rd/libzmq/src/mechanism_base.cpp @@ -0,0 +1,82 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "mechanism_base.hpp" +#include "session_base.hpp" + +zmq::mechanism_base_t::mechanism_base_t (session_base_t *const session_, + const options_t &options_) : + mechanism_t (options_), + session (session_) +{ +} + +int zmq::mechanism_base_t::check_basic_command_structure (msg_t *msg_) const +{ + if (msg_->size () <= 1 + || msg_->size () <= (static_cast (msg_->data ()))[0]) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED); + errno = EPROTO; + return -1; + } + return 0; +} + +void zmq::mechanism_base_t::handle_error_reason (const char *error_reason_, + size_t error_reason_len_) +{ + const size_t status_code_len = 3; + const char zero_digit = '0'; + const size_t significant_digit_index = 0; + const size_t first_zero_digit_index = 1; + const size_t second_zero_digit_index = 2; + const int factor = 100; + if (error_reason_len_ == status_code_len + && error_reason_[first_zero_digit_index] == zero_digit + && error_reason_[second_zero_digit_index] == zero_digit + && error_reason_[significant_digit_index] >= '3' + && error_reason_[significant_digit_index] <= '5') { + // it is a ZAP error status code (300, 400 or 500), so emit an authentication failure event + session->get_socket ()->event_handshake_failed_auth ( + session->get_endpoint (), + (error_reason_[significant_digit_index] - zero_digit) * factor); + } else { + // this is a violation of the ZAP protocol + // TODO zmq_assert in this case? + } +} + +bool zmq::mechanism_base_t::zap_required () const +{ + return !options.zap_domain.empty (); +} diff --git a/3rd/libzmq/src/mechanism_base.hpp b/3rd/libzmq/src/mechanism_base.hpp new file mode 100644 index 00000000..767f1803 --- /dev/null +++ b/3rd/libzmq/src/mechanism_base.hpp @@ -0,0 +1,55 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_MECHANISM_BASE_HPP_INCLUDED__ +#define __ZMQ_MECHANISM_BASE_HPP_INCLUDED__ + +#include "mechanism.hpp" + +namespace zmq +{ +class msg_t; + +class mechanism_base_t : public mechanism_t +{ + protected: + mechanism_base_t (session_base_t *session_, const options_t &options_); + + session_base_t *const session; + + int check_basic_command_structure (msg_t *msg_) const; + + void handle_error_reason (const char *error_reason_, + size_t error_reason_len_); + + bool zap_required () const; +}; +} + +#endif diff --git a/3rd/libzmq/src/metadata.cpp b/3rd/libzmq/src/metadata.cpp new file mode 100644 index 00000000..45a16a60 --- /dev/null +++ b/3rd/libzmq/src/metadata.cpp @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "metadata.hpp" + +zmq::metadata_t::metadata_t (const dict_t &dict_) : _ref_cnt (1), _dict (dict_) +{ +} + +const char *zmq::metadata_t::get (const std::string &property_) const +{ + const dict_t::const_iterator it = _dict.find (property_); + if (it == _dict.end ()) { + /** \todo remove this when support for the deprecated name "Identity" is dropped */ + if (property_ == "Identity") + return get (ZMQ_MSG_PROPERTY_ROUTING_ID); + + return NULL; + } + return it->second.c_str (); +} + +void zmq::metadata_t::add_ref () +{ + _ref_cnt.add (1); +} + +bool zmq::metadata_t::drop_ref () +{ + return !_ref_cnt.sub (1); +} diff --git a/3rd/libzmq/src/metadata.hpp b/3rd/libzmq/src/metadata.hpp new file mode 100644 index 00000000..b8c14513 --- /dev/null +++ b/3rd/libzmq/src/metadata.hpp @@ -0,0 +1,68 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_METADATA_HPP_INCLUDED__ +#define __ZMQ_METADATA_HPP_INCLUDED__ + +#include +#include + +#include "atomic_counter.hpp" + +namespace zmq +{ +class metadata_t +{ + public: + typedef std::map dict_t; + + metadata_t (const dict_t &dict_); + + // Returns pointer to property value or NULL if + // property is not found. + const char *get (const std::string &property_) const; + + void add_ref (); + + // Drop reference. Returns true iff the reference + // counter drops to zero. + bool drop_ref (); + + private: + // Reference counter. + atomic_counter_t _ref_cnt; + + // Dictionary holding metadata. + const dict_t _dict; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (metadata_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/msg.cpp b/3rd/libzmq/src/msg.cpp new file mode 100644 index 00000000..2116d1e4 --- /dev/null +++ b/3rd/libzmq/src/msg.cpp @@ -0,0 +1,725 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "compat.hpp" +#include "macros.hpp" +#include "msg.hpp" + +#include +#include +#include + +#include "stdint.hpp" +#include "likely.hpp" +#include "metadata.hpp" +#include "err.hpp" + +// Check whether the sizes of public representation of the message (zmq_msg_t) +// and private representation of the message (zmq::msg_t) match. + +typedef char + zmq_msg_size_check[2 * ((sizeof (zmq::msg_t) == sizeof (zmq_msg_t)) != 0) + - 1]; + +bool zmq::msg_t::check () const +{ + return _u.base.type >= type_min && _u.base.type <= type_max; +} + +int zmq::msg_t::init (void *data_, + size_t size_, + msg_free_fn *ffn_, + void *hint_, + content_t *content_) +{ + if (size_ < max_vsm_size) { + const int rc = init_size (size_); + + if (rc != -1) { + memcpy (data (), data_, size_); + return 0; + } + return -1; + } + if (content_) { + return init_external_storage (content_, data_, size_, ffn_, hint_); + } + return init_data (data_, size_, ffn_, hint_); +} + +int zmq::msg_t::init () +{ + _u.vsm.metadata = NULL; + _u.vsm.type = type_vsm; + _u.vsm.flags = 0; + _u.vsm.size = 0; + _u.vsm.group.sgroup.group[0] = '\0'; + _u.vsm.group.type = group_type_short; + _u.vsm.routing_id = 0; + return 0; +} + +int zmq::msg_t::init_size (size_t size_) +{ + if (size_ <= max_vsm_size) { + _u.vsm.metadata = NULL; + _u.vsm.type = type_vsm; + _u.vsm.flags = 0; + _u.vsm.size = static_cast (size_); + _u.vsm.group.sgroup.group[0] = '\0'; + _u.vsm.group.type = group_type_short; + _u.vsm.routing_id = 0; + } else { + _u.lmsg.metadata = NULL; + _u.lmsg.type = type_lmsg; + _u.lmsg.flags = 0; + _u.lmsg.group.sgroup.group[0] = '\0'; + _u.lmsg.group.type = group_type_short; + _u.lmsg.routing_id = 0; + _u.lmsg.content = NULL; + if (sizeof (content_t) + size_ > size_) + _u.lmsg.content = + static_cast (malloc (sizeof (content_t) + size_)); + if (unlikely (!_u.lmsg.content)) { + errno = ENOMEM; + return -1; + } + + _u.lmsg.content->data = _u.lmsg.content + 1; + _u.lmsg.content->size = size_; + _u.lmsg.content->ffn = NULL; + _u.lmsg.content->hint = NULL; + new (&_u.lmsg.content->refcnt) zmq::atomic_counter_t (); + } + return 0; +} + +int zmq::msg_t::init_buffer (const void *buf_, size_t size_) +{ + const int rc = init_size (size_); + if (unlikely (rc < 0)) { + return -1; + } + if (size_) { + // NULL and zero size is allowed + assert (NULL != buf_); + memcpy (data (), buf_, size_); + } + return 0; +} + +int zmq::msg_t::init_external_storage (content_t *content_, + void *data_, + size_t size_, + msg_free_fn *ffn_, + void *hint_) +{ + zmq_assert (NULL != data_); + zmq_assert (NULL != content_); + + _u.zclmsg.metadata = NULL; + _u.zclmsg.type = type_zclmsg; + _u.zclmsg.flags = 0; + _u.zclmsg.group.sgroup.group[0] = '\0'; + _u.zclmsg.group.type = group_type_short; + _u.zclmsg.routing_id = 0; + + _u.zclmsg.content = content_; + _u.zclmsg.content->data = data_; + _u.zclmsg.content->size = size_; + _u.zclmsg.content->ffn = ffn_; + _u.zclmsg.content->hint = hint_; + new (&_u.zclmsg.content->refcnt) zmq::atomic_counter_t (); + + return 0; +} + +int zmq::msg_t::init_data (void *data_, + size_t size_, + msg_free_fn *ffn_, + void *hint_) +{ + // If data is NULL and size is not 0, a segfault + // would occur once the data is accessed + zmq_assert (data_ != NULL || size_ == 0); + + // Initialize constant message if there's no need to deallocate + if (ffn_ == NULL) { + _u.cmsg.metadata = NULL; + _u.cmsg.type = type_cmsg; + _u.cmsg.flags = 0; + _u.cmsg.data = data_; + _u.cmsg.size = size_; + _u.cmsg.group.sgroup.group[0] = '\0'; + _u.cmsg.group.type = group_type_short; + _u.cmsg.routing_id = 0; + } else { + _u.lmsg.metadata = NULL; + _u.lmsg.type = type_lmsg; + _u.lmsg.flags = 0; + _u.lmsg.group.sgroup.group[0] = '\0'; + _u.lmsg.group.type = group_type_short; + _u.lmsg.routing_id = 0; + _u.lmsg.content = + static_cast (malloc (sizeof (content_t))); + if (!_u.lmsg.content) { + errno = ENOMEM; + return -1; + } + + _u.lmsg.content->data = data_; + _u.lmsg.content->size = size_; + _u.lmsg.content->ffn = ffn_; + _u.lmsg.content->hint = hint_; + new (&_u.lmsg.content->refcnt) zmq::atomic_counter_t (); + } + return 0; +} + +int zmq::msg_t::init_delimiter () +{ + _u.delimiter.metadata = NULL; + _u.delimiter.type = type_delimiter; + _u.delimiter.flags = 0; + _u.delimiter.group.sgroup.group[0] = '\0'; + _u.delimiter.group.type = group_type_short; + _u.delimiter.routing_id = 0; + return 0; +} + +int zmq::msg_t::init_join () +{ + _u.base.metadata = NULL; + _u.base.type = type_join; + _u.base.flags = 0; + _u.base.group.sgroup.group[0] = '\0'; + _u.base.group.type = group_type_short; + _u.base.routing_id = 0; + return 0; +} + +int zmq::msg_t::init_leave () +{ + _u.base.metadata = NULL; + _u.base.type = type_leave; + _u.base.flags = 0; + _u.base.group.sgroup.group[0] = '\0'; + _u.base.group.type = group_type_short; + _u.base.routing_id = 0; + return 0; +} + +int zmq::msg_t::init_subscribe (const size_t size_, const unsigned char *topic_) +{ + int rc = init_size (size_); + if (rc == 0) { + set_flags (zmq::msg_t::subscribe); + + // We explicitly allow a NULL subscription with size zero + if (size_) { + assert (topic_); + memcpy (data (), topic_, size_); + } + } + return rc; +} + +int zmq::msg_t::init_cancel (const size_t size_, const unsigned char *topic_) +{ + int rc = init_size (size_); + if (rc == 0) { + set_flags (zmq::msg_t::cancel); + + // We explicitly allow a NULL subscription with size zero + if (size_) { + assert (topic_); + memcpy (data (), topic_, size_); + } + } + return rc; +} + +int zmq::msg_t::close () +{ + // Check the validity of the message. + if (unlikely (!check ())) { + errno = EFAULT; + return -1; + } + + if (_u.base.type == type_lmsg) { + // If the content is not shared, or if it is shared and the reference + // count has dropped to zero, deallocate it. + if (!(_u.lmsg.flags & msg_t::shared) + || !_u.lmsg.content->refcnt.sub (1)) { + // We used "placement new" operator to initialize the reference + // counter so we call the destructor explicitly now. + _u.lmsg.content->refcnt.~atomic_counter_t (); + + if (_u.lmsg.content->ffn) + _u.lmsg.content->ffn (_u.lmsg.content->data, + _u.lmsg.content->hint); + free (_u.lmsg.content); + } + } + + if (is_zcmsg ()) { + zmq_assert (_u.zclmsg.content->ffn); + + // If the content is not shared, or if it is shared and the reference + // count has dropped to zero, deallocate it. + if (!(_u.zclmsg.flags & msg_t::shared) + || !_u.zclmsg.content->refcnt.sub (1)) { + // We used "placement new" operator to initialize the reference + // counter so we call the destructor explicitly now. + _u.zclmsg.content->refcnt.~atomic_counter_t (); + + _u.zclmsg.content->ffn (_u.zclmsg.content->data, + _u.zclmsg.content->hint); + } + } + + if (_u.base.metadata != NULL) { + if (_u.base.metadata->drop_ref ()) { + LIBZMQ_DELETE (_u.base.metadata); + } + _u.base.metadata = NULL; + } + + if (_u.base.group.type == group_type_long) { + if (!_u.base.group.lgroup.content->refcnt.sub (1)) { + // We used "placement new" operator to initialize the reference + // counter so we call the destructor explicitly now. + _u.base.group.lgroup.content->refcnt.~atomic_counter_t (); + + free (_u.base.group.lgroup.content); + } + } + + // Make the message invalid. + _u.base.type = 0; + + return 0; +} + +int zmq::msg_t::move (msg_t &src_) +{ + // Check the validity of the source. + if (unlikely (!src_.check ())) { + errno = EFAULT; + return -1; + } + + int rc = close (); + if (unlikely (rc < 0)) + return rc; + + *this = src_; + + rc = src_.init (); + if (unlikely (rc < 0)) + return rc; + + return 0; +} + +int zmq::msg_t::copy (msg_t &src_) +{ + // Check the validity of the source. + if (unlikely (!src_.check ())) { + errno = EFAULT; + return -1; + } + + const int rc = close (); + if (unlikely (rc < 0)) + return rc; + + // The initial reference count, when a non-shared message is initially + // shared (between the original and the copy we create here). + const atomic_counter_t::integer_t initial_shared_refcnt = 2; + + if (src_.is_lmsg () || src_.is_zcmsg ()) { + // One reference is added to shared messages. Non-shared messages + // are turned into shared messages. + if (src_.flags () & msg_t::shared) + src_.refcnt ()->add (1); + else { + src_.set_flags (msg_t::shared); + src_.refcnt ()->set (initial_shared_refcnt); + } + } + + if (src_._u.base.metadata != NULL) + src_._u.base.metadata->add_ref (); + + if (src_._u.base.group.type == group_type_long) + src_._u.base.group.lgroup.content->refcnt.add (1); + + *this = src_; + + return 0; +} + +void *zmq::msg_t::data () +{ + // Check the validity of the message. + zmq_assert (check ()); + + switch (_u.base.type) { + case type_vsm: + return _u.vsm.data; + case type_lmsg: + return _u.lmsg.content->data; + case type_cmsg: + return _u.cmsg.data; + case type_zclmsg: + return _u.zclmsg.content->data; + default: + zmq_assert (false); + return NULL; + } +} + +size_t zmq::msg_t::size () const +{ + // Check the validity of the message. + zmq_assert (check ()); + + switch (_u.base.type) { + case type_vsm: + return _u.vsm.size; + case type_lmsg: + return _u.lmsg.content->size; + case type_zclmsg: + return _u.zclmsg.content->size; + case type_cmsg: + return _u.cmsg.size; + default: + zmq_assert (false); + return 0; + } +} + +void zmq::msg_t::shrink (size_t new_size_) +{ + // Check the validity of the message. + zmq_assert (check ()); + zmq_assert (new_size_ <= size ()); + + switch (_u.base.type) { + case type_vsm: + _u.vsm.size = static_cast (new_size_); + break; + case type_lmsg: + _u.lmsg.content->size = new_size_; + break; + case type_zclmsg: + _u.zclmsg.content->size = new_size_; + break; + case type_cmsg: + _u.cmsg.size = new_size_; + break; + default: + zmq_assert (false); + } +} + +unsigned char zmq::msg_t::flags () const +{ + return _u.base.flags; +} + +void zmq::msg_t::set_flags (unsigned char flags_) +{ + _u.base.flags |= flags_; +} + +void zmq::msg_t::reset_flags (unsigned char flags_) +{ + _u.base.flags &= ~flags_; +} + +zmq::metadata_t *zmq::msg_t::metadata () const +{ + return _u.base.metadata; +} + +void zmq::msg_t::set_metadata (zmq::metadata_t *metadata_) +{ + assert (metadata_ != NULL); + assert (_u.base.metadata == NULL); + metadata_->add_ref (); + _u.base.metadata = metadata_; +} + +void zmq::msg_t::reset_metadata () +{ + if (_u.base.metadata) { + if (_u.base.metadata->drop_ref ()) { + LIBZMQ_DELETE (_u.base.metadata); + } + _u.base.metadata = NULL; + } +} + +bool zmq::msg_t::is_routing_id () const +{ + return (_u.base.flags & routing_id) == routing_id; +} + +bool zmq::msg_t::is_credential () const +{ + return (_u.base.flags & credential) == credential; +} + +bool zmq::msg_t::is_delimiter () const +{ + return _u.base.type == type_delimiter; +} + +bool zmq::msg_t::is_vsm () const +{ + return _u.base.type == type_vsm; +} + +bool zmq::msg_t::is_cmsg () const +{ + return _u.base.type == type_cmsg; +} + +bool zmq::msg_t::is_lmsg () const +{ + return _u.base.type == type_lmsg; +} + +bool zmq::msg_t::is_zcmsg () const +{ + return _u.base.type == type_zclmsg; +} + +bool zmq::msg_t::is_join () const +{ + return _u.base.type == type_join; +} + +bool zmq::msg_t::is_leave () const +{ + return _u.base.type == type_leave; +} + +bool zmq::msg_t::is_ping () const +{ + return (_u.base.flags & CMD_TYPE_MASK) == ping; +} + +bool zmq::msg_t::is_pong () const +{ + return (_u.base.flags & CMD_TYPE_MASK) == pong; +} + +bool zmq::msg_t::is_close_cmd () const +{ + return (_u.base.flags & CMD_TYPE_MASK) == close_cmd; +} + +size_t zmq::msg_t::command_body_size () const +{ + if (this->is_ping () || this->is_pong ()) + return this->size () - ping_cmd_name_size; + else if (!(this->flags () & msg_t::command) + && (this->is_subscribe () || this->is_cancel ())) + return this->size (); + else if (this->is_subscribe ()) + return this->size () - sub_cmd_name_size; + else if (this->is_cancel ()) + return this->size () - cancel_cmd_name_size; + + return 0; +} + +void *zmq::msg_t::command_body () +{ + unsigned char *data = NULL; + + if (this->is_ping () || this->is_pong ()) + data = + static_cast (this->data ()) + ping_cmd_name_size; + // With inproc, command flag is not set for sub/cancel + else if (!(this->flags () & msg_t::command) + && (this->is_subscribe () || this->is_cancel ())) + data = static_cast (this->data ()); + else if (this->is_subscribe ()) + data = static_cast (this->data ()) + sub_cmd_name_size; + else if (this->is_cancel ()) + data = + static_cast (this->data ()) + cancel_cmd_name_size; + + return data; +} + +void zmq::msg_t::add_refs (int refs_) +{ + zmq_assert (refs_ >= 0); + + // Operation not supported for messages with metadata. + zmq_assert (_u.base.metadata == NULL); + + // No copies required. + if (!refs_) + return; + + // VSMs, CMSGS and delimiters can be copied straight away. The only + // message type that needs special care are long messages. + if (_u.base.type == type_lmsg || is_zcmsg ()) { + if (_u.base.flags & msg_t::shared) + refcnt ()->add (refs_); + else { + refcnt ()->set (refs_ + 1); + _u.base.flags |= msg_t::shared; + } + } +} + +bool zmq::msg_t::rm_refs (int refs_) +{ + zmq_assert (refs_ >= 0); + + // Operation not supported for messages with metadata. + zmq_assert (_u.base.metadata == NULL); + + // No copies required. + if (!refs_) + return true; + + // If there's only one reference close the message. + if ((_u.base.type != type_zclmsg && _u.base.type != type_lmsg) + || !(_u.base.flags & msg_t::shared)) { + close (); + return false; + } + + // The only message type that needs special care are long and zcopy messages. + if (_u.base.type == type_lmsg && !_u.lmsg.content->refcnt.sub (refs_)) { + // We used "placement new" operator to initialize the reference + // counter so we call the destructor explicitly now. + _u.lmsg.content->refcnt.~atomic_counter_t (); + + if (_u.lmsg.content->ffn) + _u.lmsg.content->ffn (_u.lmsg.content->data, _u.lmsg.content->hint); + free (_u.lmsg.content); + + return false; + } + + if (is_zcmsg () && !_u.zclmsg.content->refcnt.sub (refs_)) { + // storage for rfcnt is provided externally + if (_u.zclmsg.content->ffn) { + _u.zclmsg.content->ffn (_u.zclmsg.content->data, + _u.zclmsg.content->hint); + } + + return false; + } + + return true; +} + +uint32_t zmq::msg_t::get_routing_id () const +{ + return _u.base.routing_id; +} + +int zmq::msg_t::set_routing_id (uint32_t routing_id_) +{ + if (routing_id_) { + _u.base.routing_id = routing_id_; + return 0; + } + errno = EINVAL; + return -1; +} + +int zmq::msg_t::reset_routing_id () +{ + _u.base.routing_id = 0; + return 0; +} + +const char *zmq::msg_t::group () const +{ + if (_u.base.group.type == group_type_long) + return _u.base.group.lgroup.content->group; + return _u.base.group.sgroup.group; +} + +int zmq::msg_t::set_group (const char *group_) +{ + size_t length = strnlen (group_, ZMQ_GROUP_MAX_LENGTH); + + return set_group (group_, length); +} + +int zmq::msg_t::set_group (const char *group_, size_t length_) +{ + if (length_ > ZMQ_GROUP_MAX_LENGTH) { + errno = EINVAL; + return -1; + } + + if (length_ > 14) { + _u.base.group.lgroup.type = group_type_long; + _u.base.group.lgroup.content = + (long_group_t *) malloc (sizeof (long_group_t)); + assert (_u.base.group.lgroup.content); + new (&_u.base.group.lgroup.content->refcnt) zmq::atomic_counter_t (); + _u.base.group.lgroup.content->refcnt.set (1); + strncpy (_u.base.group.lgroup.content->group, group_, length_); + _u.base.group.lgroup.content->group[length_] = '\0'; + } else { + strncpy (_u.base.group.sgroup.group, group_, length_); + _u.base.group.sgroup.group[length_] = '\0'; + } + + return 0; +} + +zmq::atomic_counter_t *zmq::msg_t::refcnt () +{ + switch (_u.base.type) { + case type_lmsg: + return &_u.lmsg.content->refcnt; + case type_zclmsg: + return &_u.zclmsg.content->refcnt; + default: + zmq_assert (false); + return NULL; + } +} diff --git a/3rd/libzmq/src/msg.hpp b/3rd/libzmq/src/msg.hpp new file mode 100644 index 00000000..d956b2ac --- /dev/null +++ b/3rd/libzmq/src/msg.hpp @@ -0,0 +1,345 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_MSG_HPP_INCLUDE__ +#define __ZMQ_MSG_HPP_INCLUDE__ + +#include +#include + +#include "config.hpp" +#include "err.hpp" +#include "fd.hpp" +#include "atomic_counter.hpp" +#include "metadata.hpp" + +// bits 2-5 +#define CMD_TYPE_MASK 0x1c + +// Signature for free function to deallocate the message content. +// Note that it has to be declared as "C" so that it is the same as +// zmq_free_fn defined in zmq.h. +extern "C" { +typedef void(msg_free_fn) (void *data_, void *hint_); +} + +namespace zmq +{ +// Note that this structure needs to be explicitly constructed +// (init functions) and destructed (close function). + +static const char cancel_cmd_name[] = "\6CANCEL"; +static const char sub_cmd_name[] = "\x9SUBSCRIBE"; + +class msg_t +{ + public: + // Shared message buffer. Message data are either allocated in one + // continuous block along with this structure - thus avoiding one + // malloc/free pair or they are stored in user-supplied memory. + // In the latter case, ffn member stores pointer to the function to be + // used to deallocate the data. If the buffer is actually shared (there + // are at least 2 references to it) refcount member contains number of + // references. + struct content_t + { + void *data; + size_t size; + msg_free_fn *ffn; + void *hint; + zmq::atomic_counter_t refcnt; + }; + + // Message flags. + enum + { + more = 1, // Followed by more parts + command = 2, // Command frame (see ZMTP spec) + // Command types, use only bits 2-5 and compare with ==, not bitwise, + // a command can never be of more that one type at the same time + ping = 4, + pong = 8, + subscribe = 12, + cancel = 16, + close_cmd = 20, + credential = 32, + routing_id = 64, + shared = 128 + }; + + bool check () const; + int init (); + + int init (void *data_, + size_t size_, + msg_free_fn *ffn_, + void *hint_, + content_t *content_ = NULL); + + int init_size (size_t size_); + int init_buffer (const void *buf_, size_t size_); + int init_data (void *data_, size_t size_, msg_free_fn *ffn_, void *hint_); + int init_external_storage (content_t *content_, + void *data_, + size_t size_, + msg_free_fn *ffn_, + void *hint_); + int init_delimiter (); + int init_join (); + int init_leave (); + int init_subscribe (const size_t size_, const unsigned char *topic); + int init_cancel (const size_t size_, const unsigned char *topic); + int close (); + int move (msg_t &src_); + int copy (msg_t &src_); + void *data (); + size_t size () const; + unsigned char flags () const; + void set_flags (unsigned char flags_); + void reset_flags (unsigned char flags_); + metadata_t *metadata () const; + void set_metadata (metadata_t *metadata_); + void reset_metadata (); + bool is_routing_id () const; + bool is_credential () const; + bool is_delimiter () const; + bool is_join () const; + bool is_leave () const; + bool is_ping () const; + bool is_pong () const; + bool is_close_cmd () const; + + // These are called on each message received by the session_base class, + // so get them inlined to avoid the overhead of 2 function calls per msg + bool is_subscribe () const + { + return (_u.base.flags & CMD_TYPE_MASK) == subscribe; + } + + bool is_cancel () const + { + return (_u.base.flags & CMD_TYPE_MASK) == cancel; + } + + size_t command_body_size () const; + void *command_body (); + bool is_vsm () const; + bool is_cmsg () const; + bool is_lmsg () const; + bool is_zcmsg () const; + uint32_t get_routing_id () const; + int set_routing_id (uint32_t routing_id_); + int reset_routing_id (); + const char *group () const; + int set_group (const char *group_); + int set_group (const char *, size_t length_); + + // After calling this function you can copy the message in POD-style + // refs_ times. No need to call copy. + void add_refs (int refs_); + + // Removes references previously added by add_refs. If the number of + // references drops to 0, the message is closed and false is returned. + bool rm_refs (int refs_); + + void shrink (size_t new_size_); + + // Size in bytes of the largest message that is still copied around + // rather than being reference-counted. + enum + { + msg_t_size = 64 + }; + enum + { + max_vsm_size = + msg_t_size - (sizeof (metadata_t *) + 3 + 16 + sizeof (uint32_t)) + }; + enum + { + ping_cmd_name_size = 5, // 4PING + cancel_cmd_name_size = 7, // 6CANCEL + sub_cmd_name_size = 10 // 9SUBSCRIBE + }; + + private: + zmq::atomic_counter_t *refcnt (); + + // Different message types. + enum type_t + { + type_min = 101, + // VSM messages store the content in the message itself + type_vsm = 101, + // LMSG messages store the content in malloc-ed memory + type_lmsg = 102, + // Delimiter messages are used in envelopes + type_delimiter = 103, + // CMSG messages point to constant data + type_cmsg = 104, + + // zero-copy LMSG message for v2_decoder + type_zclmsg = 105, + + // Join message for radio_dish + type_join = 106, + + // Leave message for radio_dish + type_leave = 107, + + type_max = 107 + }; + + enum group_type_t + { + group_type_short, + group_type_long + }; + + struct long_group_t + { + char group[ZMQ_GROUP_MAX_LENGTH + 1]; + atomic_counter_t refcnt; + }; + + union group_t + { + unsigned char type; + struct + { + unsigned char type; + char group[15]; + } sgroup; + struct + { + unsigned char type; + long_group_t *content; + } lgroup; + }; + + // Note that fields shared between different message types are not + // moved to the parent class (msg_t). This way we get tighter packing + // of the data. Shared fields can be accessed via 'base' member of + // the union. + union + { + struct + { + metadata_t *metadata; + unsigned char unused[msg_t_size + - (sizeof (metadata_t *) + 2 + + sizeof (uint32_t) + sizeof (group_t))]; + unsigned char type; + unsigned char flags; + uint32_t routing_id; + group_t group; + } base; + struct + { + metadata_t *metadata; + unsigned char data[max_vsm_size]; + unsigned char size; + unsigned char type; + unsigned char flags; + uint32_t routing_id; + group_t group; + } vsm; + struct + { + metadata_t *metadata; + content_t *content; + unsigned char + unused[msg_t_size + - (sizeof (metadata_t *) + sizeof (content_t *) + 2 + + sizeof (uint32_t) + sizeof (group_t))]; + unsigned char type; + unsigned char flags; + uint32_t routing_id; + group_t group; + } lmsg; + struct + { + metadata_t *metadata; + content_t *content; + unsigned char + unused[msg_t_size + - (sizeof (metadata_t *) + sizeof (content_t *) + 2 + + sizeof (uint32_t) + sizeof (group_t))]; + unsigned char type; + unsigned char flags; + uint32_t routing_id; + group_t group; + } zclmsg; + struct + { + metadata_t *metadata; + void *data; + size_t size; + unsigned char unused[msg_t_size + - (sizeof (metadata_t *) + sizeof (void *) + + sizeof (size_t) + 2 + sizeof (uint32_t) + + sizeof (group_t))]; + unsigned char type; + unsigned char flags; + uint32_t routing_id; + group_t group; + } cmsg; + struct + { + metadata_t *metadata; + unsigned char unused[msg_t_size + - (sizeof (metadata_t *) + 2 + + sizeof (uint32_t) + sizeof (group_t))]; + unsigned char type; + unsigned char flags; + uint32_t routing_id; + group_t group; + } delimiter; + } _u; +}; + +inline int close_and_return (zmq::msg_t *msg_, int echo_) +{ + // Since we abort on close failure we preserve errno for success case. + const int err = errno; + const int rc = msg_->close (); + errno_assert (rc == 0); + errno = err; + return echo_; +} + +inline int close_and_return (zmq::msg_t msg_[], int count_, int echo_) +{ + for (int i = 0; i < count_; i++) + close_and_return (&msg_[i], 0); + return echo_; +} +} + +#endif diff --git a/3rd/libzmq/src/mtrie.cpp b/3rd/libzmq/src/mtrie.cpp new file mode 100644 index 00000000..30a84e32 --- /dev/null +++ b/3rd/libzmq/src/mtrie.cpp @@ -0,0 +1,37 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "mtrie.hpp" +#include "generic_mtrie_impl.hpp" + +namespace zmq +{ +template class generic_mtrie_t; +} diff --git a/3rd/libzmq/src/mtrie.hpp b/3rd/libzmq/src/mtrie.hpp new file mode 100644 index 00000000..b5ca3ac0 --- /dev/null +++ b/3rd/libzmq/src/mtrie.hpp @@ -0,0 +1,52 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_MTRIE_HPP_INCLUDED__ +#define __ZMQ_MTRIE_HPP_INCLUDED__ + +#include "generic_mtrie.hpp" + +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER > 1600) +#define ZMQ_HAS_EXTERN_TEMPLATE 1 +#else +#define ZMQ_HAS_EXTERN_TEMPLATE 0 +#endif + +namespace zmq +{ +class pipe_t; + +#if ZMQ_HAS_EXTERN_TEMPLATE +extern template class generic_mtrie_t; +#endif + +typedef generic_mtrie_t mtrie_t; +} + +#endif diff --git a/3rd/libzmq/src/mutex.hpp b/3rd/libzmq/src/mutex.hpp new file mode 100644 index 00000000..8ff02269 --- /dev/null +++ b/3rd/libzmq/src/mutex.hpp @@ -0,0 +1,204 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_MUTEX_HPP_INCLUDED__ +#define __ZMQ_MUTEX_HPP_INCLUDED__ + +#include "err.hpp" +#include "macros.hpp" + +// Mutex class encapsulates OS mutex in a platform-independent way. + +#ifdef ZMQ_HAVE_WINDOWS + +#include "windows.hpp" + +namespace zmq +{ +class mutex_t +{ + public: + mutex_t () { InitializeCriticalSection (&_cs); } + + ~mutex_t () { DeleteCriticalSection (&_cs); } + + void lock () { EnterCriticalSection (&_cs); } + + bool try_lock () { return (TryEnterCriticalSection (&_cs)) ? true : false; } + + void unlock () { LeaveCriticalSection (&_cs); } + + CRITICAL_SECTION *get_cs () { return &_cs; } + + private: + CRITICAL_SECTION _cs; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (mutex_t) +}; +} + +#elif defined ZMQ_HAVE_VXWORKS + +#include +#include + +namespace zmq +{ +class mutex_t +{ + public: + inline mutex_t () + { + _semId = + semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE); + } + + inline ~mutex_t () { semDelete (_semId); } + + inline void lock () { semTake (_semId, WAIT_FOREVER); } + + inline bool try_lock () + { + if (semTake (_semId, NO_WAIT) == OK) { + return true; + } + return false; + } + + inline void unlock () { semGive (_semId); } + + private: + SEM_ID _semId; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (mutex_t) +}; +} + +#else + +#include + +namespace zmq +{ +class mutex_t +{ + public: + inline mutex_t () + { + int rc = pthread_mutexattr_init (&_attr); + posix_assert (rc); + + rc = pthread_mutexattr_settype (&_attr, PTHREAD_MUTEX_RECURSIVE); + posix_assert (rc); + + rc = pthread_mutex_init (&_mutex, &_attr); + posix_assert (rc); + } + + inline ~mutex_t () + { + int rc = pthread_mutex_destroy (&_mutex); + posix_assert (rc); + + rc = pthread_mutexattr_destroy (&_attr); + posix_assert (rc); + } + + inline void lock () + { + int rc = pthread_mutex_lock (&_mutex); + posix_assert (rc); + } + + inline bool try_lock () + { + int rc = pthread_mutex_trylock (&_mutex); + if (rc == EBUSY) + return false; + + posix_assert (rc); + return true; + } + + inline void unlock () + { + int rc = pthread_mutex_unlock (&_mutex); + posix_assert (rc); + } + + inline pthread_mutex_t *get_mutex () { return &_mutex; } + + private: + pthread_mutex_t _mutex; + pthread_mutexattr_t _attr; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (mutex_t) +}; +} + +#endif + + +namespace zmq +{ +struct scoped_lock_t +{ + scoped_lock_t (mutex_t &mutex_) : _mutex (mutex_) { _mutex.lock (); } + + ~scoped_lock_t () { _mutex.unlock (); } + + private: + mutex_t &_mutex; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (scoped_lock_t) +}; + + +struct scoped_optional_lock_t +{ + scoped_optional_lock_t (mutex_t *mutex_) : _mutex (mutex_) + { + if (_mutex != NULL) + _mutex->lock (); + } + + ~scoped_optional_lock_t () + { + if (_mutex != NULL) + _mutex->unlock (); + } + + private: + mutex_t *_mutex; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (scoped_optional_lock_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/norm_engine.cpp b/3rd/libzmq/src/norm_engine.cpp new file mode 100644 index 00000000..f2166fdc --- /dev/null +++ b/3rd/libzmq/src/norm_engine.cpp @@ -0,0 +1,721 @@ + +#include "precompiled.hpp" + +#include "platform.hpp" + +#if defined ZMQ_HAVE_NORM + +#include "norm_engine.hpp" +#include "session_base.hpp" +#include "v2_protocol.hpp" + +zmq::norm_engine_t::norm_engine_t (io_thread_t *parent_, + const options_t &options_) : + io_object_t (parent_), + zmq_session (NULL), + options (options_), + norm_instance (NORM_INSTANCE_INVALID), + norm_session (NORM_SESSION_INVALID), + is_sender (false), + is_receiver (false), + zmq_encoder (0), + norm_tx_stream (NORM_OBJECT_INVALID), + tx_first_msg (true), + tx_more_bit (false), + zmq_output_ready (false), + norm_tx_ready (false), + tx_index (0), + tx_len (0), + zmq_input_ready (false) +{ + int rc = tx_msg.init (); + errno_assert (0 == rc); +} + +zmq::norm_engine_t::~norm_engine_t () +{ + shutdown (); // in case it was not already called +} + + +int zmq::norm_engine_t::init (const char *network_, bool send, bool recv) +{ + // Parse the "network_" address int "iface", "addr", and "port" + // norm endpoint format: [id,][;]: + // First, look for optional local NormNodeId + // (default NORM_NODE_ANY causes NORM to use host IP addr for NormNodeId) + NormNodeId localId = NORM_NODE_ANY; + const char *ifacePtr = strchr (network_, ','); + if (NULL != ifacePtr) { + size_t idLen = ifacePtr - network_; + if (idLen > 31) + idLen = 31; + char idText[32]; + strncpy (idText, network_, idLen); + idText[idLen] = '\0'; + localId = (NormNodeId) atoi (idText); + ifacePtr++; + } else { + ifacePtr = network_; + } + + // Second, look for optional multicast ifaceName + char ifaceName[256]; + const char *addrPtr = strchr (ifacePtr, ';'); + if (NULL != addrPtr) { + size_t ifaceLen = addrPtr - ifacePtr; + if (ifaceLen > 255) + ifaceLen = 255; // return error instead? + strncpy (ifaceName, ifacePtr, ifaceLen); + ifaceName[ifaceLen] = '\0'; + ifacePtr = ifaceName; + addrPtr++; + } else { + addrPtr = ifacePtr; + ifacePtr = NULL; + } + + // Finally, parse IP address and port number + const char *portPtr = strrchr (addrPtr, ':'); + if (NULL == portPtr) { + errno = EINVAL; + return -1; + } + + char addr[256]; + size_t addrLen = portPtr - addrPtr; + if (addrLen > 255) + addrLen = 255; + strncpy (addr, addrPtr, addrLen); + addr[addrLen] = '\0'; + portPtr++; + unsigned short portNumber = atoi (portPtr); + + if (NORM_INSTANCE_INVALID == norm_instance) { + if (NORM_INSTANCE_INVALID == (norm_instance = NormCreateInstance ())) { + // errno set by whatever caused NormCreateInstance() to fail + return -1; + } + } + + // TBD - What do we use for our local NormNodeId? + // (for now we use automatic, IP addr based assignment or passed in 'id') + // a) Use ZMQ Identity somehow? + // b) Add function to use iface addr + // c) Randomize and implement a NORM session layer + // conflict detection/resolution protocol + + norm_session = NormCreateSession (norm_instance, addr, portNumber, localId); + if (NORM_SESSION_INVALID == norm_session) { + int savedErrno = errno; + NormDestroyInstance (norm_instance); + norm_instance = NORM_INSTANCE_INVALID; + errno = savedErrno; + return -1; + } + // There's many other useful NORM options that could be applied here + if (NormIsUnicastAddress (addr)) { + NormSetDefaultUnicastNack (norm_session, true); + } else { + // These only apply for multicast sessions + //NormSetTTL(norm_session, options.multicast_hops); // ZMQ default is 1 + NormSetTTL ( + norm_session, + 255); // since the ZMQ_MULTICAST_HOPS socket option isn't well-supported + NormSetRxPortReuse ( + norm_session, + true); // port reuse doesn't work for non-connected unicast + NormSetLoopback (norm_session, + true); // needed when multicast users on same machine + if (NULL != ifacePtr) { + // Note a bad interface may not be caught until sender or receiver start + // (Since sender/receiver is not yet started, this always succeeds here) + NormSetMulticastInterface (norm_session, ifacePtr); + } + } + + if (recv) { + // The alternative NORM_SYNC_CURRENT here would provide "instant" + // receiver sync to the sender's _current_ message transmission. + // NORM_SYNC_STREAM tries to get everything the sender has cached/buffered + NormSetDefaultSyncPolicy (norm_session, NORM_SYNC_STREAM); + if (!NormStartReceiver (norm_session, 2 * 1024 * 1024)) { + // errno set by whatever failed + int savedErrno = errno; + NormDestroyInstance (norm_instance); // session gets closed, too + norm_session = NORM_SESSION_INVALID; + norm_instance = NORM_INSTANCE_INVALID; + errno = savedErrno; + return -1; + } + is_receiver = true; + } + + if (send) { + // Pick a random sender instance id (aka norm sender session id) + NormSessionId instanceId = NormGetRandomSessionId (); + // TBD - provide "options" for some NORM sender parameters + if (!NormStartSender (norm_session, instanceId, 2 * 1024 * 1024, 1400, + 16, 4)) { + // errno set by whatever failed + int savedErrno = errno; + NormDestroyInstance (norm_instance); // session gets closed, too + norm_session = NORM_SESSION_INVALID; + norm_instance = NORM_INSTANCE_INVALID; + errno = savedErrno; + return -1; + } + NormSetCongestionControl (norm_session, true); + norm_tx_ready = true; + is_sender = true; + if (NORM_OBJECT_INVALID + == (norm_tx_stream = + NormStreamOpen (norm_session, 2 * 1024 * 1024))) { + // errno set by whatever failed + int savedErrno = errno; + NormDestroyInstance (norm_instance); // session gets closed, too + norm_session = NORM_SESSION_INVALID; + norm_instance = NORM_INSTANCE_INVALID; + errno = savedErrno; + return -1; + } + } + + //NormSetMessageTrace(norm_session, true); + //NormSetDebugLevel(3); + //NormOpenDebugLog(norm_instance, "normLog.txt"); + + return 0; // no error +} // end zmq::norm_engine_t::init() + +void zmq::norm_engine_t::shutdown () +{ + // TBD - implement a more graceful shutdown option + if (is_receiver) { + NormStopReceiver (norm_session); + + // delete any active NormRxStreamState + rx_pending_list.Destroy (); + rx_ready_list.Destroy (); + msg_ready_list.Destroy (); + + is_receiver = false; + } + if (is_sender) { + NormStopSender (norm_session); + is_sender = false; + } + if (NORM_SESSION_INVALID != norm_session) { + NormDestroySession (norm_session); + norm_session = NORM_SESSION_INVALID; + } + if (NORM_INSTANCE_INVALID != norm_instance) { + NormStopInstance (norm_instance); + NormDestroyInstance (norm_instance); + norm_instance = NORM_INSTANCE_INVALID; + } +} // end zmq::norm_engine_t::shutdown() + +void zmq::norm_engine_t::plug (io_thread_t *io_thread_, + session_base_t *session_) +{ + // TBD - we may assign the NORM engine to an io_thread in the future??? + zmq_session = session_; + if (is_sender) + zmq_output_ready = true; + if (is_receiver) + zmq_input_ready = true; + + fd_t normDescriptor = NormGetDescriptor (norm_instance); + norm_descriptor_handle = add_fd (normDescriptor); + // Set POLLIN for notification of pending NormEvents + set_pollin (norm_descriptor_handle); + + if (is_sender) + send_data (); + +} // end zmq::norm_engine_t::init() + +void zmq::norm_engine_t::unplug () +{ + rm_fd (norm_descriptor_handle); + + zmq_session = NULL; +} // end zmq::norm_engine_t::unplug() + +void zmq::norm_engine_t::terminate () +{ + unplug (); + shutdown (); + delete this; +} + +void zmq::norm_engine_t::restart_output () +{ + // There's new message data available from the session + zmq_output_ready = true; + if (norm_tx_ready) + send_data (); + +} // end zmq::norm_engine_t::restart_output() + +void zmq::norm_engine_t::send_data () +{ + // Here we write as much as is available or we can + while (zmq_output_ready && norm_tx_ready) { + if (0 == tx_len) { + // Our tx_buffer needs data to send + // Get more data from encoder + size_t space = BUFFER_SIZE; + unsigned char *bufPtr = (unsigned char *) tx_buffer; + tx_len = zmq_encoder.encode (&bufPtr, space); + if (0 == tx_len) { + if (tx_first_msg) { + // We don't need to mark eom/flush until a message is sent + tx_first_msg = false; + } else { + // A prior message was completely written to stream, so + // mark end-of-message and possibly flush (to force packet transmission, + // even if it's not a full segment so message gets delivered quickly) + // NormStreamMarkEom(norm_tx_stream); // the flush below marks eom + // Note NORM_FLUSH_ACTIVE makes NORM fairly chatty for low duty cycle messaging + // but makes sure content is delivered quickly. Positive acknowledgements + // with flush override would make NORM more succinct here + NormStreamFlush (norm_tx_stream, true, NORM_FLUSH_ACTIVE); + } + // Need to pull and load a new message to send + if (-1 == zmq_session->pull_msg (&tx_msg)) { + // We need to wait for "restart_output()" to be called by ZMQ + zmq_output_ready = false; + break; + } + zmq_encoder.load_msg (&tx_msg); + // Should we write message size header for NORM to use? Or expect NORM + // receiver to decode ZMQ message framing format(s)? + // OK - we need to use a byte to denote when the ZMQ frame is the _first_ + // frame of a message so it can be decoded properly when a receiver + // 'syncs' mid-stream. We key off the the state of the 'more_flag' + // I.e.,If more_flag _was_ false previously, this is the first + // frame of a ZMQ message. + if (tx_more_bit) + tx_buffer[0] = + (char) 0xff; // this is not first frame of message + else + tx_buffer[0] = 0x00; // this is first frame of message + tx_more_bit = (0 != (tx_msg.flags () & msg_t::more)); + // Go ahead an get a first chunk of the message + bufPtr++; + space--; + tx_len = 1 + zmq_encoder.encode (&bufPtr, space); + tx_index = 0; + } + } + // Do we have data in our tx_buffer pending + if (tx_index < tx_len) { + // We have data in our tx_buffer to send, so write it to the stream + tx_index += NormStreamWrite (norm_tx_stream, tx_buffer + tx_index, + tx_len - tx_index); + if (tx_index < tx_len) { + // NORM stream buffer full, wait for NORM_TX_QUEUE_VACANCY + norm_tx_ready = false; + break; + } + tx_len = 0; // all buffered data was written + } + } // end while (zmq_output_ready && norm_tx_ready) +} // end zmq::norm_engine_t::send_data() + +void zmq::norm_engine_t::in_event () +{ + // This means a NormEvent is pending, so call NormGetNextEvent() and handle + NormEvent event; + if (!NormGetNextEvent (norm_instance, &event)) { + // NORM has died before we unplugged?! + zmq_assert (false); + return; + } + + switch (event.type) { + case NORM_TX_QUEUE_VACANCY: + case NORM_TX_QUEUE_EMPTY: + if (!norm_tx_ready) { + norm_tx_ready = true; + send_data (); + } + break; + + case NORM_RX_OBJECT_NEW: + //break; + case NORM_RX_OBJECT_UPDATED: + recv_data (event.object); + break; + + case NORM_RX_OBJECT_ABORTED: { + NormRxStreamState *rxState = + (NormRxStreamState *) NormObjectGetUserData (event.object); + if (NULL != rxState) { + // Remove the state from the list it's in + // This is now unnecessary since deletion takes care of list removal + // but in the interest of being clear ... + NormRxStreamState::List *list = rxState->AccessList (); + if (NULL != list) + list->Remove (*rxState); + } + delete rxState; + break; + } + case NORM_REMOTE_SENDER_INACTIVE: + // Here we free resources used for this formerly active sender. + // Note w/ NORM_SYNC_STREAM, if sender reactivates, we may + // get some messages delivered twice. NORM_SYNC_CURRENT would + // mitigate that but might miss data at startup. Always tradeoffs. + // Instead of immediately deleting, we could instead initiate a + // user configurable timeout here to wait some amount of time + // after this event to declare the remote sender truly dead + // and delete its state??? + NormNodeDelete (event.sender); + break; + + default: + // We ignore some NORM events + break; + } +} // zmq::norm_engine_t::in_event() + +bool zmq::norm_engine_t::restart_input () +{ + // TBD - should we check/assert that zmq_input_ready was false??? + zmq_input_ready = true; + // Process any pending received messages + if (!msg_ready_list.IsEmpty ()) + recv_data (NORM_OBJECT_INVALID); + + return true; +} // end zmq::norm_engine_t::restart_input() + +void zmq::norm_engine_t::recv_data (NormObjectHandle object) +{ + if (NORM_OBJECT_INVALID != object) { + // Call result of NORM_RX_OBJECT_UPDATED notification + // This is a rx_ready indication for a new or existing rx stream + // First, determine if this is a stream we already know + zmq_assert (NORM_OBJECT_STREAM == NormObjectGetType (object)); + // Since there can be multiple senders (publishers), we keep + // state for each separate rx stream. + NormRxStreamState *rxState = + (NormRxStreamState *) NormObjectGetUserData (object); + if (NULL == rxState) { + // This is a new stream, so create rxState with zmq decoder, etc + rxState = new (std::nothrow) + NormRxStreamState (object, options.maxmsgsize, options.zero_copy, + options.in_batch_size); + errno_assert (rxState); + + if (!rxState->Init ()) { + errno_assert (false); + delete rxState; + return; + } + NormObjectSetUserData (object, rxState); + } else if (!rxState->IsRxReady ()) { + // Existing non-ready stream, so remove from pending + // list to be promoted to rx_ready_list ... + rx_pending_list.Remove (*rxState); + } + if (!rxState->IsRxReady ()) { + // TBD - prepend up front for immediate service? + rxState->SetRxReady (true); + rx_ready_list.Append (*rxState); + } + } + // This loop repeats until we've read all data available from "rx ready" inbound streams + // and pushed any accumulated messages we can up to the zmq session. + while (!rx_ready_list.IsEmpty () + || (zmq_input_ready && !msg_ready_list.IsEmpty ())) { + // Iterate through our rx_ready streams, reading data into the decoder + // (This services incoming "rx ready" streams in a round-robin fashion) + NormRxStreamState::List::Iterator iterator (rx_ready_list); + NormRxStreamState *rxState; + while (NULL != (rxState = iterator.GetNextItem ())) { + switch (rxState->Decode ()) { + case 1: // msg completed + // Complete message decoded, move this stream to msg_ready_list + // to push the message up to the session below. Note the stream + // will be returned to the "rx_ready_list" after that's done + rx_ready_list.Remove (*rxState); + msg_ready_list.Append (*rxState); + continue; + + case -1: // decoding error (shouldn't happen w/ NORM, but ...) + // We need to re-sync this stream (decoder buffer was reset) + rxState->SetSync (false); + break; + + default: // 0 - need more data + break; + } + // Get more data from this stream + NormObjectHandle stream = rxState->GetStreamHandle (); + // First, make sure we're in sync ... + while (!rxState->InSync ()) { + // seek NORM message start + if (!NormStreamSeekMsgStart (stream)) { + // Need to wait for more data + break; + } + // read message 'flag' byte to see if this it's a 'final' frame + char syncFlag; + unsigned int numBytes = 1; + if (!NormStreamRead (stream, &syncFlag, &numBytes)) { + // broken stream (shouldn't happen after seek msg start?) + zmq_assert (false); + continue; + } + if (0 == numBytes) { + // This probably shouldn't happen either since we found msg start + // Need to wait for more data + break; + } + if (0 == syncFlag) + rxState->SetSync (true); + // else keep seeking ... + } // end while(!rxState->InSync()) + if (!rxState->InSync ()) { + // Need more data for this stream, so remove from "rx ready" + // list and iterate to next "rx ready" stream + rxState->SetRxReady (false); + // Move from rx_ready_list to rx_pending_list + rx_ready_list.Remove (*rxState); + rx_pending_list.Append (*rxState); + continue; + } + // Now we're actually ready to read data from the NORM stream to the zmq_decoder + // the underlying zmq_decoder->get_buffer() call sets how much is needed. + unsigned int numBytes = rxState->GetBytesNeeded (); + if (!NormStreamRead (stream, rxState->AccessBuffer (), &numBytes)) { + // broken NORM stream, so re-sync + rxState->Init (); // TBD - check result + // This will retry syncing, and getting data from this stream + // since we don't increment the "it" iterator + continue; + } + rxState->IncrementBufferCount (numBytes); + if (0 == numBytes) { + // All the data available has been read + // Need to wait for NORM_RX_OBJECT_UPDATED for this stream + rxState->SetRxReady (false); + // Move from rx_ready_list to rx_pending_list + rx_ready_list.Remove (*rxState); + rx_pending_list.Append (*rxState); + } + } // end while(NULL != (rxState = iterator.GetNextItem())) + + if (zmq_input_ready) { + // At this point, we've made a pass through the "rx_ready" stream list + // Now make a pass through the "msg_pending" list (if the zmq session + // ready for more input). This may possibly return streams back to + // the "rx ready" stream list after their pending message is handled + NormRxStreamState::List::Iterator iterator (msg_ready_list); + NormRxStreamState *rxState; + while (NULL != (rxState = iterator.GetNextItem ())) { + msg_t *msg = rxState->AccessMsg (); + int rc = zmq_session->push_msg (msg); + if (-1 == rc) { + if (EAGAIN == errno) { + // need to wait until session calls "restart_input()" + zmq_input_ready = false; + break; + } else { + // session rejected message? + // TBD - handle this better + zmq_assert (false); + } + } + // else message was accepted. + msg_ready_list.Remove (*rxState); + if ( + rxState + ->IsRxReady ()) // Move back to "rx_ready" list to read more data + rx_ready_list.Append (*rxState); + else // Move back to "rx_pending" list until NORM_RX_OBJECT_UPDATED + msg_ready_list.Append (*rxState); + } // end while(NULL != (rxState = iterator.GetNextItem())) + } // end if (zmq_input_ready) + } // end while ((!rx_ready_list.empty() || (zmq_input_ready && !msg_ready_list.empty())) + + // Alert zmq of the messages we have pushed up + zmq_session->flush (); + +} // end zmq::norm_engine_t::recv_data() + +zmq::norm_engine_t::NormRxStreamState::NormRxStreamState ( + NormObjectHandle normStream, + int64_t maxMsgSize, + bool zeroCopy, + int inBatchSize) : + norm_stream (normStream), + max_msg_size (maxMsgSize), + zero_copy (zeroCopy), + in_batch_size (inBatchSize), + in_sync (false), + rx_ready (false), + zmq_decoder (NULL), + skip_norm_sync (false), + buffer_ptr (NULL), + buffer_size (0), + buffer_count (0), + prev (NULL), + next (NULL), + list (NULL) +{ +} + +zmq::norm_engine_t::NormRxStreamState::~NormRxStreamState () +{ + if (NULL != zmq_decoder) { + delete zmq_decoder; + zmq_decoder = NULL; + } + if (NULL != list) { + list->Remove (*this); + list = NULL; + } +} + +bool zmq::norm_engine_t::NormRxStreamState::Init () +{ + in_sync = false; + skip_norm_sync = false; + if (NULL != zmq_decoder) + delete zmq_decoder; + zmq_decoder = + new (std::nothrow) v2_decoder_t (in_batch_size, max_msg_size, zero_copy); + alloc_assert (zmq_decoder); + if (NULL != zmq_decoder) { + buffer_count = 0; + buffer_size = 0; + zmq_decoder->get_buffer (&buffer_ptr, &buffer_size); + return true; + } else { + return false; + } +} // end zmq::norm_engine_t::NormRxStreamState::Init() + +// This decodes any pending data sitting in our stream decoder buffer +// It returns 1 upon message completion, -1 on error, 1 on msg completion +int zmq::norm_engine_t::NormRxStreamState::Decode () +{ + // If we have pending bytes to decode, process those first + while (buffer_count > 0) { + // There's pending data for the decoder to decode + size_t processed = 0; + + // This a bit of a kludgy approach used to weed + // out the NORM ZMQ message transport "syncFlag" byte + // from the ZMQ message stream being decoded (but it works!) + if (skip_norm_sync) { + buffer_ptr++; + buffer_count--; + skip_norm_sync = false; + } + + int rc = zmq_decoder->decode (buffer_ptr, buffer_count, processed); + buffer_ptr += processed; + buffer_count -= processed; + switch (rc) { + case 1: + // msg completed + if (0 == buffer_count) { + buffer_size = 0; + zmq_decoder->get_buffer (&buffer_ptr, &buffer_size); + } + skip_norm_sync = true; + return 1; + case -1: + // decoder error (reset decoder and state variables) + in_sync = false; + skip_norm_sync = false; // will get consumed by norm sync check + Init (); + break; + + case 0: + // need more data, keep decoding until buffer exhausted + break; + } + } + // Reset buffer pointer/count for next read + buffer_count = 0; + buffer_size = 0; + zmq_decoder->get_buffer (&buffer_ptr, &buffer_size); + return 0; // need more data + +} // end zmq::norm_engine_t::NormRxStreamState::Decode() + +zmq::norm_engine_t::NormRxStreamState::List::List () : head (NULL), tail (NULL) +{ +} + +zmq::norm_engine_t::NormRxStreamState::List::~List () +{ + Destroy (); +} + +void zmq::norm_engine_t::NormRxStreamState::List::Destroy () +{ + NormRxStreamState *item = head; + while (NULL != item) { + Remove (*item); + delete item; + item = head; + } +} // end zmq::norm_engine_t::NormRxStreamState::List::Destroy() + +void zmq::norm_engine_t::NormRxStreamState::List::Append ( + NormRxStreamState &item) +{ + item.prev = tail; + if (NULL != tail) + tail->next = &item; + else + head = &item; + item.next = NULL; + tail = &item; + item.list = this; +} // end zmq::norm_engine_t::NormRxStreamState::List::Append() + +void zmq::norm_engine_t::NormRxStreamState::List::Remove ( + NormRxStreamState &item) +{ + if (NULL != item.prev) + item.prev->next = item.next; + else + head = item.next; + if (NULL != item.next) + item.next->prev = item.prev; + else + tail = item.prev; + item.prev = item.next = NULL; + item.list = NULL; +} // end zmq::norm_engine_t::NormRxStreamState::List::Remove() + +zmq::norm_engine_t::NormRxStreamState::List::Iterator::Iterator ( + const List &list) : + next_item (list.head) +{ +} + +zmq::norm_engine_t::NormRxStreamState * +zmq::norm_engine_t::NormRxStreamState::List::Iterator::GetNextItem () +{ + NormRxStreamState *nextItem = next_item; + if (NULL != nextItem) + next_item = nextItem->next; + return nextItem; +} // end zmq::norm_engine_t::NormRxStreamState::List::Iterator::GetNextItem() + +const zmq::endpoint_uri_pair_t &zmq::norm_engine_t::get_endpoint () const +{ + return _empty_endpoint; +} + +#endif // ZMQ_HAVE_NORM diff --git a/3rd/libzmq/src/norm_engine.hpp b/3rd/libzmq/src/norm_engine.hpp new file mode 100644 index 00000000..c5ae9e84 --- /dev/null +++ b/3rd/libzmq/src/norm_engine.hpp @@ -0,0 +1,195 @@ + +#ifndef __ZMQ_NORM_ENGINE_HPP_INCLUDED__ +#define __ZMQ_NORM_ENGINE_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_NORM + +#include "io_object.hpp" +#include "i_engine.hpp" +#include "options.hpp" +#include "v2_decoder.hpp" +#include "v2_encoder.hpp" + +#include + +namespace zmq +{ +class io_thread_t; +class msg_t; +class session_base_t; + +class norm_engine_t ZMQ_FINAL : public io_object_t, public i_engine +{ + public: + norm_engine_t (zmq::io_thread_t *parent_, const options_t &options_); + ~norm_engine_t () ZMQ_FINAL; + + // create NORM instance, session, etc + int init (const char *network_, bool send, bool recv); + void shutdown (); + + bool has_handshake_stage () ZMQ_FINAL { return false; }; + + // i_engine interface implementation. + // Plug the engine to the session. + void plug (zmq::io_thread_t *io_thread_, + class session_base_t *session_) ZMQ_FINAL; + + // Terminate and deallocate the engine. Note that 'detached' + // events are not fired on termination. + void terminate () ZMQ_FINAL; + + // This method is called by the session to signalise that more + // messages can be written to the pipe. + bool restart_input () ZMQ_FINAL; + + // This method is called by the session to signalise that there + // are messages to send available. + void restart_output () ZMQ_FINAL; + + void zap_msg_available () ZMQ_FINAL {} + + const endpoint_uri_pair_t &get_endpoint () const ZMQ_FINAL; + + // i_poll_events interface implementation. + // (we only need in_event() for NormEvent notification) + // (i.e., don't have any output events or timers (yet)) + void in_event (); + + private: + void unplug (); + void send_data (); + void recv_data (NormObjectHandle stream); + + + enum + { + BUFFER_SIZE = 2048 + }; + + // Used to keep track of streams from multiple senders + class NormRxStreamState + { + public: + NormRxStreamState (NormObjectHandle normStream, + int64_t maxMsgSize, + bool zeroCopy, + int inBatchSize); + ~NormRxStreamState (); + + NormObjectHandle GetStreamHandle () const { return norm_stream; } + + bool Init (); + + void SetRxReady (bool state) { rx_ready = state; } + bool IsRxReady () const { return rx_ready; } + + void SetSync (bool state) { in_sync = state; } + bool InSync () const { return in_sync; } + + // These are used to feed data to decoder + // and its underlying "msg" buffer + char *AccessBuffer () { return (char *) (buffer_ptr + buffer_count); } + size_t GetBytesNeeded () const { return buffer_size - buffer_count; } + void IncrementBufferCount (size_t count) { buffer_count += count; } + msg_t *AccessMsg () { return zmq_decoder->msg (); } + // This invokes the decoder "decode" method + // returning 0 if more data is needed, + // 1 if the message is complete, If an error + // occurs the 'sync' is dropped and the + // decoder re-initialized + int Decode (); + + class List + { + public: + List (); + ~List (); + + void Append (NormRxStreamState &item); + void Remove (NormRxStreamState &item); + + bool IsEmpty () const { return NULL == head; } + + void Destroy (); + + class Iterator + { + public: + Iterator (const List &list); + NormRxStreamState *GetNextItem (); + + private: + NormRxStreamState *next_item; + }; + friend class Iterator; + + private: + NormRxStreamState *head; + NormRxStreamState *tail; + + }; // end class zmq::norm_engine_t::NormRxStreamState::List + + friend class List; + + List *AccessList () { return list; } + + + private: + NormObjectHandle norm_stream; + int64_t max_msg_size; + bool zero_copy; + int in_batch_size; + bool in_sync; + bool rx_ready; + v2_decoder_t *zmq_decoder; + bool skip_norm_sync; + unsigned char *buffer_ptr; + size_t buffer_size; + size_t buffer_count; + + NormRxStreamState *prev; + NormRxStreamState *next; + NormRxStreamState::List *list; + + }; // end class zmq::norm_engine_t::NormRxStreamState + + const endpoint_uri_pair_t _empty_endpoint; + + session_base_t *zmq_session; + options_t options; + NormInstanceHandle norm_instance; + handle_t norm_descriptor_handle; + NormSessionHandle norm_session; + bool is_sender; + bool is_receiver; + // Sender state + msg_t tx_msg; + v2_encoder_t zmq_encoder; // for tx messages (we use v2 for now) + NormObjectHandle norm_tx_stream; + bool tx_first_msg; + bool tx_more_bit; + bool zmq_output_ready; // zmq has msg(s) to send + bool norm_tx_ready; // norm has tx queue vacancy + // TBD - maybe don't need buffer if can access zmq message buffer directly? + char tx_buffer[BUFFER_SIZE]; + unsigned int tx_index; + unsigned int tx_len; + + // Receiver state + // Lists of norm rx streams from remote senders + bool zmq_input_ready; // zmq ready to receive msg(s) + NormRxStreamState::List + rx_pending_list; // rx streams waiting for data reception + NormRxStreamState::List + rx_ready_list; // rx streams ready for NormStreamRead() + NormRxStreamState::List + msg_ready_list; // rx streams w/ msg ready for push to zmq + + +}; // end class norm_engine_t +} + +#endif // ZMQ_HAVE_NORM + +#endif // !__ZMQ_NORM_ENGINE_HPP_INCLUDED__ diff --git a/3rd/libzmq/src/null_mechanism.cpp b/3rd/libzmq/src/null_mechanism.cpp new file mode 100644 index 00000000..4c1fd2a4 --- /dev/null +++ b/3rd/libzmq/src/null_mechanism.cpp @@ -0,0 +1,230 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include +#include +#include + +#include "err.hpp" +#include "msg.hpp" +#include "session_base.hpp" +#include "null_mechanism.hpp" + +const char error_command_name[] = "\5ERROR"; +const size_t error_command_name_len = sizeof (error_command_name) - 1; +const size_t error_reason_len_size = 1; + +const char ready_command_name[] = "\5READY"; +const size_t ready_command_name_len = sizeof (ready_command_name) - 1; + +zmq::null_mechanism_t::null_mechanism_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_) : + mechanism_base_t (session_, options_), + zap_client_t (session_, peer_address_, options_), + _ready_command_sent (false), + _error_command_sent (false), + _ready_command_received (false), + _error_command_received (false), + _zap_request_sent (false), + _zap_reply_received (false) +{ +} + +zmq::null_mechanism_t::~null_mechanism_t () +{ +} + +int zmq::null_mechanism_t::next_handshake_command (msg_t *msg_) +{ + if (_ready_command_sent || _error_command_sent) { + errno = EAGAIN; + return -1; + } + + if (zap_required () && !_zap_reply_received) { + if (_zap_request_sent) { + errno = EAGAIN; + return -1; + } + // Given this is a backward-incompatible change, it's behind a socket + // option disabled by default. + int rc = session->zap_connect (); + if (rc == -1 && options.zap_enforce_domain) { + session->get_socket ()->event_handshake_failed_no_detail ( + session->get_endpoint (), EFAULT); + return -1; + } + if (rc == 0) { + send_zap_request (); + _zap_request_sent = true; + + // TODO actually, it is quite unlikely that we can read the ZAP + // reply already, but removing this has some strange side-effect + // (probably because the pipe's in_active flag is true until a read + // is attempted) + rc = receive_and_process_zap_reply (); + if (rc != 0) + return -1; + + _zap_reply_received = true; + } + } + + if (_zap_reply_received && status_code != "200") { + _error_command_sent = true; + if (status_code != "300") { + const size_t status_code_len = 3; + const int rc = msg_->init_size ( + error_command_name_len + error_reason_len_size + status_code_len); + zmq_assert (rc == 0); + unsigned char *msg_data = + static_cast (msg_->data ()); + memcpy (msg_data, error_command_name, error_command_name_len); + msg_data += error_command_name_len; + *msg_data = status_code_len; + msg_data += error_reason_len_size; + memcpy (msg_data, status_code.c_str (), status_code_len); + return 0; + } + errno = EAGAIN; + return -1; + } + + make_command_with_basic_properties (msg_, ready_command_name, + ready_command_name_len); + + _ready_command_sent = true; + + return 0; +} + +int zmq::null_mechanism_t::process_handshake_command (msg_t *msg_) +{ + if (_ready_command_received || _error_command_received) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + + const unsigned char *cmd_data = + static_cast (msg_->data ()); + const size_t data_size = msg_->size (); + + int rc = 0; + if (data_size >= ready_command_name_len + && !memcmp (cmd_data, ready_command_name, ready_command_name_len)) + rc = process_ready_command (cmd_data, data_size); + else if (data_size >= error_command_name_len + && !memcmp (cmd_data, error_command_name, error_command_name_len)) + rc = process_error_command (cmd_data, data_size); + else { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + rc = -1; + } + + if (rc == 0) { + rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + } + return rc; +} + +int zmq::null_mechanism_t::process_ready_command ( + const unsigned char *cmd_data_, size_t data_size_) +{ + _ready_command_received = true; + return parse_metadata (cmd_data_ + ready_command_name_len, + data_size_ - ready_command_name_len); +} + +int zmq::null_mechanism_t::process_error_command ( + const unsigned char *cmd_data_, size_t data_size_) +{ + const size_t fixed_prefix_size = + error_command_name_len + error_reason_len_size; + if (data_size_ < fixed_prefix_size) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); + + errno = EPROTO; + return -1; + } + const size_t error_reason_len = + static_cast (cmd_data_[error_command_name_len]); + if (error_reason_len > data_size_ - fixed_prefix_size) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); + + errno = EPROTO; + return -1; + } + const char *error_reason = + reinterpret_cast (cmd_data_) + fixed_prefix_size; + handle_error_reason (error_reason, error_reason_len); + _error_command_received = true; + return 0; +} + +int zmq::null_mechanism_t::zap_msg_available () +{ + if (_zap_reply_received) { + errno = EFSM; + return -1; + } + const int rc = receive_and_process_zap_reply (); + if (rc == 0) + _zap_reply_received = true; + return rc == -1 ? -1 : 0; +} + +zmq::mechanism_t::status_t zmq::null_mechanism_t::status () const +{ + if (_ready_command_sent && _ready_command_received) + return ready; + + const bool command_sent = _ready_command_sent || _error_command_sent; + const bool command_received = + _ready_command_received || _error_command_received; + return command_sent && command_received ? error : handshaking; +} + +void zmq::null_mechanism_t::send_zap_request () +{ + zap_client_t::send_zap_request ("NULL", 4, NULL, NULL, 0); +} diff --git a/3rd/libzmq/src/null_mechanism.hpp b/3rd/libzmq/src/null_mechanism.hpp new file mode 100644 index 00000000..3ea31649 --- /dev/null +++ b/3rd/libzmq/src/null_mechanism.hpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_NULL_MECHANISM_HPP_INCLUDED__ +#define __ZMQ_NULL_MECHANISM_HPP_INCLUDED__ + +#include "mechanism.hpp" +#include "options.hpp" +#include "zap_client.hpp" + +namespace zmq +{ +class msg_t; +class session_base_t; + +class null_mechanism_t ZMQ_FINAL : public zap_client_t +{ + public: + null_mechanism_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_); + ~null_mechanism_t (); + + // mechanism implementation + int next_handshake_command (msg_t *msg_); + int process_handshake_command (msg_t *msg_); + int zap_msg_available (); + status_t status () const; + + private: + bool _ready_command_sent; + bool _error_command_sent; + bool _ready_command_received; + bool _error_command_received; + bool _zap_request_sent; + bool _zap_reply_received; + + int process_ready_command (const unsigned char *cmd_data_, + size_t data_size_); + int process_error_command (const unsigned char *cmd_data_, + size_t data_size_); + + void send_zap_request (); +}; +} + +#endif diff --git a/3rd/libzmq/src/object.cpp b/3rd/libzmq/src/object.cpp new file mode 100644 index 00000000..ba3558f6 --- /dev/null +++ b/3rd/libzmq/src/object.cpp @@ -0,0 +1,551 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "object.hpp" +#include "ctx.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "io_thread.hpp" +#include "session_base.hpp" +#include "socket_base.hpp" + +zmq::object_t::object_t (ctx_t *ctx_, uint32_t tid_) : _ctx (ctx_), _tid (tid_) +{ +} + +zmq::object_t::object_t (object_t *parent_) : + _ctx (parent_->_ctx), + _tid (parent_->_tid) +{ +} + +zmq::object_t::~object_t () +{ +} + +uint32_t zmq::object_t::get_tid () const +{ + return _tid; +} + +void zmq::object_t::set_tid (uint32_t id_) +{ + _tid = id_; +} + +zmq::ctx_t *zmq::object_t::get_ctx () const +{ + return _ctx; +} + +void zmq::object_t::process_command (const command_t &cmd_) +{ + switch (cmd_.type) { + case command_t::activate_read: + process_activate_read (); + break; + + case command_t::activate_write: + process_activate_write (cmd_.args.activate_write.msgs_read); + break; + + case command_t::stop: + process_stop (); + break; + + case command_t::plug: + process_plug (); + process_seqnum (); + break; + + case command_t::own: + process_own (cmd_.args.own.object); + process_seqnum (); + break; + + case command_t::attach: + process_attach (cmd_.args.attach.engine); + process_seqnum (); + break; + + case command_t::bind: + process_bind (cmd_.args.bind.pipe); + process_seqnum (); + break; + + case command_t::hiccup: + process_hiccup (cmd_.args.hiccup.pipe); + break; + + case command_t::pipe_peer_stats: + process_pipe_peer_stats (cmd_.args.pipe_peer_stats.queue_count, + cmd_.args.pipe_peer_stats.socket_base, + cmd_.args.pipe_peer_stats.endpoint_pair); + break; + + case command_t::pipe_stats_publish: + process_pipe_stats_publish ( + cmd_.args.pipe_stats_publish.outbound_queue_count, + cmd_.args.pipe_stats_publish.inbound_queue_count, + cmd_.args.pipe_stats_publish.endpoint_pair); + break; + + case command_t::pipe_term: + process_pipe_term (); + break; + + case command_t::pipe_term_ack: + process_pipe_term_ack (); + break; + + case command_t::pipe_hwm: + process_pipe_hwm (cmd_.args.pipe_hwm.inhwm, + cmd_.args.pipe_hwm.outhwm); + break; + + case command_t::term_req: + process_term_req (cmd_.args.term_req.object); + break; + + case command_t::term: + process_term (cmd_.args.term.linger); + break; + + case command_t::term_ack: + process_term_ack (); + break; + + case command_t::term_endpoint: + process_term_endpoint (cmd_.args.term_endpoint.endpoint); + break; + + case command_t::reap: + process_reap (cmd_.args.reap.socket); + break; + + case command_t::reaped: + process_reaped (); + break; + + case command_t::inproc_connected: + process_seqnum (); + break; + + case command_t::conn_failed: + process_conn_failed (); + break; + + case command_t::done: + default: + zmq_assert (false); + } +} + +int zmq::object_t::register_endpoint (const char *addr_, + const endpoint_t &endpoint_) +{ + return _ctx->register_endpoint (addr_, endpoint_); +} + +int zmq::object_t::unregister_endpoint (const std::string &addr_, + socket_base_t *socket_) +{ + return _ctx->unregister_endpoint (addr_, socket_); +} + +void zmq::object_t::unregister_endpoints (socket_base_t *socket_) +{ + return _ctx->unregister_endpoints (socket_); +} + +zmq::endpoint_t zmq::object_t::find_endpoint (const char *addr_) const +{ + return _ctx->find_endpoint (addr_); +} + +void zmq::object_t::pend_connection (const std::string &addr_, + const endpoint_t &endpoint_, + pipe_t **pipes_) +{ + _ctx->pend_connection (addr_, endpoint_, pipes_); +} + +void zmq::object_t::connect_pending (const char *addr_, + zmq::socket_base_t *bind_socket_) +{ + return _ctx->connect_pending (addr_, bind_socket_); +} + +void zmq::object_t::destroy_socket (socket_base_t *socket_) +{ + _ctx->destroy_socket (socket_); +} + +zmq::io_thread_t *zmq::object_t::choose_io_thread (uint64_t affinity_) const +{ + return _ctx->choose_io_thread (affinity_); +} + +void zmq::object_t::send_stop () +{ + // 'stop' command goes always from administrative thread to + // the current object. + command_t cmd; + cmd.destination = this; + cmd.type = command_t::stop; + _ctx->send_command (_tid, cmd); +} + +void zmq::object_t::send_plug (own_t *destination_, bool inc_seqnum_) +{ + if (inc_seqnum_) + destination_->inc_seqnum (); + + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::plug; + send_command (cmd); +} + +void zmq::object_t::send_own (own_t *destination_, own_t *object_) +{ + destination_->inc_seqnum (); + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::own; + cmd.args.own.object = object_; + send_command (cmd); +} + +void zmq::object_t::send_attach (session_base_t *destination_, + i_engine *engine_, + bool inc_seqnum_) +{ + if (inc_seqnum_) + destination_->inc_seqnum (); + + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::attach; + cmd.args.attach.engine = engine_; + send_command (cmd); +} + +void zmq::object_t::send_conn_failed (session_base_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::conn_failed; + send_command (cmd); +} + +void zmq::object_t::send_bind (own_t *destination_, + pipe_t *pipe_, + bool inc_seqnum_) +{ + if (inc_seqnum_) + destination_->inc_seqnum (); + + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::bind; + cmd.args.bind.pipe = pipe_; + send_command (cmd); +} + +void zmq::object_t::send_activate_read (pipe_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::activate_read; + send_command (cmd); +} + +void zmq::object_t::send_activate_write (pipe_t *destination_, + uint64_t msgs_read_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::activate_write; + cmd.args.activate_write.msgs_read = msgs_read_; + send_command (cmd); +} + +void zmq::object_t::send_hiccup (pipe_t *destination_, void *pipe_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::hiccup; + cmd.args.hiccup.pipe = pipe_; + send_command (cmd); +} + +void zmq::object_t::send_pipe_peer_stats (pipe_t *destination_, + uint64_t queue_count_, + own_t *socket_base_, + endpoint_uri_pair_t *endpoint_pair_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::pipe_peer_stats; + cmd.args.pipe_peer_stats.queue_count = queue_count_; + cmd.args.pipe_peer_stats.socket_base = socket_base_; + cmd.args.pipe_peer_stats.endpoint_pair = endpoint_pair_; + send_command (cmd); +} + +void zmq::object_t::send_pipe_stats_publish ( + own_t *destination_, + uint64_t outbound_queue_count_, + uint64_t inbound_queue_count_, + endpoint_uri_pair_t *endpoint_pair_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::pipe_stats_publish; + cmd.args.pipe_stats_publish.outbound_queue_count = outbound_queue_count_; + cmd.args.pipe_stats_publish.inbound_queue_count = inbound_queue_count_; + cmd.args.pipe_stats_publish.endpoint_pair = endpoint_pair_; + send_command (cmd); +} + +void zmq::object_t::send_pipe_term (pipe_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::pipe_term; + send_command (cmd); +} + +void zmq::object_t::send_pipe_term_ack (pipe_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::pipe_term_ack; + send_command (cmd); +} + +void zmq::object_t::send_pipe_hwm (pipe_t *destination_, + int inhwm_, + int outhwm_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::pipe_hwm; + cmd.args.pipe_hwm.inhwm = inhwm_; + cmd.args.pipe_hwm.outhwm = outhwm_; + send_command (cmd); +} + +void zmq::object_t::send_term_req (own_t *destination_, own_t *object_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::term_req; + cmd.args.term_req.object = object_; + send_command (cmd); +} + +void zmq::object_t::send_term (own_t *destination_, int linger_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::term; + cmd.args.term.linger = linger_; + send_command (cmd); +} + +void zmq::object_t::send_term_ack (own_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::term_ack; + send_command (cmd); +} + +void zmq::object_t::send_term_endpoint (own_t *destination_, + std::string *endpoint_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::term_endpoint; + cmd.args.term_endpoint.endpoint = endpoint_; + send_command (cmd); +} + +void zmq::object_t::send_reap (class socket_base_t *socket_) +{ + command_t cmd; + cmd.destination = _ctx->get_reaper (); + cmd.type = command_t::reap; + cmd.args.reap.socket = socket_; + send_command (cmd); +} + +void zmq::object_t::send_reaped () +{ + command_t cmd; + cmd.destination = _ctx->get_reaper (); + cmd.type = command_t::reaped; + send_command (cmd); +} + +void zmq::object_t::send_inproc_connected (zmq::socket_base_t *socket_) +{ + command_t cmd; + cmd.destination = socket_; + cmd.type = command_t::inproc_connected; + send_command (cmd); +} + +void zmq::object_t::send_done () +{ + command_t cmd; + cmd.destination = NULL; + cmd.type = command_t::done; + _ctx->send_command (ctx_t::term_tid, cmd); +} + +void zmq::object_t::process_stop () +{ + zmq_assert (false); +} + +void zmq::object_t::process_plug () +{ + zmq_assert (false); +} + +void zmq::object_t::process_own (own_t *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_attach (i_engine *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_bind (pipe_t *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_activate_read () +{ + zmq_assert (false); +} + +void zmq::object_t::process_activate_write (uint64_t) +{ + zmq_assert (false); +} + +void zmq::object_t::process_hiccup (void *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_pipe_peer_stats (uint64_t, + own_t *, + endpoint_uri_pair_t *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_pipe_stats_publish (uint64_t, + uint64_t, + endpoint_uri_pair_t *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_pipe_term () +{ + zmq_assert (false); +} + +void zmq::object_t::process_pipe_term_ack () +{ + zmq_assert (false); +} + +void zmq::object_t::process_pipe_hwm (int, int) +{ + zmq_assert (false); +} + +void zmq::object_t::process_term_req (own_t *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_term (int) +{ + zmq_assert (false); +} + +void zmq::object_t::process_term_ack () +{ + zmq_assert (false); +} + +void zmq::object_t::process_term_endpoint (std::string *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_reap (class socket_base_t *) +{ + zmq_assert (false); +} + +void zmq::object_t::process_reaped () +{ + zmq_assert (false); +} + +void zmq::object_t::process_seqnum () +{ + zmq_assert (false); +} + +void zmq::object_t::process_conn_failed () +{ + zmq_assert (false); +} + +void zmq::object_t::send_command (const command_t &cmd_) +{ + _ctx->send_command (cmd_.destination->get_tid (), cmd_); +} diff --git a/3rd/libzmq/src/object.hpp b/3rd/libzmq/src/object.hpp new file mode 100644 index 00000000..b672b0d8 --- /dev/null +++ b/3rd/libzmq/src/object.hpp @@ -0,0 +1,170 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_OBJECT_HPP_INCLUDED__ +#define __ZMQ_OBJECT_HPP_INCLUDED__ + +#include + +#include "endpoint.hpp" +#include "macros.hpp" +#include "stdint.hpp" + +namespace zmq +{ +struct i_engine; +struct endpoint_t; +struct pending_connection_t; +struct command_t; +class ctx_t; +class pipe_t; +class socket_base_t; +class session_base_t; +class io_thread_t; +class own_t; + +// Base class for all objects that participate in inter-thread +// communication. + +class object_t +{ + public: + object_t (zmq::ctx_t *ctx_, uint32_t tid_); + object_t (object_t *parent_); + virtual ~object_t (); + + uint32_t get_tid () const; + void set_tid (uint32_t id_); + ctx_t *get_ctx () const; + void process_command (const zmq::command_t &cmd_); + void send_inproc_connected (zmq::socket_base_t *socket_); + void send_bind (zmq::own_t *destination_, + zmq::pipe_t *pipe_, + bool inc_seqnum_ = true); + + protected: + // Using following function, socket is able to access global + // repository of inproc endpoints. + int register_endpoint (const char *addr_, const zmq::endpoint_t &endpoint_); + int unregister_endpoint (const std::string &addr_, socket_base_t *socket_); + void unregister_endpoints (zmq::socket_base_t *socket_); + zmq::endpoint_t find_endpoint (const char *addr_) const; + void pend_connection (const std::string &addr_, + const endpoint_t &endpoint_, + pipe_t **pipes_); + void connect_pending (const char *addr_, zmq::socket_base_t *bind_socket_); + + void destroy_socket (zmq::socket_base_t *socket_); + + // Logs an message. + void log (const char *format_, ...); + + // Chooses least loaded I/O thread. + zmq::io_thread_t *choose_io_thread (uint64_t affinity_) const; + + // Derived object can use these functions to send commands + // to other objects. + void send_stop (); + void send_plug (zmq::own_t *destination_, bool inc_seqnum_ = true); + void send_own (zmq::own_t *destination_, zmq::own_t *object_); + void send_attach (zmq::session_base_t *destination_, + zmq::i_engine *engine_, + bool inc_seqnum_ = true); + void send_activate_read (zmq::pipe_t *destination_); + void send_activate_write (zmq::pipe_t *destination_, uint64_t msgs_read_); + void send_hiccup (zmq::pipe_t *destination_, void *pipe_); + void send_pipe_peer_stats (zmq::pipe_t *destination_, + uint64_t queue_count_, + zmq::own_t *socket_base, + endpoint_uri_pair_t *endpoint_pair_); + void send_pipe_stats_publish (zmq::own_t *destination_, + uint64_t outbound_queue_count_, + uint64_t inbound_queue_count_, + endpoint_uri_pair_t *endpoint_pair_); + void send_pipe_term (zmq::pipe_t *destination_); + void send_pipe_term_ack (zmq::pipe_t *destination_); + void send_pipe_hwm (zmq::pipe_t *destination_, int inhwm_, int outhwm_); + void send_term_req (zmq::own_t *destination_, zmq::own_t *object_); + void send_term (zmq::own_t *destination_, int linger_); + void send_term_ack (zmq::own_t *destination_); + void send_term_endpoint (own_t *destination_, std::string *endpoint_); + void send_reap (zmq::socket_base_t *socket_); + void send_reaped (); + void send_done (); + void send_conn_failed (zmq::session_base_t *destination_); + + + // These handlers can be overridden by the derived objects. They are + // called when command arrives from another thread. + virtual void process_stop (); + virtual void process_plug (); + virtual void process_own (zmq::own_t *object_); + virtual void process_attach (zmq::i_engine *engine_); + virtual void process_bind (zmq::pipe_t *pipe_); + virtual void process_activate_read (); + virtual void process_activate_write (uint64_t msgs_read_); + virtual void process_hiccup (void *pipe_); + virtual void process_pipe_peer_stats (uint64_t queue_count_, + zmq::own_t *socket_base_, + endpoint_uri_pair_t *endpoint_pair_); + virtual void + process_pipe_stats_publish (uint64_t outbound_queue_count_, + uint64_t inbound_queue_count_, + endpoint_uri_pair_t *endpoint_pair_); + virtual void process_pipe_term (); + virtual void process_pipe_term_ack (); + virtual void process_pipe_hwm (int inhwm_, int outhwm_); + virtual void process_term_req (zmq::own_t *object_); + virtual void process_term (int linger_); + virtual void process_term_ack (); + virtual void process_term_endpoint (std::string *endpoint_); + virtual void process_reap (zmq::socket_base_t *socket_); + virtual void process_reaped (); + virtual void process_conn_failed (); + + + // Special handler called after a command that requires a seqnum + // was processed. The implementation should catch up with its counter + // of processed commands here. + virtual void process_seqnum (); + + private: + // Context provides access to the global state. + zmq::ctx_t *const _ctx; + + // Thread ID of the thread the object belongs to. + uint32_t _tid; + + void send_command (const command_t &cmd_); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (object_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/options.cpp b/3rd/libzmq/src/options.cpp new file mode 100644 index 00000000..1b7d2175 --- /dev/null +++ b/3rd/libzmq/src/options.cpp @@ -0,0 +1,1303 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include +#include + +#include "options.hpp" +#include "err.hpp" +#include "macros.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#endif + +#if defined IFNAMSIZ +#define BINDDEVSIZ IFNAMSIZ +#else +#define BINDDEVSIZ 16 +#endif + +static int sockopt_invalid () +{ +#if defined(ZMQ_ACT_MILITANT) + zmq_assert (false); +#endif + errno = EINVAL; + return -1; +} + +int zmq::do_getsockopt (void *const optval_, + size_t *const optvallen_, + const std::string &value_) +{ + return do_getsockopt (optval_, optvallen_, value_.c_str (), + value_.size () + 1); +} + +int zmq::do_getsockopt (void *const optval_, + size_t *const optvallen_, + const void *value_, + const size_t value_len_) +{ + // TODO behaviour is inconsistent with options_t::getsockopt; there, an + // *exact* length match is required except for string-like (but not the + // CURVE keys!) (and therefore null-ing remaining memory is a no-op, see + // comment below) + if (*optvallen_ < value_len_) { + return sockopt_invalid (); + } + memcpy (optval_, value_, value_len_); + // TODO why is the remaining memory null-ed? + memset (static_cast (optval_) + value_len_, 0, + *optvallen_ - value_len_); + *optvallen_ = value_len_; + return 0; +} + +#ifdef ZMQ_HAVE_CURVE +static int do_getsockopt_curve_key (void *const optval_, + const size_t *const optvallen_, + const uint8_t (&curve_key_)[CURVE_KEYSIZE]) +{ + if (*optvallen_ == CURVE_KEYSIZE) { + memcpy (optval_, curve_key_, CURVE_KEYSIZE); + return 0; + } + if (*optvallen_ == CURVE_KEYSIZE_Z85 + 1) { + zmq_z85_encode (static_cast (optval_), curve_key_, + CURVE_KEYSIZE); + return 0; + } + return sockopt_invalid (); +} +#endif + +template +static int do_setsockopt (const void *const optval_, + const size_t optvallen_, + T *const out_value_) +{ + if (optvallen_ == sizeof (T)) { + memcpy (out_value_, optval_, sizeof (T)); + return 0; + } + return sockopt_invalid (); +} + +int zmq::do_setsockopt_int_as_bool_strict (const void *const optval_, + const size_t optvallen_, + bool *const out_value_) +{ + // TODO handling of values other than 0 or 1 is not consistent, + // here it is disallowed, but for other options such as + // ZMQ_ROUTER_RAW any positive value is accepted + int value = -1; + if (do_setsockopt (optval_, optvallen_, &value) == -1) + return -1; + if (value == 0 || value == 1) { + *out_value_ = (value != 0); + return 0; + } + return sockopt_invalid (); +} + +int zmq::do_setsockopt_int_as_bool_relaxed (const void *const optval_, + const size_t optvallen_, + bool *const out_value_) +{ + int value = -1; + if (do_setsockopt (optval_, optvallen_, &value) == -1) + return -1; + *out_value_ = (value != 0); + return 0; +} + +static int +do_setsockopt_string_allow_empty_strict (const void *const optval_, + const size_t optvallen_, + std::string *const out_value_, + const size_t max_len_) +{ + // TODO why is optval_ != NULL not allowed in case of optvallen_== 0? + // TODO why are empty strings allowed for some socket options, but not for others? + if (optval_ == NULL && optvallen_ == 0) { + out_value_->clear (); + return 0; + } + if (optval_ != NULL && optvallen_ > 0 && optvallen_ <= max_len_) { + out_value_->assign (static_cast (optval_), optvallen_); + return 0; + } + return sockopt_invalid (); +} + +static int +do_setsockopt_string_allow_empty_relaxed (const void *const optval_, + const size_t optvallen_, + std::string *const out_value_, + const size_t max_len_) +{ + // TODO use either do_setsockopt_string_allow_empty_relaxed or + // do_setsockopt_string_allow_empty_strict everywhere + if (optvallen_ > 0 && optvallen_ <= max_len_) { + out_value_->assign (static_cast (optval_), optvallen_); + return 0; + } + return sockopt_invalid (); +} + +template +static int do_setsockopt_set (const void *const optval_, + const size_t optvallen_, + std::set *const set_) +{ + if (optvallen_ == 0 && optval_ == NULL) { + set_->clear (); + return 0; + } + if (optvallen_ == sizeof (T) && optval_ != NULL) { + set_->insert (*(static_cast (optval_))); + return 0; + } + return sockopt_invalid (); +} + +// TODO why is 1000 a sensible default? +const int default_hwm = 1000; + +zmq::options_t::options_t () : + sndhwm (default_hwm), + rcvhwm (default_hwm), + affinity (0), + routing_id_size (0), + rate (100), + recovery_ivl (10000), + multicast_hops (1), + multicast_maxtpdu (1500), + sndbuf (-1), + rcvbuf (-1), + tos (0), + priority (0), + type (-1), + linger (-1), + connect_timeout (0), + tcp_maxrt (0), + reconnect_stop (0), + reconnect_ivl (100), + reconnect_ivl_max (0), + backlog (100), + maxmsgsize (-1), + rcvtimeo (-1), + sndtimeo (-1), + ipv6 (false), + immediate (0), + filter (false), + invert_matching (false), + recv_routing_id (false), + raw_socket (false), + raw_notify (true), + tcp_keepalive (-1), + tcp_keepalive_cnt (-1), + tcp_keepalive_idle (-1), + tcp_keepalive_intvl (-1), + mechanism (ZMQ_NULL), + as_server (0), + gss_principal_nt (ZMQ_GSSAPI_NT_HOSTBASED), + gss_service_principal_nt (ZMQ_GSSAPI_NT_HOSTBASED), + gss_plaintext (false), + socket_id (0), + conflate (false), + handshake_ivl (30000), + connected (false), + heartbeat_ttl (0), + heartbeat_interval (0), + heartbeat_timeout (-1), + use_fd (-1), + zap_enforce_domain (false), + loopback_fastpath (false), + multicast_loop (true), + in_batch_size (8192), + out_batch_size (8192), + zero_copy (true), + router_notify (0), + monitor_event_version (1), + wss_trust_system (false), + hello_msg (), + can_send_hello_msg (false), + disconnect_msg (), + can_recv_disconnect_msg (false) +{ + memset (curve_public_key, 0, CURVE_KEYSIZE); + memset (curve_secret_key, 0, CURVE_KEYSIZE); + memset (curve_server_key, 0, CURVE_KEYSIZE); +#if defined ZMQ_HAVE_VMCI + vmci_buffer_size = 0; + vmci_buffer_min_size = 0; + vmci_buffer_max_size = 0; + vmci_connect_timeout = -1; +#endif +} + +int zmq::options_t::set_curve_key (uint8_t *destination_, + const void *optval_, + size_t optvallen_) +{ + switch (optvallen_) { + case CURVE_KEYSIZE: + memcpy (destination_, optval_, optvallen_); + mechanism = ZMQ_CURVE; + return 0; + + case CURVE_KEYSIZE_Z85 + 1: { + const std::string s (static_cast (optval_), + optvallen_); + + if (zmq_z85_decode (destination_, s.c_str ())) { + mechanism = ZMQ_CURVE; + return 0; + } + break; + } + + case CURVE_KEYSIZE_Z85: + char z85_key[CURVE_KEYSIZE_Z85 + 1]; + memcpy (z85_key, reinterpret_cast (optval_), + optvallen_); + z85_key[CURVE_KEYSIZE_Z85] = 0; + if (zmq_z85_decode (destination_, z85_key)) { + mechanism = ZMQ_CURVE; + return 0; + } + break; + + default: + break; + } + return -1; +} + +const int deciseconds_per_millisecond = 100; + +int zmq::options_t::setsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + const bool is_int = (optvallen_ == sizeof (int)); + int value = 0; + if (is_int) + memcpy (&value, optval_, sizeof (int)); +#if defined(ZMQ_ACT_MILITANT) + bool malformed = true; // Did caller pass a bad option value? +#endif + + switch (option_) { + case ZMQ_SNDHWM: + if (is_int && value >= 0) { + sndhwm = value; + return 0; + } + break; + + case ZMQ_RCVHWM: + if (is_int && value >= 0) { + rcvhwm = value; + return 0; + } + break; + + case ZMQ_AFFINITY: + return do_setsockopt (optval_, optvallen_, &affinity); + + case ZMQ_ROUTING_ID: + // Routing id is any binary string from 1 to 255 octets + if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX) { + routing_id_size = static_cast (optvallen_); + memcpy (routing_id, optval_, routing_id_size); + return 0; + } + break; + + case ZMQ_RATE: + if (is_int && value > 0) { + rate = value; + return 0; + } + break; + + case ZMQ_RECOVERY_IVL: + if (is_int && value >= 0) { + recovery_ivl = value; + return 0; + } + break; + + case ZMQ_SNDBUF: + if (is_int && value >= -1) { + sndbuf = value; + return 0; + } + break; + + case ZMQ_RCVBUF: + if (is_int && value >= -1) { + rcvbuf = value; + return 0; + } + break; + + case ZMQ_TOS: + if (is_int && value >= 0) { + tos = value; + return 0; + } + break; + + case ZMQ_LINGER: + if (is_int && value >= -1) { + linger.store (value); + return 0; + } + break; + + case ZMQ_CONNECT_TIMEOUT: + if (is_int && value >= 0) { + connect_timeout = value; + return 0; + } + break; + + case ZMQ_TCP_MAXRT: + if (is_int && value >= 0) { + tcp_maxrt = value; + return 0; + } + break; + + case ZMQ_RECONNECT_STOP: + if (is_int) { + reconnect_stop = value; + return 0; + } + break; + + case ZMQ_RECONNECT_IVL: + if (is_int && value >= -1) { + reconnect_ivl = value; + return 0; + } + break; + + case ZMQ_RECONNECT_IVL_MAX: + if (is_int && value >= 0) { + reconnect_ivl_max = value; + return 0; + } + break; + + case ZMQ_BACKLOG: + if (is_int && value >= 0) { + backlog = value; + return 0; + } + break; + + case ZMQ_MAXMSGSIZE: + return do_setsockopt (optval_, optvallen_, &maxmsgsize); + + case ZMQ_MULTICAST_HOPS: + if (is_int && value > 0) { + multicast_hops = value; + return 0; + } + break; + + case ZMQ_MULTICAST_MAXTPDU: + if (is_int && value > 0) { + multicast_maxtpdu = value; + return 0; + } + break; + + case ZMQ_RCVTIMEO: + if (is_int && value >= -1) { + rcvtimeo = value; + return 0; + } + break; + + case ZMQ_SNDTIMEO: + if (is_int && value >= -1) { + sndtimeo = value; + return 0; + } + break; + + /* Deprecated in favor of ZMQ_IPV6 */ + case ZMQ_IPV4ONLY: { + bool value; + const int rc = + do_setsockopt_int_as_bool_strict (optval_, optvallen_, &value); + if (rc == 0) + ipv6 = !value; + return rc; + } + + /* To replace the somewhat surprising IPV4ONLY */ + case ZMQ_IPV6: + return do_setsockopt_int_as_bool_strict (optval_, optvallen_, + &ipv6); + + case ZMQ_SOCKS_PROXY: + return do_setsockopt_string_allow_empty_strict ( + optval_, optvallen_, &socks_proxy_address, SIZE_MAX); + + case ZMQ_SOCKS_USERNAME: + /* Make empty string or NULL equivalent. */ + if (optval_ == NULL || optvallen_ == 0) { + socks_proxy_username.clear (); + return 0; + } else { + return do_setsockopt_string_allow_empty_strict ( + optval_, optvallen_, &socks_proxy_username, 255); + } + case ZMQ_SOCKS_PASSWORD: + /* Make empty string or NULL equivalent. */ + if (optval_ == NULL || optvallen_ == 0) { + socks_proxy_password.clear (); + return 0; + } else { + return do_setsockopt_string_allow_empty_strict ( + optval_, optvallen_, &socks_proxy_password, 255); + } + case ZMQ_TCP_KEEPALIVE: + if (is_int && (value == -1 || value == 0 || value == 1)) { + tcp_keepalive = value; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_CNT: + if (is_int && (value == -1 || value >= 0)) { + tcp_keepalive_cnt = value; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_IDLE: + if (is_int && (value == -1 || value >= 0)) { + tcp_keepalive_idle = value; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_INTVL: + if (is_int && (value == -1 || value >= 0)) { + tcp_keepalive_intvl = value; + return 0; + } + break; + + case ZMQ_IMMEDIATE: + // TODO why is immediate not bool (and called non_immediate, as its meaning appears to be reversed) + if (is_int && (value == 0 || value == 1)) { + immediate = value; + return 0; + } + break; + + case ZMQ_TCP_ACCEPT_FILTER: { + std::string filter_str; + int rc = do_setsockopt_string_allow_empty_strict ( + optval_, optvallen_, &filter_str, UCHAR_MAX); + if (rc == 0) { + if (filter_str.empty ()) { + tcp_accept_filters.clear (); + } else { + tcp_address_mask_t mask; + rc = mask.resolve (filter_str.c_str (), ipv6); + if (rc == 0) { + tcp_accept_filters.push_back (mask); + } + } + } + return rc; + } + +#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED + case ZMQ_IPC_FILTER_UID: + return do_setsockopt_set (optval_, optvallen_, + &ipc_uid_accept_filters); + + + case ZMQ_IPC_FILTER_GID: + return do_setsockopt_set (optval_, optvallen_, + &ipc_gid_accept_filters); +#endif + +#if defined ZMQ_HAVE_SO_PEERCRED + case ZMQ_IPC_FILTER_PID: + return do_setsockopt_set (optval_, optvallen_, + &ipc_pid_accept_filters); +#endif + + case ZMQ_PLAIN_SERVER: + if (is_int && (value == 0 || value == 1)) { + as_server = value; + mechanism = value ? ZMQ_PLAIN : ZMQ_NULL; + return 0; + } + break; + + case ZMQ_PLAIN_USERNAME: + if (optvallen_ == 0 && optval_ == NULL) { + mechanism = ZMQ_NULL; + return 0; + } else if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX + && optval_ != NULL) { + plain_username.assign (static_cast (optval_), + optvallen_); + as_server = 0; + mechanism = ZMQ_PLAIN; + return 0; + } + break; + + case ZMQ_PLAIN_PASSWORD: + if (optvallen_ == 0 && optval_ == NULL) { + mechanism = ZMQ_NULL; + return 0; + } else if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX + && optval_ != NULL) { + plain_password.assign (static_cast (optval_), + optvallen_); + as_server = 0; + mechanism = ZMQ_PLAIN; + return 0; + } + break; + + case ZMQ_ZAP_DOMAIN: + return do_setsockopt_string_allow_empty_relaxed ( + optval_, optvallen_, &zap_domain, UCHAR_MAX); + + // If curve encryption isn't built, these options provoke EINVAL +#ifdef ZMQ_HAVE_CURVE + case ZMQ_CURVE_SERVER: + if (is_int && (value == 0 || value == 1)) { + as_server = value; + mechanism = value ? ZMQ_CURVE : ZMQ_NULL; + return 0; + } + break; + + case ZMQ_CURVE_PUBLICKEY: + if (0 == set_curve_key (curve_public_key, optval_, optvallen_)) { + return 0; + } + break; + + case ZMQ_CURVE_SECRETKEY: + if (0 == set_curve_key (curve_secret_key, optval_, optvallen_)) { + return 0; + } + break; + + case ZMQ_CURVE_SERVERKEY: + if (0 == set_curve_key (curve_server_key, optval_, optvallen_)) { + as_server = 0; + return 0; + } + break; +#endif + + case ZMQ_CONFLATE: + return do_setsockopt_int_as_bool_strict (optval_, optvallen_, + &conflate); + + // If libgssapi isn't installed, these options provoke EINVAL +#ifdef HAVE_LIBGSSAPI_KRB5 + case ZMQ_GSSAPI_SERVER: + if (is_int && (value == 0 || value == 1)) { + as_server = value; + mechanism = ZMQ_GSSAPI; + return 0; + } + break; + + case ZMQ_GSSAPI_PRINCIPAL: + if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX && optval_ != NULL) { + gss_principal.assign ((const char *) optval_, optvallen_); + mechanism = ZMQ_GSSAPI; + return 0; + } + break; + + case ZMQ_GSSAPI_SERVICE_PRINCIPAL: + if (optvallen_ > 0 && optvallen_ <= UCHAR_MAX && optval_ != NULL) { + gss_service_principal.assign ((const char *) optval_, + optvallen_); + mechanism = ZMQ_GSSAPI; + as_server = 0; + return 0; + } + break; + + case ZMQ_GSSAPI_PLAINTEXT: + return do_setsockopt_int_as_bool_strict (optval_, optvallen_, + &gss_plaintext); + + case ZMQ_GSSAPI_PRINCIPAL_NAMETYPE: + if (is_int + && (value == ZMQ_GSSAPI_NT_HOSTBASED + || value == ZMQ_GSSAPI_NT_USER_NAME + || value == ZMQ_GSSAPI_NT_KRB5_PRINCIPAL)) { + gss_principal_nt = value; + return 0; + } + break; + + case ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE: + if (is_int + && (value == ZMQ_GSSAPI_NT_HOSTBASED + || value == ZMQ_GSSAPI_NT_USER_NAME + || value == ZMQ_GSSAPI_NT_KRB5_PRINCIPAL)) { + gss_service_principal_nt = value; + return 0; + } + break; +#endif + + case ZMQ_HANDSHAKE_IVL: + if (is_int && value >= 0) { + handshake_ivl = value; + return 0; + } + break; + + case ZMQ_INVERT_MATCHING: + return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, + &invert_matching); + + case ZMQ_HEARTBEAT_IVL: + if (is_int && value >= 0) { + heartbeat_interval = value; + return 0; + } + break; + + case ZMQ_HEARTBEAT_TTL: + // Convert this to deciseconds from milliseconds + value = value / deciseconds_per_millisecond; + if (is_int && value >= 0 && value <= UINT16_MAX) { + heartbeat_ttl = static_cast (value); + return 0; + } + break; + + case ZMQ_HEARTBEAT_TIMEOUT: + if (is_int && value >= 0) { + heartbeat_timeout = value; + return 0; + } + break; + +#ifdef ZMQ_HAVE_VMCI + case ZMQ_VMCI_BUFFER_SIZE: + return do_setsockopt (optval_, optvallen_, &vmci_buffer_size); + + case ZMQ_VMCI_BUFFER_MIN_SIZE: + return do_setsockopt (optval_, optvallen_, &vmci_buffer_min_size); + + case ZMQ_VMCI_BUFFER_MAX_SIZE: + return do_setsockopt (optval_, optvallen_, &vmci_buffer_max_size); + + case ZMQ_VMCI_CONNECT_TIMEOUT: + return do_setsockopt (optval_, optvallen_, &vmci_connect_timeout); +#endif + + case ZMQ_USE_FD: + if (is_int && value >= -1) { + use_fd = value; + return 0; + } + break; + + case ZMQ_BINDTODEVICE: + return do_setsockopt_string_allow_empty_strict ( + optval_, optvallen_, &bound_device, BINDDEVSIZ); + + case ZMQ_ZAP_ENFORCE_DOMAIN: + return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, + &zap_enforce_domain); + + case ZMQ_LOOPBACK_FASTPATH: + return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, + &loopback_fastpath); + + case ZMQ_METADATA: + if (optvallen_ > 0 && !is_int) { + const std::string s (static_cast (optval_), + optvallen_); + const size_t pos = s.find (':'); + if (pos != std::string::npos && pos != 0 + && pos != s.length () - 1) { + const std::string key = s.substr (0, pos); + if (key.compare (0, 2, "X-") == 0 + && key.length () <= UCHAR_MAX) { + std::string val = s.substr (pos + 1, s.length ()); + app_metadata.insert ( + std::pair (key, val)); + return 0; + } + } + } + errno = EINVAL; + return -1; + + case ZMQ_MULTICAST_LOOP: + return do_setsockopt_int_as_bool_relaxed (optval_, optvallen_, + &multicast_loop); + +#ifdef ZMQ_BUILD_DRAFT_API + case ZMQ_IN_BATCH_SIZE: + if (is_int && value > 0) { + in_batch_size = value; + return 0; + } + break; + + case ZMQ_OUT_BATCH_SIZE: + if (is_int && value > 0) { + out_batch_size = value; + return 0; + } + break; + +#ifdef ZMQ_HAVE_WSS + 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 + + case ZMQ_HELLO_MSG: + if (optvallen_ > 0) { + unsigned char *bytes = (unsigned char *) optval_; + hello_msg = + std::vector (bytes, bytes + optvallen_); + } else { + hello_msg = std::vector (); + } + + return 0; + + case ZMQ_DISCONNECT_MSG: + if (optvallen_ > 0) { + unsigned char *bytes = (unsigned char *) optval_; + disconnect_msg = + std::vector (bytes, bytes + optvallen_); + } else { + disconnect_msg = std::vector (); + } + + return 0; + + case ZMQ_PRIORITY: + if (is_int && value >= 0) { + priority = value; + return 0; + } + break; + +#endif + + default: +#if defined(ZMQ_ACT_MILITANT) + // There are valid scenarios for probing with unknown socket option + // values, e.g. to check if security is enabled or not. This will not + // provoke a militant assert. However, passing bad values to a valid + // socket option will, if ZMQ_ACT_MILITANT is defined. + malformed = false; +#endif + break; + } + + // TODO mechanism should either be set explicitly, or determined when + // connecting. currently, it depends on the order of setsockopt calls + // if there is some inconsistency, which is confusing. in addition, + // the assumed or set mechanism should be queryable (as a socket option) + +#if defined(ZMQ_ACT_MILITANT) + // There is no valid use case for passing an error back to the application + // when it sent malformed arguments to a socket option. Use ./configure + // --with-militant to enable this checking. + if (malformed) + zmq_assert (false); +#endif + errno = EINVAL; + return -1; +} + +int zmq::options_t::getsockopt (int option_, + void *optval_, + size_t *optvallen_) const +{ + const bool is_int = (*optvallen_ == sizeof (int)); + int *value = static_cast (optval_); +#if defined(ZMQ_ACT_MILITANT) + bool malformed = true; // Did caller pass a bad option value? +#endif + + switch (option_) { + case ZMQ_SNDHWM: + if (is_int) { + *value = sndhwm; + return 0; + } + break; + + case ZMQ_RCVHWM: + if (is_int) { + *value = rcvhwm; + return 0; + } + break; + + case ZMQ_AFFINITY: + if (*optvallen_ == sizeof (uint64_t)) { + *(static_cast (optval_)) = affinity; + return 0; + } + break; + + case ZMQ_ROUTING_ID: + return do_getsockopt (optval_, optvallen_, routing_id, + routing_id_size); + + case ZMQ_RATE: + if (is_int) { + *value = rate; + return 0; + } + break; + + case ZMQ_RECOVERY_IVL: + if (is_int) { + *value = recovery_ivl; + return 0; + } + break; + + case ZMQ_SNDBUF: + if (is_int) { + *value = sndbuf; + return 0; + } + break; + + case ZMQ_RCVBUF: + if (is_int) { + *value = rcvbuf; + return 0; + } + break; + + case ZMQ_TOS: + if (is_int) { + *value = tos; + return 0; + } + break; + + case ZMQ_TYPE: + if (is_int) { + *value = type; + return 0; + } + break; + + case ZMQ_LINGER: + if (is_int) { + *value = linger.load (); + return 0; + } + break; + + case ZMQ_CONNECT_TIMEOUT: + if (is_int) { + *value = connect_timeout; + return 0; + } + break; + + case ZMQ_TCP_MAXRT: + if (is_int) { + *value = tcp_maxrt; + return 0; + } + break; + + case ZMQ_RECONNECT_STOP: + if (is_int) { + *value = reconnect_stop; + return 0; + } + break; + + case ZMQ_RECONNECT_IVL: + if (is_int) { + *value = reconnect_ivl; + return 0; + } + break; + + case ZMQ_RECONNECT_IVL_MAX: + if (is_int) { + *value = reconnect_ivl_max; + return 0; + } + break; + + case ZMQ_BACKLOG: + if (is_int) { + *value = backlog; + return 0; + } + break; + + case ZMQ_MAXMSGSIZE: + if (*optvallen_ == sizeof (int64_t)) { + *(static_cast (optval_)) = maxmsgsize; + *optvallen_ = sizeof (int64_t); + return 0; + } + break; + + case ZMQ_MULTICAST_HOPS: + if (is_int) { + *value = multicast_hops; + return 0; + } + break; + + case ZMQ_MULTICAST_MAXTPDU: + if (is_int) { + *value = multicast_maxtpdu; + return 0; + } + break; + + case ZMQ_RCVTIMEO: + if (is_int) { + *value = rcvtimeo; + return 0; + } + break; + + case ZMQ_SNDTIMEO: + if (is_int) { + *value = sndtimeo; + return 0; + } + break; + + case ZMQ_IPV4ONLY: + if (is_int) { + *value = 1 - ipv6; + return 0; + } + break; + + case ZMQ_IPV6: + if (is_int) { + *value = ipv6; + return 0; + } + break; + + case ZMQ_IMMEDIATE: + if (is_int) { + *value = immediate; + return 0; + } + break; + + case ZMQ_SOCKS_PROXY: + return do_getsockopt (optval_, optvallen_, socks_proxy_address); + + case ZMQ_SOCKS_USERNAME: + return do_getsockopt (optval_, optvallen_, socks_proxy_username); + + case ZMQ_SOCKS_PASSWORD: + return do_getsockopt (optval_, optvallen_, socks_proxy_password); + + case ZMQ_TCP_KEEPALIVE: + if (is_int) { + *value = tcp_keepalive; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_CNT: + if (is_int) { + *value = tcp_keepalive_cnt; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_IDLE: + if (is_int) { + *value = tcp_keepalive_idle; + return 0; + } + break; + + case ZMQ_TCP_KEEPALIVE_INTVL: + if (is_int) { + *value = tcp_keepalive_intvl; + return 0; + } + break; + + case ZMQ_MECHANISM: + if (is_int) { + *value = mechanism; + return 0; + } + break; + + case ZMQ_PLAIN_SERVER: + if (is_int) { + *value = as_server && mechanism == ZMQ_PLAIN; + return 0; + } + break; + + case ZMQ_PLAIN_USERNAME: + return do_getsockopt (optval_, optvallen_, plain_username); + + case ZMQ_PLAIN_PASSWORD: + return do_getsockopt (optval_, optvallen_, plain_password); + + case ZMQ_ZAP_DOMAIN: + return do_getsockopt (optval_, optvallen_, zap_domain); + + // If curve encryption isn't built, these options provoke EINVAL +#ifdef ZMQ_HAVE_CURVE + case ZMQ_CURVE_SERVER: + if (is_int) { + *value = as_server && mechanism == ZMQ_CURVE; + return 0; + } + break; + + case ZMQ_CURVE_PUBLICKEY: + return do_getsockopt_curve_key (optval_, optvallen_, + curve_public_key); + + case ZMQ_CURVE_SECRETKEY: + return do_getsockopt_curve_key (optval_, optvallen_, + curve_secret_key); + + case ZMQ_CURVE_SERVERKEY: + return do_getsockopt_curve_key (optval_, optvallen_, + curve_server_key); +#endif + + case ZMQ_CONFLATE: + if (is_int) { + *value = conflate; + return 0; + } + break; + + // If libgssapi isn't installed, these options provoke EINVAL +#ifdef HAVE_LIBGSSAPI_KRB5 + case ZMQ_GSSAPI_SERVER: + if (is_int) { + *value = as_server && mechanism == ZMQ_GSSAPI; + return 0; + } + break; + + case ZMQ_GSSAPI_PRINCIPAL: + return do_getsockopt (optval_, optvallen_, gss_principal); + + case ZMQ_GSSAPI_SERVICE_PRINCIPAL: + return do_getsockopt (optval_, optvallen_, gss_service_principal); + + case ZMQ_GSSAPI_PLAINTEXT: + if (is_int) { + *value = gss_plaintext; + return 0; + } + break; + + case ZMQ_GSSAPI_PRINCIPAL_NAMETYPE: + if (is_int) { + *value = gss_principal_nt; + return 0; + } + break; + case ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE: + if (is_int) { + *value = gss_service_principal_nt; + return 0; + } + break; +#endif + + case ZMQ_HANDSHAKE_IVL: + if (is_int) { + *value = handshake_ivl; + return 0; + } + break; + + case ZMQ_INVERT_MATCHING: + if (is_int) { + *value = invert_matching; + return 0; + } + break; + + case ZMQ_HEARTBEAT_IVL: + if (is_int) { + *value = heartbeat_interval; + return 0; + } + break; + + case ZMQ_HEARTBEAT_TTL: + if (is_int) { + // Convert the internal deciseconds value to milliseconds + *value = heartbeat_ttl * 100; + return 0; + } + break; + + case ZMQ_HEARTBEAT_TIMEOUT: + if (is_int) { + *value = heartbeat_timeout; + return 0; + } + break; + + case ZMQ_USE_FD: + if (is_int) { + *value = use_fd; + return 0; + } + break; + + case ZMQ_BINDTODEVICE: + return do_getsockopt (optval_, optvallen_, bound_device); + + case ZMQ_ZAP_ENFORCE_DOMAIN: + if (is_int) { + *value = zap_enforce_domain; + return 0; + } + break; + + case ZMQ_LOOPBACK_FASTPATH: + if (is_int) { + *value = loopback_fastpath; + return 0; + } + break; + + case ZMQ_MULTICAST_LOOP: + if (is_int) { + *value = multicast_loop; + return 0; + } + break; + +#ifdef ZMQ_BUILD_DRAFT_API + case ZMQ_ROUTER_NOTIFY: + if (is_int) { + *value = router_notify; + return 0; + } + break; + + case ZMQ_IN_BATCH_SIZE: + if (is_int) { + *value = in_batch_size; + return 0; + } + break; + + case ZMQ_OUT_BATCH_SIZE: + if (is_int) { + *value = out_batch_size; + return 0; + } + break; + + case ZMQ_PRIORITY: + if (is_int) { + *value = priority; + return 0; + } + break; +#endif + + + default: +#if defined(ZMQ_ACT_MILITANT) + malformed = false; +#endif + break; + } +#if defined(ZMQ_ACT_MILITANT) + if (malformed) + zmq_assert (false); +#endif + errno = EINVAL; + return -1; +} diff --git a/3rd/libzmq/src/options.hpp b/3rd/libzmq/src/options.hpp new file mode 100644 index 00000000..6aa8c321 --- /dev/null +++ b/3rd/libzmq/src/options.hpp @@ -0,0 +1,350 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_OPTIONS_HPP_INCLUDED__ +#define __ZMQ_OPTIONS_HPP_INCLUDED__ + +#include +#include +#include + +#include "atomic_ptr.hpp" +#include "stddef.h" +#include "stdint.hpp" +#include "tcp_address.hpp" + +#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED +#include +#include +#endif +#ifdef ZMQ_HAVE_LOCAL_PEERCRED +#include +#endif + +#if __cplusplus >= 201103L || (defined _MSC_VER && _MSC_VER >= 1700) +#include +#endif + +// Normal base 256 key is 32 bytes +#define CURVE_KEYSIZE 32 +// Key encoded using Z85 is 40 bytes +#define CURVE_KEYSIZE_Z85 40 + +namespace zmq +{ +struct options_t +{ + options_t (); + + int set_curve_key (uint8_t *destination_, + const void *optval_, + size_t optvallen_); + + int setsockopt (int option_, const void *optval_, size_t optvallen_); + int getsockopt (int option_, void *optval_, size_t *optvallen_) const; + + // High-water marks for message pipes. + int sndhwm; + int rcvhwm; + + // I/O thread affinity. + uint64_t affinity; + + // Socket routing id. + unsigned char routing_id_size; + unsigned char routing_id[256]; + + // Maximum transfer rate [kb/s]. Default 100kb/s. + int rate; + + // Reliability time interval [ms]. Default 10 seconds. + int recovery_ivl; + + // Sets the time-to-live field in every multicast packet sent. + int multicast_hops; + + // Sets the maximum transport data unit size in every multicast + // packet sent. + int multicast_maxtpdu; + + // SO_SNDBUF and SO_RCVBUF to be passed to underlying transport sockets. + int sndbuf; + int rcvbuf; + + // Type of service (containing DSCP and ECN socket options) + int tos; + + // Protocol-defined priority + int priority; + + // Socket type. + int8_t type; + + // Linger time, in milliseconds. + atomic_value_t linger; + + // Maximum interval in milliseconds beyond which userspace will + // timeout connect(). + // Default 0 (unused) + int connect_timeout; + + // Maximum interval in milliseconds beyond which TCP will timeout + // retransmitted packets. + // Default 0 (unused) + int tcp_maxrt; + + // Disable reconnect under certain conditions + // Default 0 + int reconnect_stop; + + // Minimum interval between attempts to reconnect, in milliseconds. + // Default 100ms + int reconnect_ivl; + + // Maximum interval between attempts to reconnect, in milliseconds. + // Default 0 (unused) + int reconnect_ivl_max; + + // Maximum backlog for pending connections. + int backlog; + + // Maximal size of message to handle. + int64_t maxmsgsize; + + // The timeout for send/recv operations for this socket, in milliseconds. + int rcvtimeo; + int sndtimeo; + + // If true, IPv6 is enabled (as well as IPv4) + bool ipv6; + + // If 1, connecting pipes are not attached immediately, meaning a send() + // on a socket with only connecting pipes would block + int immediate; + + // If 1, (X)SUB socket should filter the messages. If 0, it should not. + bool filter; + + // If true, the subscription matching on (X)PUB and (X)SUB sockets + // is reversed. Messages are sent to and received by non-matching + // sockets. + bool invert_matching; + + // If true, the routing id message is forwarded to the socket. + bool recv_routing_id; + + // if true, router socket accepts non-zmq tcp connections + bool raw_socket; + bool raw_notify; // Provide connect notifications + + // Address of SOCKS proxy + std::string socks_proxy_address; + + // Credentials for SOCKS proxy. + // Conneciton method will be basic auth if username + // is not empty, no auth otherwise. + std::string socks_proxy_username; + std::string socks_proxy_password; + + // TCP keep-alive settings. + // Defaults to -1 = do not change socket options + int tcp_keepalive; + int tcp_keepalive_cnt; + int tcp_keepalive_idle; + int tcp_keepalive_intvl; + + // TCP accept() filters + typedef std::vector tcp_accept_filters_t; + tcp_accept_filters_t tcp_accept_filters; + + // IPC accept() filters +#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED + typedef std::set ipc_uid_accept_filters_t; + ipc_uid_accept_filters_t ipc_uid_accept_filters; + typedef std::set ipc_gid_accept_filters_t; + ipc_gid_accept_filters_t ipc_gid_accept_filters; +#endif +#if defined ZMQ_HAVE_SO_PEERCRED + typedef std::set ipc_pid_accept_filters_t; + ipc_pid_accept_filters_t ipc_pid_accept_filters; +#endif + + // Security mechanism for all connections on this socket + int mechanism; + + // If peer is acting as server for PLAIN or CURVE mechanisms + int as_server; + + // ZAP authentication domain + std::string zap_domain; + + // Security credentials for PLAIN mechanism + std::string plain_username; + std::string plain_password; + + // Security credentials for CURVE mechanism + uint8_t curve_public_key[CURVE_KEYSIZE]; + uint8_t curve_secret_key[CURVE_KEYSIZE]; + uint8_t curve_server_key[CURVE_KEYSIZE]; + + // Principals for GSSAPI mechanism + std::string gss_principal; + std::string gss_service_principal; + + // Name types GSSAPI principals + int gss_principal_nt; + int gss_service_principal_nt; + + // If true, gss encryption will be disabled + bool gss_plaintext; + + // ID of the socket. + int socket_id; + + // If true, socket conflates outgoing/incoming messages. + // Applicable to dealer, push/pull, pub/sub socket types. + // Cannot receive multi-part messages. + // Ignores hwm + bool conflate; + + // If connection handshake is not done after this many milliseconds, + // close socket. Default is 30 secs. 0 means no handshake timeout. + int handshake_ivl; + + bool connected; + // If remote peer receives a PING message and doesn't receive another + // message within the ttl value, it should close the connection + // (measured in tenths of a second) + uint16_t heartbeat_ttl; + // Time in milliseconds between sending heartbeat PING messages. + int heartbeat_interval; + // Time in milliseconds to wait for a PING response before disconnecting + int heartbeat_timeout; + +#if defined ZMQ_HAVE_VMCI + uint64_t vmci_buffer_size; + uint64_t vmci_buffer_min_size; + uint64_t vmci_buffer_max_size; + int vmci_connect_timeout; +#endif + + // When creating a new ZMQ socket, if this option is set the value + // will be used as the File Descriptor instead of allocating a new + // one via the socket () system call. + int use_fd; + + // Device to bind the underlying socket to, eg. VRF or interface + std::string bound_device; + + // Enforce a non-empty ZAP domain requirement for PLAIN auth + bool zap_enforce_domain; + + // Use of loopback fastpath. + bool loopback_fastpath; + + // Loop sent multicast packets to local sockets + bool multicast_loop; + + // Maximal batching size for engines with receiving functionality. + // So, if there are 10 messages that fit into the batch size, all of + // them may be read by a single 'recv' system call, thus avoiding + // unnecessary network stack traversals. + int in_batch_size; + // Maximal batching size for engines with sending functionality. + // So, if there are 10 messages that fit into the batch size, all of + // them may be written by a single 'send' system call, thus avoiding + // unnecessary network stack traversals. + int out_batch_size; + + // Use zero copy strategy for storing message content when decoding. + bool zero_copy; + + // Router socket ZMQ_NOTIFY_CONNECT/ZMQ_NOTIFY_DISCONNECT notifications + int router_notify; + + // Application metadata + std::map app_metadata; + + // 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; + + // Hello msg + std::vector hello_msg; + bool can_send_hello_msg; + + // Disconnect msg + std::vector disconnect_msg; + bool can_recv_disconnect_msg; +}; + +inline bool get_effective_conflate_option (const options_t &options) +{ + // conflate is only effective for some socket types + return options.conflate + && (options.type == ZMQ_DEALER || options.type == ZMQ_PULL + || options.type == ZMQ_PUSH || options.type == ZMQ_PUB + || options.type == ZMQ_SUB); +} + +int do_getsockopt (void *optval_, + size_t *optvallen_, + const void *value_, + size_t value_len_); + +template +int do_getsockopt (void *const optval_, size_t *const optvallen_, T value_) +{ +#if __cplusplus >= 201103L && (!defined(__GNUC__) || __GNUC__ > 5) + static_assert (std::is_trivially_copyable::value, + "invalid use of do_getsockopt"); +#endif + return do_getsockopt (optval_, optvallen_, &value_, sizeof (T)); +} + +int do_getsockopt (void *optval_, + size_t *optvallen_, + const std::string &value_); + +int do_setsockopt_int_as_bool_strict (const void *optval_, + size_t optvallen_, + bool *out_value_); + +int do_setsockopt_int_as_bool_relaxed (const void *optval_, + size_t optvallen_, + bool *out_value_); +} + +#endif diff --git a/3rd/libzmq/src/own.cpp b/3rd/libzmq/src/own.cpp new file mode 100644 index 00000000..f74824bb --- /dev/null +++ b/3rd/libzmq/src/own.cpp @@ -0,0 +1,212 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "own.hpp" +#include "err.hpp" +#include "io_thread.hpp" + +zmq::own_t::own_t (class ctx_t *parent_, uint32_t tid_) : + object_t (parent_, tid_), + _terminating (false), + _sent_seqnum (0), + _processed_seqnum (0), + _owner (NULL), + _term_acks (0) +{ +} + +zmq::own_t::own_t (io_thread_t *io_thread_, const options_t &options_) : + object_t (io_thread_), + options (options_), + _terminating (false), + _sent_seqnum (0), + _processed_seqnum (0), + _owner (NULL), + _term_acks (0) +{ +} + +zmq::own_t::~own_t () +{ +} + +void zmq::own_t::set_owner (own_t *owner_) +{ + zmq_assert (!_owner); + _owner = owner_; +} + +void zmq::own_t::inc_seqnum () +{ + // This function may be called from a different thread! + _sent_seqnum.add (1); +} + +void zmq::own_t::process_seqnum () +{ + // Catch up with counter of processed commands. + _processed_seqnum++; + + // We may have catched up and still have pending terms acks. + check_term_acks (); +} + +void zmq::own_t::launch_child (own_t *object_) +{ + // Specify the owner of the object. + object_->set_owner (this); + + // Plug the object into the I/O thread. + send_plug (object_); + + // Take ownership of the object. + send_own (this, object_); +} + +void zmq::own_t::term_child (own_t *object_) +{ + process_term_req (object_); +} + +void zmq::own_t::process_term_req (own_t *object_) +{ + // When shutting down we can ignore termination requests from owned + // objects. The termination request was already sent to the object. + if (_terminating) + return; + + // If not found, we assume that termination request was already sent to + // the object so we can safely ignore the request. + if (0 == _owned.erase (object_)) + return; + + // If I/O object is well and alive let's ask it to terminate. + register_term_acks (1); + + // Note that this object is the root of the (partial shutdown) thus, its + // value of linger is used, rather than the value stored by the children. + send_term (object_, options.linger.load ()); +} + +void zmq::own_t::process_own (own_t *object_) +{ + // If the object is already being shut down, new owned objects are + // immediately asked to terminate. Note that linger is set to zero. + if (_terminating) { + register_term_acks (1); + send_term (object_, 0); + return; + } + + // Store the reference to the owned object. + _owned.insert (object_); +} + +void zmq::own_t::terminate () +{ + // If termination is already underway, there's no point + // in starting it anew. + if (_terminating) + return; + + // As for the root of the ownership tree, there's no one to terminate it, + // so it has to terminate itself. + if (!_owner) { + process_term (options.linger.load ()); + return; + } + + // If I am an owned object, I'll ask my owner to terminate me. + send_term_req (_owner, this); +} + +bool zmq::own_t::is_terminating () const +{ + return _terminating; +} + +void zmq::own_t::process_term (int linger_) +{ + // Double termination should never happen. + zmq_assert (!_terminating); + + // Send termination request to all owned objects. + for (owned_t::iterator it = _owned.begin (), end = _owned.end (); it != end; + ++it) + send_term (*it, linger_); + register_term_acks (static_cast (_owned.size ())); + _owned.clear (); + + // Start termination process and check whether by chance we cannot + // terminate immediately. + _terminating = true; + check_term_acks (); +} + +void zmq::own_t::register_term_acks (int count_) +{ + _term_acks += count_; +} + +void zmq::own_t::unregister_term_ack () +{ + zmq_assert (_term_acks > 0); + _term_acks--; + + // This may be a last ack we are waiting for before termination... + check_term_acks (); +} + +void zmq::own_t::process_term_ack () +{ + unregister_term_ack (); +} + +void zmq::own_t::check_term_acks () +{ + if (_terminating && _processed_seqnum == _sent_seqnum.get () + && _term_acks == 0) { + // Sanity check. There should be no active children at this point. + zmq_assert (_owned.empty ()); + + // The root object has nobody to confirm the termination to. + // Other nodes will confirm the termination to the owner. + if (_owner) + send_term_ack (_owner); + + // Deallocate the resources. + process_destroy (); + } +} + +void zmq::own_t::process_destroy () +{ + delete this; +} diff --git a/3rd/libzmq/src/own.hpp b/3rd/libzmq/src/own.hpp new file mode 100644 index 00000000..21463fe3 --- /dev/null +++ b/3rd/libzmq/src/own.hpp @@ -0,0 +1,147 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_OWN_HPP_INCLUDED__ +#define __ZMQ_OWN_HPP_INCLUDED__ + +#include + +#include "object.hpp" +#include "options.hpp" +#include "atomic_counter.hpp" +#include "stdint.hpp" + +namespace zmq +{ +class ctx_t; +class io_thread_t; + +// Base class for objects forming a part of ownership hierarchy. +// It handles initialisation and destruction of such objects. + +class own_t : public object_t +{ + public: + // Note that the owner is unspecified in the constructor. + // It'll be supplied later on when the object is plugged in. + + // The object is not living within an I/O thread. It has it's own + // thread outside of 0MQ infrastructure. + own_t (zmq::ctx_t *parent_, uint32_t tid_); + + // The object is living within I/O thread. + own_t (zmq::io_thread_t *io_thread_, const options_t &options_); + + // When another owned object wants to send command to this object + // it calls this function to let it know it should not shut down + // before the command is delivered. + void inc_seqnum (); + + // Use following two functions to wait for arbitrary events before + // terminating. Just add number of events to wait for using + // register_tem_acks functions. When event occurs, call + // remove_term_ack. When number of pending acks reaches zero + // object will be deallocated. + void register_term_acks (int count_); + void unregister_term_ack (); + + protected: + // Launch the supplied object and become its owner. + void launch_child (own_t *object_); + + // Terminate owned object + void term_child (own_t *object_); + + // Ask owner object to terminate this object. It may take a while + // while actual termination is started. This function should not be + // called more than once. + void terminate (); + + // Returns true if the object is in process of termination. + bool is_terminating () const; + + // Derived object destroys own_t. There's no point in allowing + // others to invoke the destructor. At the same time, it has to be + // virtual so that generic own_t deallocation mechanism destroys + // specific type of the owned object correctly. + ~own_t () ZMQ_OVERRIDE; + + // Term handler is protected rather than private so that it can + // be intercepted by the derived class. This is useful to add custom + // steps to the beginning of the termination process. + void process_term (int linger_) ZMQ_OVERRIDE; + + // A place to hook in when physical destruction of the object + // is to be delayed. + virtual void process_destroy (); + + // Socket options associated with this object. + options_t options; + + private: + // Set owner of the object + void set_owner (own_t *owner_); + + // Handlers for incoming commands. + void process_own (own_t *object_) ZMQ_OVERRIDE; + void process_term_req (own_t *object_) ZMQ_OVERRIDE; + void process_term_ack () ZMQ_OVERRIDE; + void process_seqnum () ZMQ_OVERRIDE; + + // Check whether all the pending term acks were delivered. + // If so, deallocate this object. + void check_term_acks (); + + // True if termination was already initiated. If so, we can destroy + // the object if there are no more child objects or pending term acks. + bool _terminating; + + // Sequence number of the last command sent to this object. + atomic_counter_t _sent_seqnum; + + // Sequence number of the last command processed by this object. + uint64_t _processed_seqnum; + + // Socket owning this object. It's responsible for shutting down + // this object. + own_t *_owner; + + // List of all objects owned by this socket. We are responsible + // for deallocating them before we quit. + typedef std::set owned_t; + owned_t _owned; + + // Number of events we have to get before we can destroy the object. + int _term_acks; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (own_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/pair.cpp b/3rd/libzmq/src/pair.cpp new file mode 100644 index 00000000..f3bfe34b --- /dev/null +++ b/3rd/libzmq/src/pair.cpp @@ -0,0 +1,138 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "pair.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "msg.hpp" + +zmq::pair_t::pair_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_), + _pipe (NULL), + _last_in (NULL) +{ + options.type = ZMQ_PAIR; +} + +zmq::pair_t::~pair_t () +{ + zmq_assert (!_pipe); +} + +void zmq::pair_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_ != NULL); + + // ZMQ_PAIR socket can only be connected to a single peer. + // The socket rejects any further connection requests. + if (_pipe == NULL) + _pipe = pipe_; + else + pipe_->terminate (false); +} + +void zmq::pair_t::xpipe_terminated (pipe_t *pipe_) +{ + if (pipe_ == _pipe) { + if (_last_in == _pipe) { + _last_in = NULL; + } + _pipe = NULL; + } +} + +void zmq::pair_t::xread_activated (pipe_t *) +{ + // There's just one pipe. No lists of active and inactive pipes. + // There's nothing to do here. +} + +void zmq::pair_t::xwrite_activated (pipe_t *) +{ + // There's just one pipe. No lists of active and inactive pipes. + // There's nothing to do here. +} + +int zmq::pair_t::xsend (msg_t *msg_) +{ + if (!_pipe || !_pipe->write (msg_)) { + errno = EAGAIN; + return -1; + } + + if (!(msg_->flags () & msg_t::more)) + _pipe->flush (); + + // Detach the original message from the data buffer. + const int rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +int zmq::pair_t::xrecv (msg_t *msg_) +{ + // Deallocate old content of the message. + int rc = msg_->close (); + errno_assert (rc == 0); + + if (!_pipe || !_pipe->read (msg_)) { + // Initialise the output parameter to be a 0-byte message. + rc = msg_->init (); + errno_assert (rc == 0); + + errno = EAGAIN; + return -1; + } + _last_in = _pipe; + return 0; +} + +bool zmq::pair_t::xhas_in () +{ + if (!_pipe) + return false; + + return _pipe->check_read (); +} + +bool zmq::pair_t::xhas_out () +{ + if (!_pipe) + return false; + + return _pipe->check_write (); +} diff --git a/3rd/libzmq/src/pair.hpp b/3rd/libzmq/src/pair.hpp new file mode 100644 index 00000000..2d7bc071 --- /dev/null +++ b/3rd/libzmq/src/pair.hpp @@ -0,0 +1,71 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PAIR_HPP_INCLUDED__ +#define __ZMQ_PAIR_HPP_INCLUDED__ + +#include "blob.hpp" +#include "socket_base.hpp" +#include "session_base.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; +class io_thread_t; + +class pair_t ZMQ_FINAL : public socket_base_t +{ + public: + pair_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~pair_t (); + + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + void xread_activated (zmq::pipe_t *pipe_); + void xwrite_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + zmq::pipe_t *_pipe; + + zmq::pipe_t *_last_in; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (pair_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/peer.cpp b/3rd/libzmq/src/peer.cpp new file mode 100644 index 00000000..4d7e7461 --- /dev/null +++ b/3rd/libzmq/src/peer.cpp @@ -0,0 +1,70 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "peer.hpp" +#include "pipe.hpp" +#include "wire.hpp" +#include "random.hpp" +#include "likely.hpp" +#include "err.hpp" + +zmq::peer_t::peer_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + server_t (parent_, tid_, sid_) +{ + options.type = ZMQ_PEER; + options.can_send_hello_msg = true; + options.can_recv_disconnect_msg = true; +} + +uint32_t zmq::peer_t::connect_peer (const char *endpoint_uri_) +{ + scoped_optional_lock_t sync_lock (&_sync); + + // connect_peer cannot work with immediate enabled + if (options.immediate == 1) { + errno = EFAULT; + return 0; + } + + int rc = socket_base_t::connect_internal (endpoint_uri_); + if (rc != 0) + return 0; + + return _peer_last_routing_id; +} + +void zmq::peer_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + server_t::xattach_pipe (pipe_, subscribe_to_all_, locally_initiated_); + _peer_last_routing_id = pipe_->get_server_socket_routing_id (); +} diff --git a/3rd/libzmq/src/peer.hpp b/3rd/libzmq/src/peer.hpp new file mode 100644 index 00000000..96674b29 --- /dev/null +++ b/3rd/libzmq/src/peer.hpp @@ -0,0 +1,67 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PEER_HPP_INCLUDED__ +#define __ZMQ_PEER_HPP_INCLUDED__ + +#include + +#include "socket_base.hpp" +#include "server.hpp" +#include "session_base.hpp" +#include "stdint.hpp" +#include "blob.hpp" +#include "fq.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; + +class peer_t ZMQ_FINAL : public server_t +{ + public: + peer_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + + uint32_t connect_peer (const char *endpoint_uri_); + + private: + uint32_t _peer_last_routing_id; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (peer_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/pgm_receiver.cpp b/3rd/libzmq/src/pgm_receiver.cpp new file mode 100644 index 00000000..227efa06 --- /dev/null +++ b/3rd/libzmq/src/pgm_receiver.cpp @@ -0,0 +1,314 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" + +#if defined ZMQ_HAVE_OPENPGM + +#include + +#include "pgm_receiver.hpp" +#include "session_base.hpp" +#include "v1_decoder.hpp" +#include "stdint.hpp" +#include "wire.hpp" +#include "err.hpp" + +zmq::pgm_receiver_t::pgm_receiver_t (class io_thread_t *parent_, + const options_t &options_) : + io_object_t (parent_), + has_rx_timer (false), + pgm_socket (true, options_), + options (options_), + session (NULL), + active_tsi (NULL), + insize (0) +{ +} + +zmq::pgm_receiver_t::~pgm_receiver_t () +{ + // Destructor should not be called before unplug. + zmq_assert (peers.empty ()); +} + +int zmq::pgm_receiver_t::init (bool udp_encapsulation_, const char *network_) +{ + return pgm_socket.init (udp_encapsulation_, network_); +} + +void zmq::pgm_receiver_t::plug (io_thread_t *io_thread_, + session_base_t *session_) +{ + LIBZMQ_UNUSED (io_thread_); + // Retrieve PGM fds and start polling. + fd_t socket_fd = retired_fd; + fd_t waiting_pipe_fd = retired_fd; + pgm_socket.get_receiver_fds (&socket_fd, &waiting_pipe_fd); + socket_handle = add_fd (socket_fd); + pipe_handle = add_fd (waiting_pipe_fd); + set_pollin (pipe_handle); + set_pollin (socket_handle); + + session = session_; + + // If there are any subscriptions already queued in the session, drop them. + drop_subscriptions (); +} + +void zmq::pgm_receiver_t::unplug () +{ + // Delete decoders. + for (peers_t::iterator it = peers.begin (), end = peers.end (); it != end; + ++it) { + if (it->second.decoder != NULL) { + LIBZMQ_DELETE (it->second.decoder); + } + } + peers.clear (); + active_tsi = NULL; + + if (has_rx_timer) { + cancel_timer (rx_timer_id); + has_rx_timer = false; + } + + rm_fd (socket_handle); + rm_fd (pipe_handle); + + session = NULL; +} + +void zmq::pgm_receiver_t::terminate () +{ + unplug (); + delete this; +} + +void zmq::pgm_receiver_t::restart_output () +{ + drop_subscriptions (); +} + +bool zmq::pgm_receiver_t::restart_input () +{ + zmq_assert (session != NULL); + zmq_assert (active_tsi != NULL); + + const peers_t::iterator it = peers.find (*active_tsi); + zmq_assert (it != peers.end ()); + zmq_assert (it->second.joined); + + // Push the pending message into the session. + int rc = session->push_msg (it->second.decoder->msg ()); + errno_assert (rc == 0); + + if (insize > 0) { + rc = process_input (it->second.decoder); + if (rc == -1) { + // HWM reached; we will try later. + if (errno == EAGAIN) { + session->flush (); + return true; + } + // Data error. Delete message decoder, mark the + // peer as not joined and drop remaining data. + it->second.joined = false; + LIBZMQ_DELETE (it->second.decoder); + insize = 0; + } + } + + // Resume polling. + set_pollin (pipe_handle); + set_pollin (socket_handle); + + active_tsi = NULL; + in_event (); + + return true; +} + +const zmq::endpoint_uri_pair_t &zmq::pgm_receiver_t::get_endpoint () const +{ + return _empty_endpoint; +} + +void zmq::pgm_receiver_t::in_event () +{ + // If active_tsi is not null, there is a pending restart_input. + // Keep the internal state as is so that restart_input would process the right data + if (active_tsi) { + return; + } + + // Read data from the underlying pgm_socket. + const pgm_tsi_t *tsi = NULL; + + if (has_rx_timer) { + cancel_timer (rx_timer_id); + has_rx_timer = false; + } + + // TODO: This loop can effectively block other engines in the same I/O + // thread in the case of high load. + while (true) { + // Get new batch of data. + // Note the workaround made not to break strict-aliasing rules. + insize = 0; + void *tmp = NULL; + ssize_t received = pgm_socket.receive (&tmp, &tsi); + + // No data to process. This may happen if the packet received is + // neither ODATA nor ODATA. + if (received == 0) { + if (errno == ENOMEM || errno == EBUSY) { + const long timeout = pgm_socket.get_rx_timeout (); + add_timer (timeout, rx_timer_id); + has_rx_timer = true; + } + break; + } + + // Find the peer based on its TSI. + peers_t::iterator it = peers.find (*tsi); + + // Data loss. Delete decoder and mark the peer as disjoint. + if (received == -1) { + if (it != peers.end ()) { + it->second.joined = false; + if (it->second.decoder != NULL) { + LIBZMQ_DELETE (it->second.decoder); + } + } + break; + } + + // New peer. Add it to the list of know but unjoint peers. + if (it == peers.end ()) { + peer_info_t peer_info = {false, NULL}; + it = peers.ZMQ_MAP_INSERT_OR_EMPLACE (*tsi, peer_info).first; + } + + insize = static_cast (received); + inpos = (unsigned char *) tmp; + + // Read the offset of the fist message in the current packet. + zmq_assert (insize >= sizeof (uint16_t)); + uint16_t offset = get_uint16 (inpos); + inpos += sizeof (uint16_t); + insize -= sizeof (uint16_t); + + // Join the stream if needed. + if (!it->second.joined) { + // There is no beginning of the message in current packet. + // Ignore the data. + if (offset == 0xffff) + continue; + + zmq_assert (offset <= insize); + zmq_assert (it->second.decoder == NULL); + + // We have to move data to the beginning of the first message. + inpos += offset; + insize -= offset; + + // Mark the stream as joined. + it->second.joined = true; + + // Create and connect decoder for the peer. + it->second.decoder = + new (std::nothrow) v1_decoder_t (0, options.maxmsgsize); + alloc_assert (it->second.decoder); + } + + int rc = process_input (it->second.decoder); + if (rc == -1) { + if (errno == EAGAIN) { + active_tsi = tsi; + + // Stop polling. + reset_pollin (pipe_handle); + reset_pollin (socket_handle); + + break; + } + + it->second.joined = false; + LIBZMQ_DELETE (it->second.decoder); + insize = 0; + } + } + + // Flush any messages decoder may have produced. + session->flush (); +} + +int zmq::pgm_receiver_t::process_input (v1_decoder_t *decoder) +{ + zmq_assert (session != NULL); + + while (insize > 0) { + size_t n = 0; + int rc = decoder->decode (inpos, insize, n); + if (rc == -1) + return -1; + inpos += n; + insize -= n; + if (rc == 0) + break; + rc = session->push_msg (decoder->msg ()); + if (rc == -1) { + errno_assert (errno == EAGAIN); + return -1; + } + } + return 0; +} + + +void zmq::pgm_receiver_t::timer_event (int token) +{ + zmq_assert (token == rx_timer_id); + + // Timer cancels on return by poller_base. + has_rx_timer = false; + in_event (); +} + +void zmq::pgm_receiver_t::drop_subscriptions () +{ + msg_t msg; + msg.init (); + while (session->pull_msg (&msg) == 0) + msg.close (); +} + +#endif diff --git a/3rd/libzmq/src/pgm_receiver.hpp b/3rd/libzmq/src/pgm_receiver.hpp new file mode 100644 index 00000000..cb033745 --- /dev/null +++ b/3rd/libzmq/src/pgm_receiver.hpp @@ -0,0 +1,145 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PGM_RECEIVER_HPP_INCLUDED__ +#define __ZMQ_PGM_RECEIVER_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_OPENPGM + +#include +#include + +#include "io_object.hpp" +#include "i_engine.hpp" +#include "options.hpp" +#include "v1_decoder.hpp" +#include "pgm_socket.hpp" + +namespace zmq +{ +class io_thread_t; +class session_base_t; + +class pgm_receiver_t ZMQ_FINAL : public io_object_t, public i_engine +{ + public: + pgm_receiver_t (zmq::io_thread_t *parent_, const options_t &options_); + ~pgm_receiver_t (); + + int init (bool udp_encapsulation_, const char *network_); + + // i_engine interface implementation. + bool has_handshake_stage () { return false; }; + void plug (zmq::io_thread_t *io_thread_, zmq::session_base_t *session_); + void terminate (); + bool restart_input (); + void restart_output (); + void zap_msg_available () {} + const endpoint_uri_pair_t &get_endpoint () const; + + // i_poll_events interface implementation. + void in_event (); + void timer_event (int token); + + private: + // Unplug the engine from the session. + void unplug (); + + // Decode received data (inpos, insize) and forward decoded + // messages to the session. + int process_input (v1_decoder_t *decoder); + + // PGM is not able to move subscriptions upstream. Thus, drop all + // the pending subscriptions. + void drop_subscriptions (); + + // RX timeout timer ID. + enum + { + rx_timer_id = 0xa1 + }; + + const endpoint_uri_pair_t _empty_endpoint; + + // RX timer is running. + bool has_rx_timer; + + // If joined is true we are already getting messages from the peer. + // It it's false, we are getting data but still we haven't seen + // beginning of a message. + struct peer_info_t + { + bool joined; + v1_decoder_t *decoder; + }; + + struct tsi_comp + { + bool operator() (const pgm_tsi_t <si, const pgm_tsi_t &rtsi) const + { + uint32_t ll[2], rl[2]; + memcpy (ll, <si, sizeof (ll)); + memcpy (rl, &rtsi, sizeof (rl)); + return (ll[0] < rl[0]) || (ll[0] == rl[0] && ll[1] < rl[1]); + } + }; + + typedef std::map peers_t; + peers_t peers; + + // PGM socket. + pgm_socket_t pgm_socket; + + // Socket options. + options_t options; + + // Associated session. + zmq::session_base_t *session; + + const pgm_tsi_t *active_tsi; + + // Number of bytes not consumed by the decoder due to pipe overflow. + size_t insize; + + // Pointer to data still waiting to be processed by the decoder. + const unsigned char *inpos; + + // Poll handle associated with PGM socket. + handle_t socket_handle; + + // Poll handle associated with engine PGM waiting pipe. + handle_t pipe_handle; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (pgm_receiver_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/pgm_sender.cpp b/3rd/libzmq/src/pgm_sender.cpp new file mode 100644 index 00000000..3c68f23b --- /dev/null +++ b/3rd/libzmq/src/pgm_sender.cpp @@ -0,0 +1,256 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#if defined ZMQ_HAVE_OPENPGM + +#include + +#include "io_thread.hpp" +#include "pgm_sender.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "wire.hpp" +#include "stdint.hpp" +#include "macros.hpp" + +zmq::pgm_sender_t::pgm_sender_t (io_thread_t *parent_, + const options_t &options_) : + io_object_t (parent_), + has_tx_timer (false), + has_rx_timer (false), + session (NULL), + encoder (0), + more_flag (false), + pgm_socket (false, options_), + options (options_), + handle (static_cast (NULL)), + uplink_handle (static_cast (NULL)), + rdata_notify_handle (static_cast (NULL)), + pending_notify_handle (static_cast (NULL)), + out_buffer (NULL), + out_buffer_size (0), + write_size (0) +{ + int rc = msg.init (); + errno_assert (rc == 0); +} + +int zmq::pgm_sender_t::init (bool udp_encapsulation_, const char *network_) +{ + int rc = pgm_socket.init (udp_encapsulation_, network_); + if (rc != 0) + return rc; + + out_buffer_size = pgm_socket.get_max_tsdu_size (); + out_buffer = (unsigned char *) malloc (out_buffer_size); + alloc_assert (out_buffer); + + return rc; +} + +void zmq::pgm_sender_t::plug (io_thread_t *io_thread_, session_base_t *session_) +{ + LIBZMQ_UNUSED (io_thread_); + // Allocate 2 fds for PGM socket. + fd_t downlink_socket_fd = retired_fd; + fd_t uplink_socket_fd = retired_fd; + fd_t rdata_notify_fd = retired_fd; + fd_t pending_notify_fd = retired_fd; + + session = session_; + + // Fill fds from PGM transport and add them to the poller. + pgm_socket.get_sender_fds (&downlink_socket_fd, &uplink_socket_fd, + &rdata_notify_fd, &pending_notify_fd); + + handle = add_fd (downlink_socket_fd); + uplink_handle = add_fd (uplink_socket_fd); + rdata_notify_handle = add_fd (rdata_notify_fd); + pending_notify_handle = add_fd (pending_notify_fd); + + // Set POLLIN. We wont never want to stop polling for uplink = we never + // want to stop processing NAKs. + set_pollin (uplink_handle); + set_pollin (rdata_notify_handle); + set_pollin (pending_notify_handle); + + // Set POLLOUT for downlink_socket_handle. + set_pollout (handle); +} + +void zmq::pgm_sender_t::unplug () +{ + if (has_rx_timer) { + cancel_timer (rx_timer_id); + has_rx_timer = false; + } + + if (has_tx_timer) { + cancel_timer (tx_timer_id); + has_tx_timer = false; + } + + rm_fd (handle); + rm_fd (uplink_handle); + rm_fd (rdata_notify_handle); + rm_fd (pending_notify_handle); + session = NULL; +} + +void zmq::pgm_sender_t::terminate () +{ + unplug (); + delete this; +} + +void zmq::pgm_sender_t::restart_output () +{ + set_pollout (handle); + out_event (); +} + +bool zmq::pgm_sender_t::restart_input () +{ + zmq_assert (false); + return true; +} + +const zmq::endpoint_uri_pair_t &zmq::pgm_sender_t::get_endpoint () const +{ + return _empty_endpoint; +} + +zmq::pgm_sender_t::~pgm_sender_t () +{ + int rc = msg.close (); + errno_assert (rc == 0); + + if (out_buffer) { + free (out_buffer); + out_buffer = NULL; + } +} + +void zmq::pgm_sender_t::in_event () +{ + if (has_rx_timer) { + cancel_timer (rx_timer_id); + has_rx_timer = false; + } + + // In-event on sender side means NAK or SPMR receiving from some peer. + pgm_socket.process_upstream (); + if (errno == ENOMEM || errno == EBUSY) { + const long timeout = pgm_socket.get_rx_timeout (); + add_timer (timeout, rx_timer_id); + has_rx_timer = true; + } +} + +void zmq::pgm_sender_t::out_event () +{ + // POLLOUT event from send socket. If write buffer is empty, + // try to read new data from the encoder. + if (write_size == 0) { + // First two bytes (sizeof uint16_t) are used to store message + // offset in following steps. Note that by passing our buffer to + // the get data function we prevent it from returning its own buffer. + unsigned char *bf = out_buffer + sizeof (uint16_t); + size_t bfsz = out_buffer_size - sizeof (uint16_t); + uint16_t offset = 0xffff; + + size_t bytes = encoder.encode (&bf, bfsz); + while (bytes < bfsz) { + if (!more_flag && offset == 0xffff) + offset = static_cast (bytes); + int rc = session->pull_msg (&msg); + if (rc == -1) + break; + more_flag = msg.flags () & msg_t::more; + encoder.load_msg (&msg); + bf = out_buffer + sizeof (uint16_t) + bytes; + bytes += encoder.encode (&bf, bfsz - bytes); + } + + // If there are no data to write stop polling for output. + if (bytes == 0) { + reset_pollout (handle); + return; + } + + write_size = sizeof (uint16_t) + bytes; + + // Put offset information in the buffer. + put_uint16 (out_buffer, offset); + } + + if (has_tx_timer) { + cancel_timer (tx_timer_id); + set_pollout (handle); + has_tx_timer = false; + } + + // Send the data. + size_t nbytes = pgm_socket.send (out_buffer, write_size); + + // We can write either all data or 0 which means rate limit reached. + if (nbytes == write_size) + write_size = 0; + else { + zmq_assert (nbytes == 0); + + if (errno == ENOMEM) { + // Stop polling handle and wait for tx timeout + const long timeout = pgm_socket.get_tx_timeout (); + add_timer (timeout, tx_timer_id); + reset_pollout (handle); + has_tx_timer = true; + } else + errno_assert (errno == EBUSY); + } +} + +void zmq::pgm_sender_t::timer_event (int token) +{ + // Timer cancels on return by poller_base. + if (token == rx_timer_id) { + has_rx_timer = false; + in_event (); + } else if (token == tx_timer_id) { + // Restart polling handle and retry sending + has_tx_timer = false; + set_pollout (handle); + out_event (); + } else + zmq_assert (false); +} + +#endif diff --git a/3rd/libzmq/src/pgm_sender.hpp b/3rd/libzmq/src/pgm_sender.hpp new file mode 100644 index 00000000..1d120c95 --- /dev/null +++ b/3rd/libzmq/src/pgm_sender.hpp @@ -0,0 +1,124 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PGM_SENDER_HPP_INCLUDED__ +#define __ZMQ_PGM_SENDER_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_OPENPGM + +#include "stdint.hpp" +#include "io_object.hpp" +#include "i_engine.hpp" +#include "options.hpp" +#include "pgm_socket.hpp" +#include "v1_encoder.hpp" +#include "msg.hpp" + +namespace zmq +{ +class io_thread_t; +class session_base_t; + +class pgm_sender_t ZMQ_FINAL : public io_object_t, public i_engine +{ + public: + pgm_sender_t (zmq::io_thread_t *parent_, const options_t &options_); + ~pgm_sender_t (); + + int init (bool udp_encapsulation_, const char *network_); + + // i_engine interface implementation. + bool has_handshake_stage () { return false; }; + void plug (zmq::io_thread_t *io_thread_, zmq::session_base_t *session_); + void terminate (); + bool restart_input (); + void restart_output (); + void zap_msg_available () {} + const endpoint_uri_pair_t &get_endpoint () const; + + // i_poll_events interface implementation. + void in_event (); + void out_event (); + void timer_event (int token); + + private: + // Unplug the engine from the session. + void unplug (); + + // TX and RX timeout timer ID's. + enum + { + tx_timer_id = 0xa0, + rx_timer_id = 0xa1 + }; + + const endpoint_uri_pair_t _empty_endpoint; + + // Timers are running. + bool has_tx_timer; + bool has_rx_timer; + + session_base_t *session; + + // Message encoder. + v1_encoder_t encoder; + + msg_t msg; + + // Keeps track of message boundaries. + bool more_flag; + + // PGM socket. + pgm_socket_t pgm_socket; + + // Socket options. + options_t options; + + // Poll handle associated with PGM socket. + handle_t handle; + handle_t uplink_handle; + handle_t rdata_notify_handle; + handle_t pending_notify_handle; + + // Output buffer from pgm_socket. + unsigned char *out_buffer; + + // Output buffer size. + size_t out_buffer_size; + + // Number of bytes in the buffer to be written to the socket. + // If zero, there are no data to be sent. + size_t write_size; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (pgm_sender_t) +}; +} +#endif + +#endif diff --git a/3rd/libzmq/src/pgm_socket.cpp b/3rd/libzmq/src/pgm_socket.cpp new file mode 100644 index 00000000..e3399db1 --- /dev/null +++ b/3rd/libzmq/src/pgm_socket.cpp @@ -0,0 +1,687 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#ifdef ZMQ_HAVE_OPENPGM + +#ifdef ZMQ_HAVE_LINUX +#include +#endif + +#include +#include +#include + +#include "options.hpp" +#include "pgm_socket.hpp" +#include "config.hpp" +#include "err.hpp" +#include "random.hpp" +#include "stdint.hpp" + +#ifndef MSG_ERRQUEUE +#define MSG_ERRQUEUE 0x2000 +#endif + +zmq::pgm_socket_t::pgm_socket_t (bool receiver_, const options_t &options_) : + sock (NULL), + options (options_), + receiver (receiver_), + pgm_msgv (NULL), + pgm_msgv_len (0), + nbytes_rec (0), + nbytes_processed (0), + pgm_msgv_processed (0) +{ +} + +// Resolve PGM socket address. +// network_ of the form : +// e.g. eth0;239.192.0.1:7500 +// link-local;224.250.0.1,224.250.0.2;224.250.0.3:8000 +// ;[fe80::1%en0]:7500 +int zmq::pgm_socket_t::init_address (const char *network_, + struct pgm_addrinfo_t **res, + uint16_t *port_number) +{ + // Parse port number, start from end for IPv6 + const char *port_delim = strrchr (network_, ':'); + if (!port_delim) { + errno = EINVAL; + return -1; + } + + *port_number = atoi (port_delim + 1); + + char network[256]; + if (port_delim - network_ >= (int) sizeof (network) - 1) { + errno = EINVAL; + return -1; + } + memset (network, '\0', sizeof (network)); + memcpy (network, network_, port_delim - network_); + + pgm_error_t *pgm_error = NULL; + struct pgm_addrinfo_t hints; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + if (!pgm_getaddrinfo (network, NULL, res, &pgm_error)) { + // Invalid parameters don't set pgm_error_t. + zmq_assert (pgm_error != NULL); + if (pgm_error->domain == PGM_ERROR_DOMAIN_IF && + + // NB: cannot catch EAI_BADFLAGS. + (pgm_error->code != PGM_ERROR_SERVICE + && pgm_error->code != PGM_ERROR_SOCKTNOSUPPORT)) { + // User, host, or network configuration or transient error. + pgm_error_free (pgm_error); + errno = EINVAL; + return -1; + } + + // Fatal OpenPGM internal error. + zmq_assert (false); + } + return 0; +} + +// Create, bind and connect PGM socket. +int zmq::pgm_socket_t::init (bool udp_encapsulation_, const char *network_) +{ + // Can not open transport before destroying old one. + zmq_assert (sock == NULL); + zmq_assert (options.rate > 0); + + // Zero counter used in msgrecv. + nbytes_rec = 0; + nbytes_processed = 0; + pgm_msgv_processed = 0; + + uint16_t port_number; + struct pgm_addrinfo_t *res = NULL; + sa_family_t sa_family; + + pgm_error_t *pgm_error = NULL; + + if (init_address (network_, &res, &port_number) < 0) { + goto err_abort; + } + + zmq_assert (res != NULL); + + // Pick up detected IP family. + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + // Create IP/PGM or UDP/PGM socket. + if (udp_encapsulation_) { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, + &pgm_error)) { + // Invalid parameters don't set pgm_error_t. + zmq_assert (pgm_error != NULL); + if (pgm_error->domain == PGM_ERROR_DOMAIN_SOCKET + && (pgm_error->code != PGM_ERROR_BADF + && pgm_error->code != PGM_ERROR_FAULT + && pgm_error->code != PGM_ERROR_NOPROTOOPT + && pgm_error->code != PGM_ERROR_FAILED)) + + // User, host, or network configuration or transient error. + goto err_abort; + + // Fatal OpenPGM internal error. + zmq_assert (false); + } + + // All options are of data type int + const int encapsulation_port = port_number; + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, + &encapsulation_port, sizeof (encapsulation_port))) + goto err_abort; + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, + &encapsulation_port, sizeof (encapsulation_port))) + goto err_abort; + } else { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, + &pgm_error)) { + // Invalid parameters don't set pgm_error_t. + zmq_assert (pgm_error != NULL); + if (pgm_error->domain == PGM_ERROR_DOMAIN_SOCKET + && (pgm_error->code != PGM_ERROR_BADF + && pgm_error->code != PGM_ERROR_FAULT + && pgm_error->code != PGM_ERROR_NOPROTOOPT + && pgm_error->code != PGM_ERROR_FAILED)) + + // User, host, or network configuration or transient error. + goto err_abort; + + // Fatal OpenPGM internal error. + zmq_assert (false); + } + } + + { + const int rcvbuf = (int) options.rcvbuf; + if (rcvbuf >= 0) { + if (!pgm_setsockopt (sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, + sizeof (rcvbuf))) + goto err_abort; + } + + const int sndbuf = (int) options.sndbuf; + if (sndbuf >= 0) { + if (!pgm_setsockopt (sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, + sizeof (sndbuf))) + goto err_abort; + } + + const int max_tpdu = (int) options.multicast_maxtpdu; + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, + sizeof (max_tpdu))) + goto err_abort; + } + + if (receiver) { + const int recv_only = 1, rxw_max_tpdu = (int) options.multicast_maxtpdu, + rxw_sqns = compute_sqns (rxw_max_tpdu), + peer_expiry = pgm_secs (300), spmr_expiry = pgm_msecs (25), + nak_bo_ivl = pgm_msecs (50), nak_rpt_ivl = pgm_msecs (200), + nak_rdata_ivl = pgm_msecs (200), nak_data_retries = 50, + nak_ncf_retries = 50; + + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, + sizeof (recv_only)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_RXW_SQNS, &rxw_sqns, + sizeof (rxw_sqns)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_PEER_EXPIRY, + &peer_expiry, sizeof (peer_expiry)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, + &spmr_expiry, sizeof (spmr_expiry)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, + sizeof (nak_bo_ivl)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, + &nak_rpt_ivl, sizeof (nak_rpt_ivl)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, + &nak_rdata_ivl, sizeof (nak_rdata_ivl)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, + &nak_data_retries, sizeof (nak_data_retries)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, + &nak_ncf_retries, sizeof (nak_ncf_retries))) + goto err_abort; + } else { + const int send_only = 1, max_rte = (int) ((options.rate * 1000) / 8), + txw_max_tpdu = (int) options.multicast_maxtpdu, + txw_sqns = compute_sqns (txw_max_tpdu), + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { + pgm_msecs (100), pgm_msecs (100), pgm_msecs (100), + pgm_msecs (100), pgm_msecs (1300), pgm_secs (7), + pgm_secs (16), pgm_secs (25), pgm_secs (30)}; + + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_only, + sizeof (send_only)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_ODATA_MAX_RTE, &max_rte, + sizeof (max_rte)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_TXW_SQNS, &txw_sqns, + sizeof (txw_sqns)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_AMBIENT_SPM, + &ambient_spm, sizeof (ambient_spm)) + || !pgm_setsockopt (sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, + &heartbeat_spm, sizeof (heartbeat_spm))) + goto err_abort; + } + + // PGM transport GSI. + struct pgm_sockaddr_t addr; + + memset (&addr, 0, sizeof (addr)); + addr.sa_port = port_number; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + + // Create random GSI. + uint32_t buf[2]; + buf[0] = generate_random (); + buf[1] = generate_random (); + if (!pgm_gsi_create_from_data (&addr.sa_addr.gsi, (uint8_t *) buf, 8)) + goto err_abort; + + + // Bind a transport to the specified network devices. + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof (if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof (sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, &addr, sizeof (addr), &if_req, sizeof (if_req), + &if_req, sizeof (if_req), &pgm_error)) { + // Invalid parameters don't set pgm_error_t. + zmq_assert (pgm_error != NULL); + if ((pgm_error->domain == PGM_ERROR_DOMAIN_SOCKET + || pgm_error->domain == PGM_ERROR_DOMAIN_IF) + && (pgm_error->code != PGM_ERROR_INVAL + && pgm_error->code != PGM_ERROR_BADF + && pgm_error->code != PGM_ERROR_FAULT)) + + // User, host, or network configuration or transient error. + goto err_abort; + + // Fatal OpenPGM internal error. + zmq_assert (false); + } + + // Join IP multicast groups. + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) { + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_JOIN_GROUP, + &res->ai_recv_addrs[i], sizeof (struct group_req))) + goto err_abort; + } + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_GROUP, + &res->ai_send_addrs[0], sizeof (struct group_req))) + goto err_abort; + + pgm_freeaddrinfo (res); + res = NULL; + + // Set IP level parameters. + { + // Multicast loopback disabled by default + const int multicast_loop = 0; + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, + &multicast_loop, sizeof (multicast_loop))) + goto err_abort; + + const int multicast_hops = options.multicast_hops; + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, + &multicast_hops, sizeof (multicast_hops))) + goto err_abort; + + // Expedited Forwarding PHB for network elements, no ECN. + // Ignore return value due to varied runtime support. + const int dscp = 0x2e << 2; + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof (dscp)); + + const int nonblocking = 1; + if (!pgm_setsockopt (sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, + sizeof (nonblocking))) + goto err_abort; + } + + // Connect PGM transport to start state machine. + if (!pgm_connect (sock, &pgm_error)) { + // Invalid parameters don't set pgm_error_t. + zmq_assert (pgm_error != NULL); + goto err_abort; + } + + // For receiver transport preallocate pgm_msgv array. + if (receiver) { + zmq_assert (options.in_batch_size > 0); + size_t max_tsdu_size = get_max_tsdu_size (); + pgm_msgv_len = (int) options.in_batch_size / max_tsdu_size; + if ((int) options.in_batch_size % max_tsdu_size) + pgm_msgv_len++; + zmq_assert (pgm_msgv_len); + + pgm_msgv = (pgm_msgv_t *) malloc (sizeof (pgm_msgv_t) * pgm_msgv_len); + alloc_assert (pgm_msgv); + } + + return 0; + +err_abort: + if (sock != NULL) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (res != NULL) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (pgm_error != NULL) { + pgm_error_free (pgm_error); + pgm_error = NULL; + } + errno = EINVAL; + return -1; +} + +zmq::pgm_socket_t::~pgm_socket_t () +{ + if (pgm_msgv) + free (pgm_msgv); + if (sock) + pgm_close (sock, TRUE); +} + +// Get receiver fds. receive_fd_ is signaled for incoming packets, +// waiting_pipe_fd_ is signaled for state driven events and data. +void zmq::pgm_socket_t::get_receiver_fds (fd_t *receive_fd_, + fd_t *waiting_pipe_fd_) +{ + socklen_t socklen; + bool rc; + + zmq_assert (receive_fd_); + zmq_assert (waiting_pipe_fd_); + + socklen = sizeof (*receive_fd_); + rc = + pgm_getsockopt (sock, IPPROTO_PGM, PGM_RECV_SOCK, receive_fd_, &socklen); + zmq_assert (rc); + zmq_assert (socklen == sizeof (*receive_fd_)); + + socklen = sizeof (*waiting_pipe_fd_); + rc = pgm_getsockopt (sock, IPPROTO_PGM, PGM_PENDING_SOCK, waiting_pipe_fd_, + &socklen); + zmq_assert (rc); + zmq_assert (socklen == sizeof (*waiting_pipe_fd_)); +} + +// Get fds and store them into user allocated memory. +// send_fd is for non-blocking send wire notifications. +// receive_fd_ is for incoming back-channel protocol packets. +// rdata_notify_fd_ is raised for waiting repair transmissions. +// pending_notify_fd_ is for state driven events. +void zmq::pgm_socket_t::get_sender_fds (fd_t *send_fd_, + fd_t *receive_fd_, + fd_t *rdata_notify_fd_, + fd_t *pending_notify_fd_) +{ + socklen_t socklen; + bool rc; + + zmq_assert (send_fd_); + zmq_assert (receive_fd_); + zmq_assert (rdata_notify_fd_); + zmq_assert (pending_notify_fd_); + + socklen = sizeof (*send_fd_); + rc = pgm_getsockopt (sock, IPPROTO_PGM, PGM_SEND_SOCK, send_fd_, &socklen); + zmq_assert (rc); + zmq_assert (socklen == sizeof (*receive_fd_)); + + socklen = sizeof (*receive_fd_); + rc = + pgm_getsockopt (sock, IPPROTO_PGM, PGM_RECV_SOCK, receive_fd_, &socklen); + zmq_assert (rc); + zmq_assert (socklen == sizeof (*receive_fd_)); + + socklen = sizeof (*rdata_notify_fd_); + rc = pgm_getsockopt (sock, IPPROTO_PGM, PGM_REPAIR_SOCK, rdata_notify_fd_, + &socklen); + zmq_assert (rc); + zmq_assert (socklen == sizeof (*rdata_notify_fd_)); + + socklen = sizeof (*pending_notify_fd_); + rc = pgm_getsockopt (sock, IPPROTO_PGM, PGM_PENDING_SOCK, + pending_notify_fd_, &socklen); + zmq_assert (rc); + zmq_assert (socklen == sizeof (*pending_notify_fd_)); +} + +// Send one APDU, transmit window owned memory. +// data_len_ must be less than one TPDU. +size_t zmq::pgm_socket_t::send (unsigned char *data_, size_t data_len_) +{ + size_t nbytes = 0; + + const int status = pgm_send (sock, data_, data_len_, &nbytes); + + // We have to write all data as one packet. + if (nbytes > 0) { + zmq_assert (status == PGM_IO_STATUS_NORMAL); + zmq_assert (nbytes == data_len_); + } else { + zmq_assert (status == PGM_IO_STATUS_RATE_LIMITED + || status == PGM_IO_STATUS_WOULD_BLOCK); + + if (status == PGM_IO_STATUS_RATE_LIMITED) + errno = ENOMEM; + else + errno = EBUSY; + } + + // Save return value. + last_tx_status = status; + + return nbytes; +} + +long zmq::pgm_socket_t::get_rx_timeout () +{ + if (last_rx_status != PGM_IO_STATUS_RATE_LIMITED + && last_rx_status != PGM_IO_STATUS_TIMER_PENDING) + return -1; + + struct timeval tv; + socklen_t optlen = sizeof (tv); + const bool rc = pgm_getsockopt (sock, IPPROTO_PGM, + last_rx_status == PGM_IO_STATUS_RATE_LIMITED + ? PGM_RATE_REMAIN + : PGM_TIME_REMAIN, + &tv, &optlen); + zmq_assert (rc); + + const long timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + + return timeout; +} + +long zmq::pgm_socket_t::get_tx_timeout () +{ + if (last_tx_status != PGM_IO_STATUS_RATE_LIMITED) + return -1; + + struct timeval tv; + socklen_t optlen = sizeof (tv); + const bool rc = + pgm_getsockopt (sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + zmq_assert (rc); + + const long timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + + return timeout; +} + +// Return max TSDU size without fragmentation from current PGM transport. +size_t zmq::pgm_socket_t::get_max_tsdu_size () +{ + int max_tsdu = 0; + socklen_t optlen = sizeof (max_tsdu); + + bool rc = pgm_getsockopt (sock, IPPROTO_PGM, PGM_MSS, &max_tsdu, &optlen); + zmq_assert (rc); + zmq_assert (optlen == sizeof (max_tsdu)); + return (size_t) max_tsdu; +} + +// pgm_recvmsgv is called to fill the pgm_msgv array up to pgm_msgv_len. +// In subsequent calls data from pgm_msgv structure are returned. +ssize_t zmq::pgm_socket_t::receive (void **raw_data_, const pgm_tsi_t **tsi_) +{ + size_t raw_data_len = 0; + + // We just sent all data from pgm_transport_recvmsgv up + // and have to return 0 that another engine in this thread is scheduled. + if (nbytes_rec == nbytes_processed && nbytes_rec > 0) { + // Reset all the counters. + nbytes_rec = 0; + nbytes_processed = 0; + pgm_msgv_processed = 0; + errno = EAGAIN; + return 0; + } + + // If we have are going first time or if we have processed all pgm_msgv_t + // structure previously read from the pgm socket. + if (nbytes_rec == nbytes_processed) { + // Check program flow. + zmq_assert (pgm_msgv_processed == 0); + zmq_assert (nbytes_processed == 0); + zmq_assert (nbytes_rec == 0); + + // Receive a vector of Application Protocol Domain Unit's (APDUs) + // from the transport. + pgm_error_t *pgm_error = NULL; + + const int status = pgm_recvmsgv (sock, pgm_msgv, pgm_msgv_len, + MSG_ERRQUEUE, &nbytes_rec, &pgm_error); + + // Invalid parameters. + zmq_assert (status != PGM_IO_STATUS_ERROR); + + last_rx_status = status; + + // In a case when no ODATA/RDATA fired POLLIN event (SPM...) + // pgm_recvmsg returns PGM_IO_STATUS_TIMER_PENDING. + if (status == PGM_IO_STATUS_TIMER_PENDING) { + zmq_assert (nbytes_rec == 0); + + // In case if no RDATA/ODATA caused POLLIN 0 is + // returned. + nbytes_rec = 0; + errno = EBUSY; + return 0; + } + + // Send SPMR, NAK, ACK is rate limited. + if (status == PGM_IO_STATUS_RATE_LIMITED) { + zmq_assert (nbytes_rec == 0); + + // In case if no RDATA/ODATA caused POLLIN 0 is returned. + nbytes_rec = 0; + errno = ENOMEM; + return 0; + } + + // No peers and hence no incoming packets. + if (status == PGM_IO_STATUS_WOULD_BLOCK) { + zmq_assert (nbytes_rec == 0); + + // In case if no RDATA/ODATA caused POLLIN 0 is returned. + nbytes_rec = 0; + errno = EAGAIN; + return 0; + } + + // Data loss. + if (status == PGM_IO_STATUS_RESET) { + struct pgm_sk_buff_t *skb = pgm_msgv[0].msgv_skb[0]; + + // Save lost data TSI. + *tsi_ = &skb->tsi; + nbytes_rec = 0; + + // In case of dala loss -1 is returned. + errno = EINVAL; + pgm_free_skb (skb); + return -1; + } + + zmq_assert (status == PGM_IO_STATUS_NORMAL); + } else { + zmq_assert (pgm_msgv_processed <= pgm_msgv_len); + } + + // Zero byte payloads are valid in PGM, but not 0MQ protocol. + zmq_assert (nbytes_rec > 0); + + // Only one APDU per pgm_msgv_t structure is allowed. + zmq_assert (pgm_msgv[pgm_msgv_processed].msgv_len == 1); + + struct pgm_sk_buff_t *skb = pgm_msgv[pgm_msgv_processed].msgv_skb[0]; + + // Take pointers from pgm_msgv_t structure. + *raw_data_ = skb->data; + raw_data_len = skb->len; + + // Save current TSI. + *tsi_ = &skb->tsi; + + // Move the the next pgm_msgv_t structure. + pgm_msgv_processed++; + zmq_assert (pgm_msgv_processed <= pgm_msgv_len); + nbytes_processed += raw_data_len; + + return raw_data_len; +} + +void zmq::pgm_socket_t::process_upstream () +{ + pgm_msgv_t dummy_msg; + + size_t dummy_bytes = 0; + pgm_error_t *pgm_error = NULL; + + const int status = pgm_recvmsgv (sock, &dummy_msg, 1, MSG_ERRQUEUE, + &dummy_bytes, &pgm_error); + + // Invalid parameters. + zmq_assert (status != PGM_IO_STATUS_ERROR); + + // No data should be returned. + zmq_assert (dummy_bytes == 0 + && (status == PGM_IO_STATUS_TIMER_PENDING + || status == PGM_IO_STATUS_RATE_LIMITED + || status == PGM_IO_STATUS_WOULD_BLOCK)); + + last_rx_status = status; + + if (status == PGM_IO_STATUS_TIMER_PENDING) + errno = EBUSY; + else if (status == PGM_IO_STATUS_RATE_LIMITED) + errno = ENOMEM; + else + errno = EAGAIN; +} + +int zmq::pgm_socket_t::compute_sqns (int tpdu_) +{ + // Convert rate into B/ms. + uint64_t rate = uint64_t (options.rate) / 8; + + // Compute the size of the buffer in bytes. + uint64_t size = uint64_t (options.recovery_ivl) * rate; + + // Translate the size into number of packets. + uint64_t sqns = size / tpdu_; + + // Buffer should be able to hold at least one packet. + if (sqns == 0) + sqns = 1; + + return (int) sqns; +} + +#endif diff --git a/3rd/libzmq/src/pgm_socket.hpp b/3rd/libzmq/src/pgm_socket.hpp new file mode 100644 index 00000000..00ec4856 --- /dev/null +++ b/3rd/libzmq/src/pgm_socket.hpp @@ -0,0 +1,128 @@ +/* + 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 . +*/ + +#ifndef __PGM_SOCKET_HPP_INCLUDED__ +#define __PGM_SOCKET_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_OPENPGM + +#ifdef ZMQ_HAVE_WINDOWS +#define __PGM_WININT_H__ +#endif + +#include + +#if defined(ZMQ_HAVE_OSX) || defined(ZMQ_HAVE_NETBSD) +#include +#endif + +#include "fd.hpp" +#include "options.hpp" + +namespace zmq +{ +// Encapsulates PGM socket. +class pgm_socket_t +{ + public: + // If receiver_ is true PGM transport is not generating SPM packets. + pgm_socket_t (bool receiver_, const options_t &options_); + + // Closes the transport. + ~pgm_socket_t (); + + // Initialize PGM network structures (GSI, GSRs). + int init (bool udp_encapsulation_, const char *network_); + + // Resolve PGM socket address. + static int init_address (const char *network_, + struct pgm_addrinfo_t **addr, + uint16_t *port_number); + + // Get receiver fds and store them into user allocated memory. + void get_receiver_fds (fd_t *receive_fd_, fd_t *waiting_pipe_fd_); + + // Get sender and receiver fds and store it to user allocated + // memory. Receive fd is used to process NAKs from peers. + void get_sender_fds (fd_t *send_fd_, + fd_t *receive_fd_, + fd_t *rdata_notify_fd_, + fd_t *pending_notify_fd_); + + // Send data as one APDU, transmit window owned memory. + size_t send (unsigned char *data_, size_t data_len_); + + // Returns max tsdu size without fragmentation. + size_t get_max_tsdu_size (); + + // Receive data from pgm socket. + ssize_t receive (void **data_, const pgm_tsi_t **tsi_); + + long get_rx_timeout (); + long get_tx_timeout (); + + // POLLIN on sender side should mean NAK or SPMR receiving. + // process_upstream function is used to handle such a situation. + void process_upstream (); + + private: + // Compute size of the buffer based on rate and recovery interval. + int compute_sqns (int tpdu_); + + // OpenPGM transport. + pgm_sock_t *sock; + + int last_rx_status, last_tx_status; + + // Associated socket options. + options_t options; + + // true when pgm_socket should create receiving side. + bool receiver; + + // Array of pgm_msgv_t structures to store received data + // from the socket (pgm_transport_recvmsgv). + pgm_msgv_t *pgm_msgv; + + // Size of pgm_msgv array. + size_t pgm_msgv_len; + + // How many bytes were read from pgm socket. + size_t nbytes_rec; + + // How many bytes were processed from last pgm socket read. + size_t nbytes_processed; + + // How many messages from pgm_msgv were already sent up. + size_t pgm_msgv_processed; +}; +} +#endif + +#endif diff --git a/3rd/libzmq/src/pipe.cpp b/3rd/libzmq/src/pipe.cpp new file mode 100644 index 00000000..b391b6ea --- /dev/null +++ b/3rd/libzmq/src/pipe.cpp @@ -0,0 +1,617 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "macros.hpp" +#include "pipe.hpp" +#include "err.hpp" + +#include "ypipe.hpp" +#include "ypipe_conflate.hpp" + +int zmq::pipepair (object_t *parents_[2], + pipe_t *pipes_[2], + const int hwms_[2], + const bool conflate_[2]) +{ + // Creates two pipe objects. These objects are connected by two ypipes, + // each to pass messages in one direction. + + typedef ypipe_t upipe_normal_t; + typedef ypipe_conflate_t upipe_conflate_t; + + pipe_t::upipe_t *upipe1; + if (conflate_[0]) + upipe1 = new (std::nothrow) upipe_conflate_t (); + else + upipe1 = new (std::nothrow) upipe_normal_t (); + alloc_assert (upipe1); + + pipe_t::upipe_t *upipe2; + if (conflate_[1]) + upipe2 = new (std::nothrow) upipe_conflate_t (); + else + upipe2 = new (std::nothrow) upipe_normal_t (); + alloc_assert (upipe2); + + pipes_[0] = new (std::nothrow) + pipe_t (parents_[0], upipe1, upipe2, hwms_[1], hwms_[0], conflate_[0]); + alloc_assert (pipes_[0]); + pipes_[1] = new (std::nothrow) + pipe_t (parents_[1], upipe2, upipe1, hwms_[0], hwms_[1], conflate_[1]); + alloc_assert (pipes_[1]); + + pipes_[0]->set_peer (pipes_[1]); + pipes_[1]->set_peer (pipes_[0]); + + return 0; +} + +void zmq::send_routing_id (pipe_t *pipe_, const options_t &options_) +{ + zmq::msg_t id; + const int rc = id.init_size (options_.routing_id_size); + errno_assert (rc == 0); + memcpy (id.data (), options_.routing_id, options_.routing_id_size); + id.set_flags (zmq::msg_t::routing_id); + const bool written = pipe_->write (&id); + zmq_assert (written); + pipe_->flush (); +} + +void zmq::send_hello_msg (pipe_t *pipe_, const options_t &options_) +{ + zmq::msg_t hello; + const int rc = + hello.init_buffer (&options_.hello_msg[0], options_.hello_msg.size ()); + errno_assert (rc == 0); + const bool written = pipe_->write (&hello); + zmq_assert (written); + pipe_->flush (); +} + +zmq::pipe_t::pipe_t (object_t *parent_, + upipe_t *inpipe_, + upipe_t *outpipe_, + int inhwm_, + int outhwm_, + bool conflate_) : + object_t (parent_), + _in_pipe (inpipe_), + _out_pipe (outpipe_), + _in_active (true), + _out_active (true), + _hwm (outhwm_), + _lwm (compute_lwm (inhwm_)), + _in_hwm_boost (-1), + _out_hwm_boost (-1), + _msgs_read (0), + _msgs_written (0), + _peers_msgs_read (0), + _peer (NULL), + _sink (NULL), + _state (active), + _delay (true), + _server_socket_routing_id (0), + _conflate (conflate_) +{ + _disconnect_msg.init (); +} + +zmq::pipe_t::~pipe_t () +{ + _disconnect_msg.close (); +} + +void zmq::pipe_t::set_peer (pipe_t *peer_) +{ + // Peer can be set once only. + zmq_assert (!_peer); + _peer = peer_; +} + +void zmq::pipe_t::set_event_sink (i_pipe_events *sink_) +{ + // Sink can be set once only. + zmq_assert (!_sink); + _sink = sink_; +} + +void zmq::pipe_t::set_server_socket_routing_id ( + uint32_t server_socket_routing_id_) +{ + _server_socket_routing_id = server_socket_routing_id_; +} + +uint32_t zmq::pipe_t::get_server_socket_routing_id () const +{ + return _server_socket_routing_id; +} + +void zmq::pipe_t::set_router_socket_routing_id ( + const blob_t &router_socket_routing_id_) +{ + _router_socket_routing_id.set_deep_copy (router_socket_routing_id_); +} + +const zmq::blob_t &zmq::pipe_t::get_routing_id () const +{ + return _router_socket_routing_id; +} + +bool zmq::pipe_t::check_read () +{ + if (unlikely (!_in_active)) + return false; + if (unlikely (_state != active && _state != waiting_for_delimiter)) + return false; + + // Check if there's an item in the pipe. + if (!_in_pipe->check_read ()) { + _in_active = false; + return false; + } + + // If the next item in the pipe is message delimiter, + // initiate termination process. + if (_in_pipe->probe (is_delimiter)) { + msg_t msg; + const bool ok = _in_pipe->read (&msg); + zmq_assert (ok); + process_delimiter (); + return false; + } + + return true; +} + +bool zmq::pipe_t::read (msg_t *msg_) +{ + if (unlikely (!_in_active)) + return false; + if (unlikely (_state != active && _state != waiting_for_delimiter)) + return false; + + while (true) { + if (!_in_pipe->read (msg_)) { + _in_active = false; + return false; + } + + // If this is a credential, ignore it and receive next message. + if (unlikely (msg_->is_credential ())) { + const int rc = msg_->close (); + zmq_assert (rc == 0); + } else { + break; + } + } + + // If delimiter was read, start termination process of the pipe. + if (msg_->is_delimiter ()) { + process_delimiter (); + return false; + } + + if (!(msg_->flags () & msg_t::more) && !msg_->is_routing_id ()) + _msgs_read++; + + if (_lwm > 0 && _msgs_read % _lwm == 0) + send_activate_write (_peer, _msgs_read); + + return true; +} + +bool zmq::pipe_t::check_write () +{ + if (unlikely (!_out_active || _state != active)) + return false; + + const bool full = !check_hwm (); + + if (unlikely (full)) { + _out_active = false; + return false; + } + + return true; +} + +bool zmq::pipe_t::write (const msg_t *msg_) +{ + if (unlikely (!check_write ())) + return false; + + const bool more = (msg_->flags () & msg_t::more) != 0; + const bool is_routing_id = msg_->is_routing_id (); + _out_pipe->write (*msg_, more); + if (!more && !is_routing_id) + _msgs_written++; + + return true; +} + +void zmq::pipe_t::rollback () const +{ + // Remove incomplete message from the outbound pipe. + msg_t msg; + if (_out_pipe) { + while (_out_pipe->unwrite (&msg)) { + zmq_assert (msg.flags () & msg_t::more); + const int rc = msg.close (); + errno_assert (rc == 0); + } + } +} + +void zmq::pipe_t::flush () +{ + // The peer does not exist anymore at this point. + if (_state == term_ack_sent) + return; + + if (_out_pipe && !_out_pipe->flush ()) + send_activate_read (_peer); +} + +void zmq::pipe_t::process_activate_read () +{ + if (!_in_active && (_state == active || _state == waiting_for_delimiter)) { + _in_active = true; + _sink->read_activated (this); + } +} + +void zmq::pipe_t::process_activate_write (uint64_t msgs_read_) +{ + // Remember the peer's message sequence number. + _peers_msgs_read = msgs_read_; + + if (!_out_active && _state == active) { + _out_active = true; + _sink->write_activated (this); + } +} + +void zmq::pipe_t::process_hiccup (void *pipe_) +{ + // Destroy old outpipe. Note that the read end of the pipe was already + // migrated to this thread. + zmq_assert (_out_pipe); + _out_pipe->flush (); + msg_t msg; + while (_out_pipe->read (&msg)) { + if (!(msg.flags () & msg_t::more)) + _msgs_written--; + const int rc = msg.close (); + errno_assert (rc == 0); + } + LIBZMQ_DELETE (_out_pipe); + + // Plug in the new outpipe. + zmq_assert (pipe_); + _out_pipe = static_cast (pipe_); + _out_active = true; + + // If appropriate, notify the user about the hiccup. + if (_state == active) + _sink->hiccuped (this); +} + +void zmq::pipe_t::process_pipe_term () +{ + zmq_assert (_state == active || _state == delimiter_received + || _state == term_req_sent1); + + // This is the simple case of peer-induced termination. If there are no + // more pending messages to read, or if the pipe was configured to drop + // pending messages, we can move directly to the term_ack_sent state. + // Otherwise we'll hang up in waiting_for_delimiter state till all + // pending messages are read. + if (_state == active) { + if (_delay) + _state = waiting_for_delimiter; + else { + _state = term_ack_sent; + _out_pipe = NULL; + send_pipe_term_ack (_peer); + } + } + + // Delimiter happened to arrive before the term command. Now we have the + // term command as well, so we can move straight to term_ack_sent state. + else if (_state == delimiter_received) { + _state = term_ack_sent; + _out_pipe = NULL; + send_pipe_term_ack (_peer); + } + + // This is the case where both ends of the pipe are closed in parallel. + // We simply reply to the request by ack and continue waiting for our + // own ack. + else if (_state == term_req_sent1) { + _state = term_req_sent2; + _out_pipe = NULL; + send_pipe_term_ack (_peer); + } +} + +void zmq::pipe_t::process_pipe_term_ack () +{ + // Notify the user that all the references to the pipe should be dropped. + zmq_assert (_sink); + _sink->pipe_terminated (this); + + // In term_ack_sent and term_req_sent2 states there's nothing to do. + // Simply deallocate the pipe. In term_req_sent1 state we have to ack + // the peer before deallocating this side of the pipe. + // All the other states are invalid. + if (_state == term_req_sent1) { + _out_pipe = NULL; + send_pipe_term_ack (_peer); + } else + zmq_assert (_state == term_ack_sent || _state == term_req_sent2); + + // We'll deallocate the inbound pipe, the peer will deallocate the outbound + // pipe (which is an inbound pipe from its point of view). + // First, delete all the unread messages in the pipe. We have to do it by + // hand because msg_t doesn't have automatic destructor. Then deallocate + // the ypipe itself. + + if (!_conflate) { + msg_t msg; + while (_in_pipe->read (&msg)) { + const int rc = msg.close (); + errno_assert (rc == 0); + } + } + + LIBZMQ_DELETE (_in_pipe); + + // Deallocate the pipe object + delete this; +} + +void zmq::pipe_t::process_pipe_hwm (int inhwm_, int outhwm_) +{ + set_hwms (inhwm_, outhwm_); +} + +void zmq::pipe_t::set_nodelay () +{ + this->_delay = false; +} + +void zmq::pipe_t::terminate (bool delay_) +{ + // Overload the value specified at pipe creation. + _delay = delay_; + + // If terminate was already called, we can ignore the duplicate invocation. + if (_state == term_req_sent1 || _state == term_req_sent2) { + return; + } + // If the pipe is in the final phase of async termination, it's going to + // closed anyway. No need to do anything special here. + if (_state == term_ack_sent) { + return; + } + // The simple sync termination case. Ask the peer to terminate and wait + // for the ack. + if (_state == active) { + send_pipe_term (_peer); + _state = term_req_sent1; + } + // There are still pending messages available, but the user calls + // 'terminate'. We can act as if all the pending messages were read. + else if (_state == waiting_for_delimiter && !_delay) { + // Drop any unfinished outbound messages. + rollback (); + _out_pipe = NULL; + send_pipe_term_ack (_peer); + _state = term_ack_sent; + } + // If there are pending messages still available, do nothing. + else if (_state == waiting_for_delimiter) { + } + // We've already got delimiter, but not term command yet. We can ignore + // the delimiter and ack synchronously terminate as if we were in + // active state. + else if (_state == delimiter_received) { + send_pipe_term (_peer); + _state = term_req_sent1; + } + // There are no other states. + else { + zmq_assert (false); + } + + // Stop outbound flow of messages. + _out_active = false; + + if (_out_pipe) { + // Drop any unfinished outbound messages. + rollback (); + + // Write the delimiter into the pipe. Note that watermarks are not + // checked; thus the delimiter can be written even when the pipe is full. + msg_t msg; + msg.init_delimiter (); + _out_pipe->write (msg, false); + flush (); + } +} + +bool zmq::pipe_t::is_delimiter (const msg_t &msg_) +{ + return msg_.is_delimiter (); +} + +int zmq::pipe_t::compute_lwm (int hwm_) +{ + // Compute the low water mark. Following point should be taken + // into consideration: + // + // 1. LWM has to be less than HWM. + // 2. LWM cannot be set to very low value (such as zero) as after filling + // the queue it would start to refill only after all the messages are + // read from it and thus unnecessarily hold the progress back. + // 3. LWM cannot be set to very high value (such as HWM-1) as it would + // result in lock-step filling of the queue - if a single message is + // read from a full queue, writer thread is resumed to write exactly one + // message to the queue and go back to sleep immediately. This would + // result in low performance. + // + // Given the 3. it would be good to keep HWM and LWM as far apart as + // possible to reduce the thread switching overhead to almost zero. + // Let's make LWM 1/2 of HWM. + const int result = (hwm_ + 1) / 2; + + return result; +} + +void zmq::pipe_t::process_delimiter () +{ + zmq_assert (_state == active || _state == waiting_for_delimiter); + + if (_state == active) + _state = delimiter_received; + else { + rollback (); + _out_pipe = NULL; + send_pipe_term_ack (_peer); + _state = term_ack_sent; + } +} + +void zmq::pipe_t::hiccup () +{ + // If termination is already under way do nothing. + if (_state != active) + return; + + // We'll drop the pointer to the inpipe. From now on, the peer is + // responsible for deallocating it. + + // Create new inpipe. + _in_pipe = + _conflate + ? static_cast (new (std::nothrow) ypipe_conflate_t ()) + : new (std::nothrow) ypipe_t (); + + alloc_assert (_in_pipe); + _in_active = true; + + // Notify the peer about the hiccup. + send_hiccup (_peer, _in_pipe); +} + +void zmq::pipe_t::set_hwms (int inhwm_, int outhwm_) +{ + int in = inhwm_ + std::max (_in_hwm_boost, 0); + int out = outhwm_ + std::max (_out_hwm_boost, 0); + + // if either send or recv side has hwm <= 0 it means infinite so we should set hwms infinite + if (inhwm_ <= 0 || _in_hwm_boost == 0) + in = 0; + + if (outhwm_ <= 0 || _out_hwm_boost == 0) + out = 0; + + _lwm = compute_lwm (in); + _hwm = out; +} + +void zmq::pipe_t::set_hwms_boost (int inhwmboost_, int outhwmboost_) +{ + _in_hwm_boost = inhwmboost_; + _out_hwm_boost = outhwmboost_; +} + +bool zmq::pipe_t::check_hwm () const +{ + const bool full = + _hwm > 0 && _msgs_written - _peers_msgs_read >= uint64_t (_hwm); + return !full; +} + +void zmq::pipe_t::send_hwms_to_peer (int inhwm_, int outhwm_) +{ + send_pipe_hwm (_peer, inhwm_, outhwm_); +} + +void zmq::pipe_t::set_endpoint_pair (zmq::endpoint_uri_pair_t endpoint_pair_) +{ + _endpoint_pair = ZMQ_MOVE (endpoint_pair_); +} + +const zmq::endpoint_uri_pair_t &zmq::pipe_t::get_endpoint_pair () const +{ + return _endpoint_pair; +} + +void zmq::pipe_t::send_stats_to_peer (own_t *socket_base_) +{ + endpoint_uri_pair_t *ep = + new (std::nothrow) endpoint_uri_pair_t (_endpoint_pair); + send_pipe_peer_stats (_peer, _msgs_written - _peers_msgs_read, socket_base_, + ep); +} + +void zmq::pipe_t::process_pipe_peer_stats (uint64_t queue_count_, + own_t *socket_base_, + endpoint_uri_pair_t *endpoint_pair_) +{ + send_pipe_stats_publish (socket_base_, queue_count_, + _msgs_written - _peers_msgs_read, endpoint_pair_); +} + +void zmq::pipe_t::send_disconnect_msg () +{ + if (_disconnect_msg.size () > 0) { + // Rollback any incomplete message in the pipe, and push the disconnect message. + rollback (); + + _out_pipe->write (_disconnect_msg, false); + flush (); + _disconnect_msg.init (); + } +} + +void zmq::pipe_t::set_disconnect_msg ( + const std::vector &disconnect_) +{ + _disconnect_msg.close (); + const int rc = + _disconnect_msg.init_buffer (&disconnect_[0], disconnect_.size ()); + errno_assert (rc == 0); +} diff --git a/3rd/libzmq/src/pipe.hpp b/3rd/libzmq/src/pipe.hpp new file mode 100644 index 00000000..8136b860 --- /dev/null +++ b/3rd/libzmq/src/pipe.hpp @@ -0,0 +1,274 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PIPE_HPP_INCLUDED__ +#define __ZMQ_PIPE_HPP_INCLUDED__ + +#include "ypipe_base.hpp" +#include "config.hpp" +#include "object.hpp" +#include "stdint.hpp" +#include "array.hpp" +#include "blob.hpp" +#include "options.hpp" +#include "endpoint.hpp" +#include "msg.hpp" + +namespace zmq +{ +class pipe_t; + +// Create a pipepair for bi-directional transfer of messages. +// First HWM is for messages passed from first pipe to the second pipe. +// Second HWM is for messages passed from second pipe to the first pipe. +// Delay specifies how the pipe behaves when the peer terminates. If true +// pipe receives all the pending messages before terminating, otherwise it +// terminates straight away. +// If conflate is true, only the most recently arrived message could be +// read (older messages are discarded) +int pipepair (zmq::object_t *parents_[2], + zmq::pipe_t *pipes_[2], + const int hwms_[2], + const bool conflate_[2]); + +struct i_pipe_events +{ + virtual ~i_pipe_events () ZMQ_DEFAULT; + + virtual void read_activated (zmq::pipe_t *pipe_) = 0; + virtual void write_activated (zmq::pipe_t *pipe_) = 0; + virtual void hiccuped (zmq::pipe_t *pipe_) = 0; + virtual void pipe_terminated (zmq::pipe_t *pipe_) = 0; +}; + +// Note that pipe can be stored in three different arrays. +// The array of inbound pipes (1), the array of outbound pipes (2) and +// the generic array of pipes to be deallocated (3). + +class pipe_t ZMQ_FINAL : public object_t, + public array_item_t<1>, + public array_item_t<2>, + public array_item_t<3> +{ + // This allows pipepair to create pipe objects. + friend int pipepair (zmq::object_t *parents_[2], + zmq::pipe_t *pipes_[2], + const int hwms_[2], + const bool conflate_[2]); + + public: + // Specifies the object to send events to. + void set_event_sink (i_pipe_events *sink_); + + // Pipe endpoint can store an routing ID to be used by its clients. + void set_server_socket_routing_id (uint32_t server_socket_routing_id_); + uint32_t get_server_socket_routing_id () const; + + // Pipe endpoint can store an opaque ID to be used by its clients. + void set_router_socket_routing_id (const blob_t &router_socket_routing_id_); + const blob_t &get_routing_id () const; + + // Returns true if there is at least one message to read in the pipe. + bool check_read (); + + // Reads a message to the underlying pipe. + bool read (msg_t *msg_); + + // Checks whether messages can be written to the pipe. If the pipe is + // closed or if writing the message would cause high watermark the + // function returns false. + bool check_write (); + + // Writes a message to the underlying pipe. Returns false if the + // message does not pass check_write. If false, the message object + // retains ownership of its message buffer. + bool write (const msg_t *msg_); + + // Remove unfinished parts of the outbound message from the pipe. + void rollback () const; + + // Flush the messages downstream. + void flush (); + + // Temporarily disconnects the inbound message stream and drops + // all the messages on the fly. Causes 'hiccuped' event to be generated + // in the peer. + void hiccup (); + + // Ensure the pipe won't block on receiving pipe_term. + void set_nodelay (); + + // Ask pipe to terminate. The termination will happen asynchronously + // and user will be notified about actual deallocation by 'terminated' + // event. If delay is true, the pending messages will be processed + // before actual shutdown. + void terminate (bool delay_); + + // Set the high water marks. + void set_hwms (int inhwm_, int outhwm_); + + // Set the boost to high water marks, used by inproc sockets so total hwm are sum of connect and bind sockets watermarks + void set_hwms_boost (int inhwmboost_, int outhwmboost_); + + // send command to peer for notify the change of hwm + void send_hwms_to_peer (int inhwm_, int outhwm_); + + // Returns true if HWM is not reached + bool check_hwm () const; + + void set_endpoint_pair (endpoint_uri_pair_t endpoint_pair_); + const endpoint_uri_pair_t &get_endpoint_pair () const; + + void send_stats_to_peer (own_t *socket_base_); + + void send_disconnect_msg (); + void set_disconnect_msg (const std::vector &disconnect_); + + private: + // Type of the underlying lock-free pipe. + typedef ypipe_base_t upipe_t; + + // Command handlers. + void process_activate_read () ZMQ_OVERRIDE; + void process_activate_write (uint64_t msgs_read_) ZMQ_OVERRIDE; + void process_hiccup (void *pipe_) ZMQ_OVERRIDE; + void + process_pipe_peer_stats (uint64_t queue_count_, + own_t *socket_base_, + endpoint_uri_pair_t *endpoint_pair_) ZMQ_OVERRIDE; + void process_pipe_term () ZMQ_OVERRIDE; + void process_pipe_term_ack () ZMQ_OVERRIDE; + void process_pipe_hwm (int inhwm_, int outhwm_) ZMQ_OVERRIDE; + + // Handler for delimiter read from the pipe. + void process_delimiter (); + + // Constructor is private. Pipe can only be created using + // pipepair function. + pipe_t (object_t *parent_, + upipe_t *inpipe_, + upipe_t *outpipe_, + int inhwm_, + int outhwm_, + bool conflate_); + + // Pipepair uses this function to let us know about + // the peer pipe object. + void set_peer (pipe_t *peer_); + + // Destructor is private. Pipe objects destroy themselves. + ~pipe_t () ZMQ_OVERRIDE; + + // Underlying pipes for both directions. + upipe_t *_in_pipe; + upipe_t *_out_pipe; + + // Can the pipe be read from / written to? + bool _in_active; + bool _out_active; + + // High watermark for the outbound pipe. + int _hwm; + + // Low watermark for the inbound pipe. + int _lwm; + + // boosts for high and low watermarks, used with inproc sockets so hwm are sum of send and recv hmws on each side of pipe + int _in_hwm_boost; + int _out_hwm_boost; + + // Number of messages read and written so far. + uint64_t _msgs_read; + uint64_t _msgs_written; + + // Last received peer's msgs_read. The actual number in the peer + // can be higher at the moment. + uint64_t _peers_msgs_read; + + // The pipe object on the other side of the pipepair. + pipe_t *_peer; + + // Sink to send events to. + i_pipe_events *_sink; + + // States of the pipe endpoint: + // active: common state before any termination begins, + // delimiter_received: delimiter was read from pipe before + // term command was received, + // waiting_for_delimiter: term command was already received + // from the peer but there are still pending messages to read, + // term_ack_sent: all pending messages were already read and + // all we are waiting for is ack from the peer, + // term_req_sent1: 'terminate' was explicitly called by the user, + // term_req_sent2: user called 'terminate' and then we've got + // term command from the peer as well. + enum + { + active, + delimiter_received, + waiting_for_delimiter, + term_ack_sent, + term_req_sent1, + term_req_sent2 + } _state; + + // If true, we receive all the pending inbound messages before + // terminating. If false, we terminate immediately when the peer + // asks us to. + bool _delay; + + // Routing id of the writer. Used uniquely by the reader side. + blob_t _router_socket_routing_id; + + // Routing id of the writer. Used uniquely by the reader side. + int _server_socket_routing_id; + + // Returns true if the message is delimiter; false otherwise. + static bool is_delimiter (const msg_t &msg_); + + // Computes appropriate low watermark from the given high watermark. + static int compute_lwm (int hwm_); + + const bool _conflate; + + // The endpoints of this pipe. + endpoint_uri_pair_t _endpoint_pair; + + // Disconnect msg + msg_t _disconnect_msg; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (pipe_t) +}; + +void send_routing_id (pipe_t *pipe_, const options_t &options_); + +void send_hello_msg (pipe_t *pipe_, const options_t &options_); +} + +#endif diff --git a/3rd/libzmq/src/plain_client.cpp b/3rd/libzmq/src/plain_client.cpp new file mode 100644 index 00000000..2af6d1dd --- /dev/null +++ b/3rd/libzmq/src/plain_client.cpp @@ -0,0 +1,224 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" + +#include +#include + +#include "msg.hpp" +#include "err.hpp" +#include "plain_client.hpp" +#include "session_base.hpp" +#include "plain_common.hpp" + +zmq::plain_client_t::plain_client_t (session_base_t *const session_, + const options_t &options_) : + mechanism_base_t (session_, options_), + _state (sending_hello) +{ +} + +zmq::plain_client_t::~plain_client_t () +{ +} + +int zmq::plain_client_t::next_handshake_command (msg_t *msg_) +{ + int rc = 0; + + switch (_state) { + case sending_hello: + produce_hello (msg_); + _state = waiting_for_welcome; + break; + case sending_initiate: + produce_initiate (msg_); + _state = waiting_for_ready; + break; + default: + errno = EAGAIN; + rc = -1; + } + return rc; +} + +int zmq::plain_client_t::process_handshake_command (msg_t *msg_) +{ + const unsigned char *cmd_data = + static_cast (msg_->data ()); + const size_t data_size = msg_->size (); + + int rc = 0; + if (data_size >= welcome_prefix_len + && !memcmp (cmd_data, welcome_prefix, welcome_prefix_len)) + rc = process_welcome (cmd_data, data_size); + else if (data_size >= ready_prefix_len + && !memcmp (cmd_data, ready_prefix, ready_prefix_len)) + rc = process_ready (cmd_data, data_size); + else if (data_size >= error_prefix_len + && !memcmp (cmd_data, error_prefix, error_prefix_len)) + rc = process_error (cmd_data, data_size); + else { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + rc = -1; + } + + if (rc == 0) { + rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + } + + return rc; +} + +zmq::mechanism_t::status_t zmq::plain_client_t::status () const +{ + switch (_state) { + case ready: + return mechanism_t::ready; + case error_command_received: + return mechanism_t::error; + default: + return mechanism_t::handshaking; + } +} + +void zmq::plain_client_t::produce_hello (msg_t *msg_) const +{ + const std::string username = options.plain_username; + zmq_assert (username.length () <= UCHAR_MAX); + + const std::string password = options.plain_password; + zmq_assert (password.length () <= UCHAR_MAX); + + const size_t command_size = hello_prefix_len + brief_len_size + + username.length () + brief_len_size + + password.length (); + + const int rc = msg_->init_size (command_size); + errno_assert (rc == 0); + + unsigned char *ptr = static_cast (msg_->data ()); + memcpy (ptr, hello_prefix, hello_prefix_len); + ptr += hello_prefix_len; + + *ptr++ = static_cast (username.length ()); + memcpy (ptr, username.c_str (), username.length ()); + ptr += username.length (); + + *ptr++ = static_cast (password.length ()); + memcpy (ptr, password.c_str (), password.length ()); +} + +int zmq::plain_client_t::process_welcome (const unsigned char *cmd_data_, + size_t data_size_) +{ + LIBZMQ_UNUSED (cmd_data_); + + if (_state != waiting_for_welcome) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + if (data_size_ != welcome_prefix_len) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME); + errno = EPROTO; + return -1; + } + _state = sending_initiate; + return 0; +} + +void zmq::plain_client_t::produce_initiate (msg_t *msg_) const +{ + make_command_with_basic_properties (msg_, initiate_prefix, + initiate_prefix_len); +} + +int zmq::plain_client_t::process_ready (const unsigned char *cmd_data_, + size_t data_size_) +{ + if (_state != waiting_for_ready) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + const int rc = parse_metadata (cmd_data_ + ready_prefix_len, + data_size_ - ready_prefix_len); + if (rc == 0) + _state = ready; + else + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA); + + return rc; +} + +int zmq::plain_client_t::process_error (const unsigned char *cmd_data_, + size_t data_size_) +{ + if (_state != waiting_for_welcome && _state != waiting_for_ready) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + const size_t start_of_error_reason = error_prefix_len + brief_len_size; + if (data_size_ < start_of_error_reason) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); + errno = EPROTO; + return -1; + } + const size_t error_reason_len = + static_cast (cmd_data_[error_prefix_len]); + if (error_reason_len > data_size_ - start_of_error_reason) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); + errno = EPROTO; + return -1; + } + const char *error_reason = + reinterpret_cast (cmd_data_) + start_of_error_reason; + handle_error_reason (error_reason, error_reason_len); + _state = error_command_received; + return 0; +} diff --git a/3rd/libzmq/src/plain_client.hpp b/3rd/libzmq/src/plain_client.hpp new file mode 100644 index 00000000..7b49d498 --- /dev/null +++ b/3rd/libzmq/src/plain_client.hpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PLAIN_CLIENT_HPP_INCLUDED__ +#define __ZMQ_PLAIN_CLIENT_HPP_INCLUDED__ + +#include "mechanism_base.hpp" +#include "options.hpp" + +namespace zmq +{ +class msg_t; + +class plain_client_t ZMQ_FINAL : public mechanism_base_t +{ + public: + plain_client_t (session_base_t *session_, const options_t &options_); + ~plain_client_t (); + + // mechanism implementation + int next_handshake_command (msg_t *msg_); + int process_handshake_command (msg_t *msg_); + status_t status () const; + + private: + enum state_t + { + sending_hello, + waiting_for_welcome, + sending_initiate, + waiting_for_ready, + error_command_received, + ready + }; + + state_t _state; + + void produce_hello (msg_t *msg_) const; + void produce_initiate (msg_t *msg_) const; + + int process_welcome (const unsigned char *cmd_data_, size_t data_size_); + int process_ready (const unsigned char *cmd_data_, size_t data_size_); + int process_error (const unsigned char *cmd_data_, size_t data_size_); +}; +} + +#endif diff --git a/3rd/libzmq/src/plain_common.hpp b/3rd/libzmq/src/plain_common.hpp new file mode 100644 index 00000000..c97e184e --- /dev/null +++ b/3rd/libzmq/src/plain_common.hpp @@ -0,0 +1,53 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PLAIN_COMMON_HPP_INCLUDED__ +#define __ZMQ_PLAIN_COMMON_HPP_INCLUDED__ + +namespace zmq +{ +const char hello_prefix[] = "\x05HELLO"; +const size_t hello_prefix_len = sizeof (hello_prefix) - 1; + +const char welcome_prefix[] = "\x07WELCOME"; +const size_t welcome_prefix_len = sizeof (welcome_prefix) - 1; + +const char initiate_prefix[] = "\x08INITIATE"; +const size_t initiate_prefix_len = sizeof (initiate_prefix) - 1; + +const char ready_prefix[] = "\x05READY"; +const size_t ready_prefix_len = sizeof (ready_prefix) - 1; + +const char error_prefix[] = "\x05ERROR"; +const size_t error_prefix_len = sizeof (error_prefix) - 1; + +const size_t brief_len_size = sizeof (char); +} + +#endif diff --git a/3rd/libzmq/src/plain_server.cpp b/3rd/libzmq/src/plain_server.cpp new file mode 100644 index 00000000..694c925e --- /dev/null +++ b/3rd/libzmq/src/plain_server.cpp @@ -0,0 +1,253 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include + +#include "msg.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "plain_server.hpp" +#include "wire.hpp" +#include "plain_common.hpp" + +zmq::plain_server_t::plain_server_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_) : + mechanism_base_t (session_, options_), + zap_client_common_handshake_t ( + session_, peer_address_, options_, sending_welcome) +{ + // Note that there is no point to PLAIN if ZAP is not set up to handle the + // username and password, so if ZAP is not configured it is considered a + // failure. + // Given this is a backward-incompatible change, it's behind a socket + // option disabled by default. + if (options.zap_enforce_domain) + zmq_assert (zap_required ()); +} + +zmq::plain_server_t::~plain_server_t () +{ +} + +int zmq::plain_server_t::next_handshake_command (msg_t *msg_) +{ + int rc = 0; + + switch (state) { + case sending_welcome: + produce_welcome (msg_); + state = waiting_for_initiate; + break; + case sending_ready: + produce_ready (msg_); + state = ready; + break; + case sending_error: + produce_error (msg_); + state = error_sent; + break; + default: + errno = EAGAIN; + rc = -1; + } + return rc; +} + +int zmq::plain_server_t::process_handshake_command (msg_t *msg_) +{ + int rc = 0; + + switch (state) { + case waiting_for_hello: + rc = process_hello (msg_); + break; + case waiting_for_initiate: + rc = process_initiate (msg_); + break; + default: + // TODO see comment in curve_server_t::process_handshake_command + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED); + errno = EPROTO; + rc = -1; + break; + } + if (rc == 0) { + rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + } + return rc; +} + +int zmq::plain_server_t::process_hello (msg_t *msg_) +{ + int rc = check_basic_command_structure (msg_); + if (rc == -1) + return -1; + + const char *ptr = static_cast (msg_->data ()); + size_t bytes_left = msg_->size (); + + if (bytes_left < hello_prefix_len + || memcmp (ptr, hello_prefix, hello_prefix_len) != 0) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + ptr += hello_prefix_len; + bytes_left -= hello_prefix_len; + + if (bytes_left < 1) { + // PLAIN I: invalid PLAIN client, did not send username + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); + errno = EPROTO; + return -1; + } + const uint8_t username_length = *ptr++; + bytes_left -= sizeof (username_length); + + if (bytes_left < username_length) { + // PLAIN I: invalid PLAIN client, sent malformed username + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); + errno = EPROTO; + return -1; + } + const std::string username = std::string (ptr, username_length); + ptr += username_length; + bytes_left -= username_length; + if (bytes_left < 1) { + // PLAIN I: invalid PLAIN client, did not send password + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); + errno = EPROTO; + return -1; + } + + const uint8_t password_length = *ptr++; + bytes_left -= sizeof (password_length); + if (bytes_left != password_length) { + // PLAIN I: invalid PLAIN client, sent malformed password or + // extraneous data + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); + errno = EPROTO; + return -1; + } + + const std::string password = std::string (ptr, password_length); + + // Use ZAP protocol (RFC 27) to authenticate the user. + rc = session->zap_connect (); + if (rc != 0) { + session->get_socket ()->event_handshake_failed_no_detail ( + session->get_endpoint (), EFAULT); + return -1; + } + + send_zap_request (username, password); + state = waiting_for_zap_reply; + + // TODO actually, it is quite unlikely that we can read the ZAP + // reply already, but removing this has some strange side-effect + // (probably because the pipe's in_active flag is true until a read + // is attempted) + return receive_and_process_zap_reply () == -1 ? -1 : 0; +} + +void zmq::plain_server_t::produce_welcome (msg_t *msg_) +{ + const int rc = msg_->init_size (welcome_prefix_len); + errno_assert (rc == 0); + memcpy (msg_->data (), welcome_prefix, welcome_prefix_len); +} + +int zmq::plain_server_t::process_initiate (msg_t *msg_) +{ + const unsigned char *ptr = static_cast (msg_->data ()); + const size_t bytes_left = msg_->size (); + + if (bytes_left < initiate_prefix_len + || memcmp (ptr, initiate_prefix, initiate_prefix_len) != 0) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); + errno = EPROTO; + return -1; + } + const int rc = parse_metadata (ptr + initiate_prefix_len, + bytes_left - initiate_prefix_len); + if (rc == 0) + state = sending_ready; + return rc; +} + +void zmq::plain_server_t::produce_ready (msg_t *msg_) const +{ + make_command_with_basic_properties (msg_, ready_prefix, ready_prefix_len); +} + +void zmq::plain_server_t::produce_error (msg_t *msg_) const +{ + const char expected_status_code_len = 3; + zmq_assert (status_code.length () + == static_cast (expected_status_code_len)); + const size_t status_code_len_size = sizeof (expected_status_code_len); + const int rc = msg_->init_size (error_prefix_len + status_code_len_size + + expected_status_code_len); + zmq_assert (rc == 0); + char *msg_data = static_cast (msg_->data ()); + memcpy (msg_data, error_prefix, error_prefix_len); + msg_data[error_prefix_len] = expected_status_code_len; + memcpy (msg_data + error_prefix_len + status_code_len_size, + status_code.c_str (), status_code.length ()); +} + +void zmq::plain_server_t::send_zap_request (const std::string &username_, + const std::string &password_) +{ + const uint8_t *credentials[] = { + reinterpret_cast (username_.c_str ()), + reinterpret_cast (password_.c_str ())}; + size_t credentials_sizes[] = {username_.size (), password_.size ()}; + const char plain_mechanism_name[] = "PLAIN"; + zap_client_t::send_zap_request ( + plain_mechanism_name, sizeof (plain_mechanism_name) - 1, credentials, + credentials_sizes, sizeof (credentials) / sizeof (credentials[0])); +} diff --git a/3rd/libzmq/src/plain_server.hpp b/3rd/libzmq/src/plain_server.hpp new file mode 100644 index 00000000..3e6e2d3f --- /dev/null +++ b/3rd/libzmq/src/plain_server.hpp @@ -0,0 +1,66 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PLAIN_SERVER_HPP_INCLUDED__ +#define __ZMQ_PLAIN_SERVER_HPP_INCLUDED__ + +#include "options.hpp" +#include "zap_client.hpp" + +namespace zmq +{ +class msg_t; +class session_base_t; + +class plain_server_t ZMQ_FINAL : public zap_client_common_handshake_t +{ + public: + plain_server_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_); + ~plain_server_t (); + + // mechanism implementation + int next_handshake_command (msg_t *msg_); + int process_handshake_command (msg_t *msg_); + + private: + static void produce_welcome (msg_t *msg_); + void produce_ready (msg_t *msg_) const; + void produce_error (msg_t *msg_) const; + + int process_hello (msg_t *msg_); + int process_initiate (msg_t *msg_); + + void send_zap_request (const std::string &username_, + const std::string &password_); +}; +} + +#endif diff --git a/3rd/libzmq/src/poll.cpp b/3rd/libzmq/src/poll.cpp new file mode 100644 index 00000000..67de088d --- /dev/null +++ b/3rd/libzmq/src/poll.cpp @@ -0,0 +1,204 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "poll.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_POLL + +#include +#include +#include +#include + +#include "poll.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zmq::poll_t::poll_t (const zmq::thread_ctx_t &ctx_) : + worker_poller_base_t (ctx_), + retired (false) +{ +} + +zmq::poll_t::~poll_t () +{ + stop_worker (); +} + +zmq::poll_t::handle_t zmq::poll_t::add_fd (fd_t fd_, i_poll_events *events_) +{ + check_thread (); + zmq_assert (fd_ != retired_fd); + + // If the file descriptor table is too small expand it. + fd_table_t::size_type sz = fd_table.size (); + if (sz <= (fd_table_t::size_type) fd_) { + fd_table.resize (fd_ + 1); + while (sz != (fd_table_t::size_type) (fd_ + 1)) { + fd_table[sz].index = retired_fd; + ++sz; + } + } + + pollfd pfd = {fd_, 0, 0}; + pollset.push_back (pfd); + zmq_assert (fd_table[fd_].index == retired_fd); + + fd_table[fd_].index = pollset.size () - 1; + fd_table[fd_].events = events_; + + // Increase the load metric of the thread. + adjust_load (1); + + return fd_; +} + +void zmq::poll_t::rm_fd (handle_t handle_) +{ + check_thread (); + fd_t index = fd_table[handle_].index; + zmq_assert (index != retired_fd); + + // Mark the fd as unused. + pollset[index].fd = retired_fd; + fd_table[handle_].index = retired_fd; + retired = true; + + // Decrease the load metric of the thread. + adjust_load (-1); +} + +void zmq::poll_t::set_pollin (handle_t handle_) +{ + check_thread (); + fd_t index = fd_table[handle_].index; + pollset[index].events |= POLLIN; +} + +void zmq::poll_t::reset_pollin (handle_t handle_) +{ + check_thread (); + fd_t index = fd_table[handle_].index; + pollset[index].events &= ~((short) POLLIN); +} + +void zmq::poll_t::set_pollout (handle_t handle_) +{ + check_thread (); + fd_t index = fd_table[handle_].index; + pollset[index].events |= POLLOUT; +} + +void zmq::poll_t::reset_pollout (handle_t handle_) +{ + check_thread (); + fd_t index = fd_table[handle_].index; + pollset[index].events &= ~((short) POLLOUT); +} + +void zmq::poll_t::stop () +{ + check_thread (); + // no-op... thread is stopped when no more fds or timers are registered +} + +int zmq::poll_t::max_fds () +{ + return -1; +} + +void zmq::poll_t::loop () +{ + while (true) { + // Execute any due timers. + int timeout = (int) execute_timers (); + + cleanup_retired (); + + if (pollset.empty ()) { + zmq_assert (get_load () == 0); + + if (timeout == 0) + break; + + // TODO sleep for timeout + continue; + } + + // Wait for events. + int rc = poll (&pollset[0], static_cast (pollset.size ()), + timeout ? timeout : -1); + if (rc == -1) { + errno_assert (errno == EINTR); + continue; + } + + // If there are no events (i.e. it's a timeout) there's no point + // in checking the pollset. + if (rc == 0) + continue; + + for (pollset_t::size_type i = 0; i != pollset.size (); i++) { + zmq_assert (!(pollset[i].revents & POLLNVAL)); + if (pollset[i].fd == retired_fd) + continue; + if (pollset[i].revents & (POLLERR | POLLHUP)) + fd_table[pollset[i].fd].events->in_event (); + if (pollset[i].fd == retired_fd) + continue; + if (pollset[i].revents & POLLOUT) + fd_table[pollset[i].fd].events->out_event (); + if (pollset[i].fd == retired_fd) + continue; + if (pollset[i].revents & POLLIN) + fd_table[pollset[i].fd].events->in_event (); + } + } +} + +void zmq::poll_t::cleanup_retired () +{ + // Clean up the pollset and update the fd_table accordingly. + if (retired) { + pollset_t::size_type i = 0; + while (i < pollset.size ()) { + if (pollset[i].fd == retired_fd) + pollset.erase (pollset.begin () + i); + else { + fd_table[pollset[i].fd].index = i; + i++; + } + } + retired = false; + } +} + + +#endif diff --git a/3rd/libzmq/src/poll.hpp b/3rd/libzmq/src/poll.hpp new file mode 100644 index 00000000..4c274425 --- /dev/null +++ b/3rd/libzmq/src/poll.hpp @@ -0,0 +1,110 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_POLL_HPP_INCLUDED__ +#define __ZMQ_POLL_HPP_INCLUDED__ + +// poller.hpp decides which polling mechanism to use. +#include "poller.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_POLL + +#if defined ZMQ_HAVE_WINDOWS +#error \ + "poll is broken on Windows for the purpose of the I/O thread poller, use select instead; "\ + "see https://github.com/zeromq/libzmq/issues/3107" +#endif + +#include +#include +#include + +#include "ctx.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "poller_base.hpp" + +namespace zmq +{ +struct i_poll_events; + +// Implements socket polling mechanism using the POSIX.1-2001 +// poll() system call. + +class poll_t ZMQ_FINAL : public worker_poller_base_t +{ + public: + typedef fd_t handle_t; + + poll_t (const thread_ctx_t &ctx_); + ~poll_t (); + + // "poller" concept. + // These methods may only be called from an event callback; add_fd may also be called before start. + handle_t add_fd (fd_t fd_, zmq::i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void stop (); + + static int max_fds (); + + private: + // Main event loop. + void loop () ZMQ_FINAL; + + void cleanup_retired (); + + struct fd_entry_t + { + fd_t index; + zmq::i_poll_events *events; + }; + + // This table stores data for registered descriptors. + typedef std::vector fd_table_t; + fd_table_t fd_table; + + // Pollset to pass to the poll function. + typedef std::vector pollset_t; + pollset_t pollset; + + // If true, there's at least one retired event source. + bool retired; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (poll_t) +}; + +typedef poll_t poller_t; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/poller.hpp b/3rd/libzmq/src/poller.hpp new file mode 100644 index 00000000..ba9fbb17 --- /dev/null +++ b/3rd/libzmq/src/poller.hpp @@ -0,0 +1,68 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_POLLER_HPP_INCLUDED__ +#define __ZMQ_POLLER_HPP_INCLUDED__ + +#if defined ZMQ_IOTHREAD_POLLER_USE_KQUEUE \ + + defined ZMQ_IOTHREAD_POLLER_USE_EPOLL \ + + defined ZMQ_IOTHREAD_POLLER_USE_DEVPOLL \ + + defined ZMQ_IOTHREAD_POLLER_USE_POLLSET \ + + defined ZMQ_IOTHREAD_POLLER_POLL \ + + defined ZMQ_IOTHREAD_POLLER_USE_SELECT \ + > 1 +#error More than one of the ZMQ_IOTHREAD_POLLER_USE_* macros defined +#endif + +#if defined ZMQ_IOTHREAD_POLLER_USE_KQUEUE +#include "kqueue.hpp" +#elif defined ZMQ_IOTHREAD_POLLER_USE_EPOLL +#include "epoll.hpp" +#elif defined ZMQ_IOTHREAD_POLLER_USE_DEVPOLL +#include "devpoll.hpp" +#elif defined ZMQ_IOTHREAD_POLLER_USE_POLLSET +#include "pollset.hpp" +#elif defined ZMQ_IOTHREAD_POLLER_USE_POLL +#include "poll.hpp" +#elif defined ZMQ_IOTHREAD_POLLER_USE_SELECT +#include "select.hpp" +#elif defined ZMQ_HAVE_GNU +#define ZMQ_IOTHREAD_POLLER_USE_POLL +#include "poll.hpp" +#else +#error None of the ZMQ_IOTHREAD_POLLER_USE_* macros defined +#endif + +#if (defined ZMQ_POLL_BASED_ON_SELECT + defined ZMQ_POLL_BASED_ON_POLL) > 1 +#error More than one of the ZMQ_POLL_BASED_ON_* macros defined +#elif (defined ZMQ_POLL_BASED_ON_SELECT + defined ZMQ_POLL_BASED_ON_POLL) == 0 +#error None of the ZMQ_POLL_BASED_ON_* macros defined +#endif + +#endif diff --git a/3rd/libzmq/src/poller_base.cpp b/3rd/libzmq/src/poller_base.cpp new file mode 100644 index 00000000..78db28f6 --- /dev/null +++ b/3rd/libzmq/src/poller_base.cpp @@ -0,0 +1,147 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "poller_base.hpp" +#include "i_poll_events.hpp" +#include "err.hpp" + +zmq::poller_base_t::~poller_base_t () +{ + // Make sure there is no more load on the shutdown. + zmq_assert (get_load () == 0); +} + +int zmq::poller_base_t::get_load () const +{ + return _load.get (); +} + +void zmq::poller_base_t::adjust_load (int amount_) +{ + if (amount_ > 0) + _load.add (amount_); + else if (amount_ < 0) + _load.sub (-amount_); +} + +void zmq::poller_base_t::add_timer (int timeout_, i_poll_events *sink_, int id_) +{ + uint64_t expiration = _clock.now_ms () + timeout_; + timer_info_t info = {sink_, id_}; + _timers.insert (timers_t::value_type (expiration, info)); +} + +void zmq::poller_base_t::cancel_timer (i_poll_events *sink_, int id_) +{ + // Complexity of this operation is O(n). We assume it is rarely used. + for (timers_t::iterator it = _timers.begin (), end = _timers.end (); + it != end; ++it) + if (it->second.sink == sink_ && it->second.id == id_) { + _timers.erase (it); + return; + } + + // We should generally never get here. Calling 'cancel_timer ()' on + // an already expired or canceled timer (or even worse - on a timer which + // never existed, supplying bad sink_ and/or id_ values) does not make any + // sense. + // But in some edge cases this might happen. As described in issue #3645 + // `timer_event ()` call from `execute_timers ()` might call `cancel_timer ()` + // on already canceled (deleted) timer. + // As soon as that is resolved an 'assert (false)' should be put here. +} + +uint64_t zmq::poller_base_t::execute_timers () +{ + // Fast track. + if (_timers.empty ()) + return 0; + + // Get the current time. + const uint64_t current = _clock.now_ms (); + + // Execute the timers that are already due. + uint64_t res = 0; + timer_info_t timer_temp; + timers_t::iterator it; + + do { + it = _timers.begin (); + + // If we have to wait to execute the item, same will be true for + // all the following items because multimap is sorted. Thus we can + // stop checking the subsequent timers. + if (it->first > current) { + res = it->first - current; + break; + } + + // Save and remove the timer because timer_event() call might delete + // exactly this timer and then the iterator will be invalid. + timer_temp = it->second; + _timers.erase (it); + + // Trigger the timer. + timer_temp.sink->timer_event (timer_temp.id); + + } while (!_timers.empty ()); + + // Return the time to wait for the next timer (at least 1ms), or 0, if + // there are no more timers. + return res; +} + +zmq::worker_poller_base_t::worker_poller_base_t (const thread_ctx_t &ctx_) : + _ctx (ctx_) +{ +} + +void zmq::worker_poller_base_t::stop_worker () +{ + _worker.stop (); +} + +void zmq::worker_poller_base_t::start (const char *name_) +{ + zmq_assert (get_load () > 0); + _ctx.start_thread (_worker, worker_routine, this, name_); +} + +void zmq::worker_poller_base_t::check_thread () const +{ +#ifndef NDEBUG + zmq_assert (!_worker.get_started () || _worker.is_current_thread ()); +#endif +} + +void zmq::worker_poller_base_t::worker_routine (void *arg_) +{ + (static_cast (arg_))->loop (); +} diff --git a/3rd/libzmq/src/poller_base.hpp b/3rd/libzmq/src/poller_base.hpp new file mode 100644 index 00000000..b39f2a41 --- /dev/null +++ b/3rd/libzmq/src/poller_base.hpp @@ -0,0 +1,195 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_POLLER_BASE_HPP_INCLUDED__ +#define __ZMQ_POLLER_BASE_HPP_INCLUDED__ + +#include + +#include "clock.hpp" +#include "atomic_counter.hpp" +#include "ctx.hpp" + +namespace zmq +{ +struct i_poll_events; + +// A build of libzmq must provide an implementation of the poller_t concept. By +// convention, this is done via a typedef. +// +// At the time of writing, the following implementations of the poller_t +// concept exist: zmq::devpoll_t, zmq::epoll_t, zmq::kqueue_t, zmq::poll_t, +// zmq::pollset_t, zmq::select_t +// +// An implementation of the poller_t concept must provide the following public +// methods: +// Returns load of the poller. +// int get_load() const; +// +// Add a timeout to expire in timeout_ milliseconds. After the +// expiration, timer_event on sink_ object will be called with +// argument set to id_. +// void add_timer(int timeout_, zmq::i_poll_events *sink_, int id_); +// +// Cancel the timer created by sink_ object with ID equal to id_. +// void cancel_timer(zmq::i_poll_events *sink_, int id_); +// +// Adds a fd to the poller. Initially, no events are activated. These must +// be activated by the set_* methods using the returned handle_. +// handle_t add_fd(fd_t fd_, zmq::i_poll_events *events_); +// +// Deactivates any events that may be active for the given handle_, and +// removes the fd associated with the given handle_. +// void rm_fd(handle_t handle_); +// +// The set_* and reset_* methods activate resp. deactivate polling for +// input/output readiness on the respective handle_, such that the +// in_event/out_event methods on the associated zmq::i_poll_events object +// will be called. +// Note: while handle_t and fd_t may be the same type, and may even have the +// same values for some implementation, this may not be assumed in general. +// The methods may only be called with the handle returned by add_fd. +// void set_pollin(handle_t handle_); +// void reset_pollin(handle_t handle_); +// void set_pollout(handle_t handle_);// +// void reset_pollout(handle_t handle_); +// +// Starts operation of the poller. See below for details. +// void start(); +// +// Request termination of the poller. +// TODO: might be removed in the future, as it has no effect. +// void stop(); +// +// Returns the maximum number of fds that can be added to an instance of the +// poller at the same time, or -1 if there is no such fixed limit. +// static int max_fds(); +// +// Most of the methods may only be called from a zmq::i_poll_events callback +// function when invoked by the poller (and, therefore, typically from the +// poller's worker thread), with the following exceptions: +// - get_load may be called from outside +// - add_fd and add_timer may be called from outside before start +// - start may be called from outside once +// +// After a poller is started, it waits for the registered events (input/output +// readiness, timeout) to happen, and calls the respective functions on the +// zmq::i_poll_events object. It terminates when no further registrations (fds +// or timers) exist. +// +// Before start, add_fd must have been called at least once. Behavior may be +// undefined otherwise. +// +// If the poller is implemented by a single worker thread (the +// worker_poller_base_t base class may be used to implement such a poller), +// no synchronization is required for the data structures modified by +// add_fd, rm_fd, add_timer, cancel_timer, (re)set_poll(in|out). However, +// reentrancy must be considered, e.g. when one of the functions modifies +// a container that is being iterated by the poller. + + +// A class that can be used as abase class for implementations of the poller +// concept. +// +// For documentation of the public methods, see the description of the poller_t +// concept. +class poller_base_t +{ + public: + poller_base_t () ZMQ_DEFAULT; + virtual ~poller_base_t (); + + // Methods from the poller concept. + int get_load () const; + void add_timer (int timeout_, zmq::i_poll_events *sink_, int id_); + void cancel_timer (zmq::i_poll_events *sink_, int id_); + + protected: + // Called by individual poller implementations to manage the load. + void adjust_load (int amount_); + + // Executes any timers that are due. Returns number of milliseconds + // to wait to match the next timer or 0 meaning "no timers". + uint64_t execute_timers (); + + private: + // Clock instance private to this I/O thread. + clock_t _clock; + + // List of active timers. + struct timer_info_t + { + zmq::i_poll_events *sink; + int id; + }; + typedef std::multimap timers_t; + timers_t _timers; + + // Load of the poller. Currently the number of file descriptors + // registered. + atomic_counter_t _load; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (poller_base_t) +}; + +// Base class for a poller with a single worker thread. +class worker_poller_base_t : public poller_base_t +{ + public: + worker_poller_base_t (const thread_ctx_t &ctx_); + + // Methods from the poller concept. + void start (const char *name = NULL); + + protected: + // Checks whether the currently executing thread is the worker thread + // via an assertion. + // Should be called by the add_fd, removed_fd, set_*, reset_* functions + // to ensure correct usage. + void check_thread () const; + + // Stops the worker thread. Should be called from the destructor of the + // leaf class. + void stop_worker (); + + private: + // Main worker thread routine. + static void worker_routine (void *arg_); + + virtual void loop () = 0; + + // Reference to ZMQ context. + const thread_ctx_t &_ctx; + + // Handle of the physical thread doing the I/O work. + thread_t _worker; +}; +} + +#endif diff --git a/3rd/libzmq/src/polling_util.cpp b/3rd/libzmq/src/polling_util.cpp new file mode 100644 index 00000000..c8e42eb8 --- /dev/null +++ b/3rd/libzmq/src/polling_util.cpp @@ -0,0 +1,51 @@ +/* + Copyright (c) 2007-2018 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 . +*/ + +#include "precompiled.hpp" +#include "polling_util.hpp" + +#if defined ZMQ_POLL_BASED_ON_POLL +#include +#include + +zmq::timeout_t zmq::compute_timeout (const bool first_pass_, + const long timeout_, + const uint64_t now_, + const uint64_t end_) +{ + if (first_pass_) + return 0; + + if (timeout_ < 0) + return -1; + + return static_cast ( + std::min (end_ - now_, INT_MAX)); +} +#endif diff --git a/3rd/libzmq/src/polling_util.hpp b/3rd/libzmq/src/polling_util.hpp new file mode 100644 index 00000000..2af8c456 --- /dev/null +++ b/3rd/libzmq/src/polling_util.hpp @@ -0,0 +1,178 @@ +/* + Copyright (c) 2007-2018 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 . +*/ + +#ifndef __ZMQ_SOCKET_POLLING_UTIL_HPP_INCLUDED__ +#define __ZMQ_SOCKET_POLLING_UTIL_HPP_INCLUDED__ + +#include +#include + +#include "macros.hpp" +#include "stdint.hpp" +#include "platform.hpp" +#include "err.hpp" + +namespace zmq +{ +template class fast_vector_t +{ + public: + explicit fast_vector_t (const size_t nitems_) + { + if (nitems_ > S) { + _buf = static_cast (malloc (nitems_ * sizeof (T))); + // TODO since this function is called by a client, we could return errno == ENOMEM here + alloc_assert (_buf); + } else { + _buf = _static_buf; + } + } + + T &operator[] (const size_t i) { return _buf[i]; } + + ~fast_vector_t () + { + if (_buf != _static_buf) + free (_buf); + } + + private: + T _static_buf[S]; + T *_buf; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (fast_vector_t) +}; + +template class resizable_fast_vector_t +{ + public: + resizable_fast_vector_t () : _dynamic_buf (NULL) {} + + void resize (const size_t nitems_) + { + if (_dynamic_buf) + _dynamic_buf->resize (nitems_); + if (nitems_ > S) { + _dynamic_buf = new (std::nothrow) std::vector; + // TODO since this function is called by a client, we could return errno == ENOMEM here + alloc_assert (_dynamic_buf); + } + } + + T *get_buf () + { + // e.g. MSVC 2008 does not have std::vector::data, so we use &...[0] + return _dynamic_buf ? &(*_dynamic_buf)[0] : _static_buf; + } + + T &operator[] (const size_t i) { return get_buf ()[i]; } + + ~resizable_fast_vector_t () { delete _dynamic_buf; } + + private: + T _static_buf[S]; + std::vector *_dynamic_buf; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (resizable_fast_vector_t) +}; + +#if defined ZMQ_POLL_BASED_ON_POLL +typedef int timeout_t; + +timeout_t +compute_timeout (bool first_pass_, long timeout_, uint64_t now_, uint64_t end_); + +#elif defined ZMQ_POLL_BASED_ON_SELECT +inline size_t valid_pollset_bytes (const fd_set &pollset_) +{ +#if defined ZMQ_HAVE_WINDOWS + // On Windows we don't need to copy the whole fd_set. + // SOCKETS are continuous from the beginning of fd_array in fd_set. + // We just need to copy fd_count elements of fd_array. + // We gain huge memcpy() improvement if number of used SOCKETs is much lower than FD_SETSIZE. + return reinterpret_cast ( + &pollset_.fd_array[pollset_.fd_count]) + - reinterpret_cast (&pollset_); +#else + return sizeof (fd_set); +#endif +} + +#if defined ZMQ_HAVE_WINDOWS +// struct fd_set { +// u_int fd_count; +// SOCKET fd_array[1]; +// }; +// NOTE: offsetof(fd_set, fd_array)==sizeof(SOCKET) on both x86 and x64 +// due to alignment bytes for the latter. +class optimized_fd_set_t +{ + public: + explicit optimized_fd_set_t (size_t nevents_) : _fd_set (1 + nevents_) {} + + fd_set *get () { return reinterpret_cast (&_fd_set[0]); } + + private: + fast_vector_t _fd_set; +}; + +class resizable_optimized_fd_set_t +{ + public: + void resize (size_t nevents_) { _fd_set.resize (1 + nevents_); } + + fd_set *get () { return reinterpret_cast (&_fd_set[0]); } + + private: + resizable_fast_vector_t _fd_set; +}; +#else +class optimized_fd_set_t +{ + public: + explicit optimized_fd_set_t (size_t /*nevents_*/) {} + + fd_set *get () { return &_fd_set; } + + private: + fd_set _fd_set; +}; + +class resizable_optimized_fd_set_t : public optimized_fd_set_t +{ + public: + resizable_optimized_fd_set_t () : optimized_fd_set_t (0) {} + + void resize (size_t /*nevents_*/) {} +}; +#endif +#endif +} + +#endif diff --git a/3rd/libzmq/src/pollset.cpp b/3rd/libzmq/src/pollset.cpp new file mode 100644 index 00000000..1a1dee95 --- /dev/null +++ b/3rd/libzmq/src/pollset.cpp @@ -0,0 +1,254 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "pollset.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_POLLSET + +#include +#include +#include +#include +#include + +#include "macros.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zmq::pollset_t::pollset_t (const zmq::thread_ctx_t &ctx_) : + ctx (ctx_), + stopping (false) +{ + pollset_fd = pollset_create (-1); + errno_assert (pollset_fd != -1); +} + +zmq::pollset_t::~pollset_t () +{ + // Wait till the worker thread exits. + worker.stop (); + + pollset_destroy (pollset_fd); + for (retired_t::iterator it = retired.begin (); it != retired.end (); ++it) + LIBZMQ_DELETE (*it); +} + +zmq::pollset_t::handle_t zmq::pollset_t::add_fd (fd_t fd_, + i_poll_events *events_) +{ + poll_entry_t *pe = new (std::nothrow) poll_entry_t; + alloc_assert (pe); + + pe->fd = fd_; + pe->flag_pollin = false; + pe->flag_pollout = false; + pe->events = events_; + + struct poll_ctl pc; + pc.fd = fd_; + pc.cmd = PS_ADD; + pc.events = 0; + + int rc = pollset_ctl (pollset_fd, &pc, 1); + errno_assert (rc != -1); + + // Increase the load metric of the thread. + adjust_load (1); + + if (fd_ >= fd_table.size ()) { + fd_table.resize (fd_ + 1, NULL); + } + fd_table[fd_] = pe; + return pe; +} + +void zmq::pollset_t::rm_fd (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t *) handle_; + + struct poll_ctl pc; + pc.fd = pe->fd; + pc.cmd = PS_DELETE; + pc.events = 0; + pollset_ctl (pollset_fd, &pc, 1); + + fd_table[pe->fd] = NULL; + + pe->fd = retired_fd; + retired.push_back (pe); + + // Decrease the load metric of the thread. + adjust_load (-1); +} + +void zmq::pollset_t::set_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t *) handle_; + if (likely (!pe->flag_pollin)) { + struct poll_ctl pc; + pc.fd = pe->fd; + pc.cmd = PS_MOD; + pc.events = POLLIN; + + const int rc = pollset_ctl (pollset_fd, &pc, 1); + errno_assert (rc != -1); + + pe->flag_pollin = true; + } +} + +void zmq::pollset_t::reset_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t *) handle_; + if (unlikely (!pe->flag_pollin)) { + return; + } + + struct poll_ctl pc; + pc.fd = pe->fd; + pc.events = 0; + + pc.cmd = PS_DELETE; + int rc = pollset_ctl (pollset_fd, &pc, 1); + + if (pe->flag_pollout) { + pc.events = POLLOUT; + pc.cmd = PS_MOD; + rc = pollset_ctl (pollset_fd, &pc, 1); + errno_assert (rc != -1); + } + + pe->flag_pollin = false; +} + +void zmq::pollset_t::set_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t *) handle_; + if (likely (!pe->flag_pollout)) { + struct poll_ctl pc; + pc.fd = pe->fd; + pc.cmd = PS_MOD; + pc.events = POLLOUT; + + const int rc = pollset_ctl (pollset_fd, &pc, 1); + errno_assert (rc != -1); + + pe->flag_pollout = true; + } +} + +void zmq::pollset_t::reset_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t *) handle_; + if (unlikely (!pe->flag_pollout)) { + return; + } + + struct poll_ctl pc; + pc.fd = pe->fd; + pc.events = 0; + + pc.cmd = PS_DELETE; + int rc = pollset_ctl (pollset_fd, &pc, 1); + errno_assert (rc != -1); + + if (pe->flag_pollin) { + pc.cmd = PS_MOD; + pc.events = POLLIN; + rc = pollset_ctl (pollset_fd, &pc, 1); + errno_assert (rc != -1); + } + pe->flag_pollout = false; +} + +void zmq::pollset_t::start () +{ + ctx.start_thread (worker, worker_routine, this); +} + +void zmq::pollset_t::stop () +{ + stopping = true; +} + +int zmq::pollset_t::max_fds () +{ + return -1; +} + +void zmq::pollset_t::loop () +{ + struct pollfd polldata_array[max_io_events]; + + while (!stopping) { + // Execute any due timers. + int timeout = (int) execute_timers (); + + // Wait for events. + int n = pollset_poll (pollset_fd, polldata_array, max_io_events, + timeout ? timeout : -1); + if (n == -1) { + errno_assert (errno == EINTR); + continue; + } + + for (int i = 0; i < n; i++) { + poll_entry_t *pe = fd_table[polldata_array[i].fd]; + if (!pe) + continue; + + if (pe->fd == retired_fd) + continue; + if (polldata_array[i].revents & (POLLERR | POLLHUP)) + pe->events->in_event (); + if (pe->fd == retired_fd) + continue; + if (polldata_array[i].revents & POLLOUT) + pe->events->out_event (); + if (pe->fd == retired_fd) + continue; + if (polldata_array[i].revents & POLLIN) + pe->events->in_event (); + } + + // Destroy retired event sources. + for (retired_t::iterator it = retired.begin (); it != retired.end (); + ++it) + LIBZMQ_DELETE (*it); + retired.clear (); + } +} + +void zmq::pollset_t::worker_routine (void *arg_) +{ + ((pollset_t *) arg_)->loop (); +} + +#endif diff --git a/3rd/libzmq/src/pollset.hpp b/3rd/libzmq/src/pollset.hpp new file mode 100644 index 00000000..8286bed2 --- /dev/null +++ b/3rd/libzmq/src/pollset.hpp @@ -0,0 +1,116 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_POLLSET_HPP_INCLUDED__ +#define __ZMQ_POLLSET_HPP_INCLUDED__ + +// poller.hpp decides which polling mechanism to use. +#include "poller.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_POLLSET + +#include +#include +#include + +#include "ctx.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "poller_base.hpp" + +namespace zmq +{ +struct i_poll_events; + +// This class implements socket polling mechanism using the AIX-specific +// pollset mechanism. + +class pollset_t ZMQ_FINAL : public poller_base_t +{ + public: + typedef void *handle_t; + + pollset_t (const thread_ctx_t &ctx_); + ~pollset_t () ZMQ_FINAL; + + // "poller" concept. + handle_t add_fd (fd_t fd_, zmq::i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void start (); + void stop (); + + static int max_fds (); + + private: + // Main worker thread routine. + static void worker_routine (void *arg_); + + // Main event loop. + void loop () ZMQ_FINAL; + + // Reference to ZMQ context. + const thread_ctx_t &ctx; + + // Main pollset file descriptor + ::pollset_t pollset_fd; + + struct poll_entry_t + { + fd_t fd; + bool flag_pollin; + bool flag_pollout; + zmq::i_poll_events *events; + }; + + // List of retired event sources. + typedef std::vector retired_t; + retired_t retired; + + // This table stores data for registered descriptors. + typedef std::vector fd_table_t; + fd_table_t fd_table; + + // If true, thread is in the process of shutting down. + bool stopping; + + // Handle of the physical thread doing the I/O work. + thread_t worker; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (pollset_t) +}; + +typedef pollset_t poller_t; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/precompiled.cpp b/3rd/libzmq/src/precompiled.cpp new file mode 100644 index 00000000..acf58f76 --- /dev/null +++ b/3rd/libzmq/src/precompiled.cpp @@ -0,0 +1,30 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" diff --git a/3rd/libzmq/src/precompiled.hpp b/3rd/libzmq/src/precompiled.hpp new file mode 100644 index 00000000..f22a6937 --- /dev/null +++ b/3rd/libzmq/src/precompiled.hpp @@ -0,0 +1,134 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PRECOMPILED_HPP_INCLUDED__ +#define __ZMQ_PRECOMPILED_HPP_INCLUDED__ + +// On AIX platform, poll.h has to be included first to get consistent +// definition of pollfd structure (AIX uses 'reqevents' and 'retnevents' +// instead of 'events' and 'revents' and defines macros to map from POSIX-y +// names to AIX-specific names). +// zmq.h must be included *after* poll.h for AIX to build properly. +// precompiled.hpp includes include/zmq.h +#if defined ZMQ_POLL_BASED_ON_POLL && defined ZMQ_HAVE_AIX +#include +#endif + +#include "platform.hpp" + +#define __STDC_LIMIT_MACROS + +// This must be included before any windows headers are compiled. +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#endif + +#if defined ZMQ_HAVE_OPENBSD +#define ucred sockpeercred +#endif + +// 0MQ definitions and exported functions +#include "../include/zmq.h" + +// 0MQ DRAFT definitions and exported functions +#include "zmq_draft.h" + +// TODO: expand pch implementation to non-windows builds. +#ifdef _MSC_VER + +// standard C headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// standard C++ headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1800 +#include +#endif + +#if _MSC_VER >= 1700 +#include +#endif + +#if defined _WIN32_WCE +#include +#else +#include +#endif + +#if defined HAVE_LIBGSSAPI_KRB5 +#include "err.hpp" +#include "msg.hpp" +#include "mechanism.hpp" +#include "session_base.hpp" +#include "gssapi_server.hpp" +#include "wire.hpp" +#include +#include +#endif + +#include "options.hpp" + +#endif // _MSC_VER + +#endif //ifndef __ZMQ_PRECOMPILED_HPP_INCLUDED__ diff --git a/3rd/libzmq/src/proxy.cpp b/3rd/libzmq/src/proxy.cpp new file mode 100644 index 00000000..8a517c78 --- /dev/null +++ b/3rd/libzmq/src/proxy.cpp @@ -0,0 +1,646 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include +#include "poller.hpp" +#include "proxy.hpp" +#include "likely.hpp" +#include "msg.hpp" + +#if defined ZMQ_POLL_BASED_ON_POLL && !defined ZMQ_HAVE_WINDOWS \ + && !defined ZMQ_HAVE_AIX +#include +#endif + +// These headers end up pulling in zmq.h somewhere in their include +// dependency chain +#include "socket_base.hpp" +#include "err.hpp" + +#ifdef ZMQ_HAVE_POLLER + +#include "socket_poller.hpp" + +// Macros for repetitive code. + +// PROXY_CLEANUP() must not be used before these variables are initialized. +#define PROXY_CLEANUP() \ + do { \ + delete poller_all; \ + delete poller_in; \ + delete poller_control; \ + delete poller_receive_blocked; \ + delete poller_send_blocked; \ + delete poller_both_blocked; \ + delete poller_frontend_only; \ + delete poller_backend_only; \ + } while (false) + + +#define CHECK_RC_EXIT_ON_FAILURE() \ + do { \ + if (rc < 0) { \ + PROXY_CLEANUP (); \ + return close_and_return (&msg, -1); \ + } \ + } while (false) + +#endif // ZMQ_HAVE_POLLER + + +// Control socket messages + +typedef struct +{ + uint64_t msg_in; + uint64_t bytes_in; + uint64_t msg_out; + uint64_t bytes_out; +} zmq_socket_stats_t; + + +// Utility functions + +static int +capture (class zmq::socket_base_t *capture_, zmq::msg_t *msg_, int more_ = 0) +{ + // Copy message to capture socket if any + if (capture_) { + zmq::msg_t ctrl; + int rc = ctrl.init (); + if (unlikely (rc < 0)) + return -1; + rc = ctrl.copy (*msg_); + if (unlikely (rc < 0)) + return -1; + rc = capture_->send (&ctrl, more_ ? ZMQ_SNDMORE : 0); + if (unlikely (rc < 0)) + return -1; + } + return 0; +} + +static int forward (class zmq::socket_base_t *from_, + zmq_socket_stats_t *from_stats_, + class zmq::socket_base_t *to_, + zmq_socket_stats_t *to_stats_, + class zmq::socket_base_t *capture_, + zmq::msg_t *msg_) +{ + // Forward a burst of messages + for (unsigned int i = 0; i < zmq::proxy_burst_size; i++) { + int more; + size_t moresz; + size_t complete_msg_size = 0; + + // Forward all the parts of one message + while (true) { + int rc = from_->recv (msg_, ZMQ_DONTWAIT); + if (rc < 0) { + if (likely (errno == EAGAIN && i > 0)) + return 0; // End of burst + + return -1; + } + + complete_msg_size += msg_->size (); + + moresz = sizeof more; + rc = from_->getsockopt (ZMQ_RCVMORE, &more, &moresz); + if (unlikely (rc < 0)) + return -1; + + // Copy message to capture socket if any + rc = capture (capture_, msg_, more); + if (unlikely (rc < 0)) + return -1; + + rc = to_->send (msg_, more ? ZMQ_SNDMORE : 0); + if (unlikely (rc < 0)) + return -1; + + if (more == 0) + break; + } + + // A multipart message counts as 1 packet: + from_stats_->msg_in++; + from_stats_->bytes_in += complete_msg_size; + to_stats_->msg_out++; + to_stats_->bytes_out += complete_msg_size; + } + + return 0; +} + +static int loop_and_send_multipart_stat (zmq::socket_base_t *control_, + uint64_t stat_, + bool first_, + bool more_) +{ + int rc; + zmq::msg_t msg; + + // VSM of 8 bytes can't fail to init + msg.init_size (sizeof (uint64_t)); + memcpy (msg.data (), &stat_, sizeof (uint64_t)); + + // if the first message is handed to the pipe successfully then the HWM + // is not full, which means failures are due to interrupts (on Windows pipes + // are TCP sockets), so keep retrying + do { + rc = control_->send (&msg, more_ ? ZMQ_SNDMORE : 0); + } while (!first_ && rc != 0 && errno == EAGAIN); + + return rc; +} + +static int reply_stats (zmq::socket_base_t *control_, + const zmq_socket_stats_t *frontend_stats_, + const zmq_socket_stats_t *backend_stats_) +{ + // first part: frontend stats - the first send might fail due to HWM + if (loop_and_send_multipart_stat (control_, frontend_stats_->msg_in, true, + true) + != 0) + return -1; + + loop_and_send_multipart_stat (control_, frontend_stats_->bytes_in, false, + true); + loop_and_send_multipart_stat (control_, frontend_stats_->msg_out, false, + true); + loop_and_send_multipart_stat (control_, frontend_stats_->bytes_out, false, + true); + + // second part: backend stats + loop_and_send_multipart_stat (control_, backend_stats_->msg_in, false, + true); + loop_and_send_multipart_stat (control_, backend_stats_->bytes_in, false, + true); + loop_and_send_multipart_stat (control_, backend_stats_->msg_out, false, + true); + loop_and_send_multipart_stat (control_, backend_stats_->bytes_out, false, + false); + + return 0; +} + + +#ifdef ZMQ_HAVE_POLLER + +int zmq::proxy (class socket_base_t *frontend_, + class socket_base_t *backend_, + class socket_base_t *capture_, + class socket_base_t *control_) +{ + msg_t msg; + int rc = msg.init (); + if (rc != 0) + return -1; + + // The algorithm below assumes ratio of requests and replies processed + // under full load to be 1:1. + + int more; + size_t moresz = sizeof (more); + + // Proxy can be in these three states + enum + { + active, + paused, + terminated + } state = active; + + bool frontend_equal_to_backend; + bool frontend_in = false; + bool frontend_out = false; + bool backend_in = false; + bool backend_out = false; + bool control_in = false; + zmq::socket_poller_t::event_t events[3]; + zmq_socket_stats_t frontend_stats; + zmq_socket_stats_t backend_stats; + memset (&frontend_stats, 0, sizeof (frontend_stats)); + memset (&backend_stats, 0, sizeof (backend_stats)); + + // Don't allocate these pollers from stack because they will take more than 900 kB of stack! + // On Windows this blows up default stack of 1 MB and aborts the program. + // I wanted to use std::shared_ptr here as the best solution but that requires C++11... + zmq::socket_poller_t *poller_all = + new (std::nothrow) zmq::socket_poller_t; // Poll for everything. + zmq::socket_poller_t *poller_in = new (std::nothrow) zmq:: + socket_poller_t; // Poll only 'ZMQ_POLLIN' on all sockets. Initial blocking poll in loop. + zmq::socket_poller_t *poller_control = new (std::nothrow) zmq:: + socket_poller_t; // Poll only for 'ZMQ_POLLIN' on 'control_', when proxy is paused. + zmq::socket_poller_t *poller_receive_blocked = new (std::nothrow) + zmq::socket_poller_t; // All except 'ZMQ_POLLIN' on 'frontend_'. + + // If frontend_==backend_ 'poller_send_blocked' and 'poller_receive_blocked' are the same, 'ZMQ_POLLIN' is ignored. + // In that case 'poller_send_blocked' is not used. We need only 'poller_receive_blocked'. + // We also don't need 'poller_both_blocked', 'poller_backend_only' nor 'poller_frontend_only' no need to initialize it. + // We save some RAM and time for initialization. + zmq::socket_poller_t *poller_send_blocked = + NULL; // All except 'ZMQ_POLLIN' on 'backend_'. + zmq::socket_poller_t *poller_both_blocked = + NULL; // All except 'ZMQ_POLLIN' on both 'frontend_' and 'backend_'. + zmq::socket_poller_t *poller_frontend_only = + NULL; // Only 'ZMQ_POLLIN' and 'ZMQ_POLLOUT' on 'frontend_'. + zmq::socket_poller_t *poller_backend_only = + NULL; // Only 'ZMQ_POLLIN' and 'ZMQ_POLLOUT' on 'backend_'. + + if (frontend_ != backend_) { + poller_send_blocked = new (std::nothrow) + zmq::socket_poller_t; // All except 'ZMQ_POLLIN' on 'backend_'. + poller_both_blocked = new (std::nothrow) zmq:: + socket_poller_t; // All except 'ZMQ_POLLIN' on both 'frontend_' and 'backend_'. + poller_frontend_only = new (std::nothrow) zmq:: + socket_poller_t; // Only 'ZMQ_POLLIN' and 'ZMQ_POLLOUT' on 'frontend_'. + poller_backend_only = new (std::nothrow) zmq:: + socket_poller_t; // Only 'ZMQ_POLLIN' and 'ZMQ_POLLOUT' on 'backend_'. + frontend_equal_to_backend = false; + } else + frontend_equal_to_backend = true; + + if (poller_all == NULL || poller_in == NULL || poller_control == NULL + || poller_receive_blocked == NULL + || ((poller_send_blocked == NULL || poller_both_blocked == NULL) + && !frontend_equal_to_backend)) { + PROXY_CLEANUP (); + return close_and_return (&msg, -1); + } + + zmq::socket_poller_t *poller_wait = + poller_in; // Poller for blocking wait, initially all 'ZMQ_POLLIN'. + + // Register 'frontend_' and 'backend_' with pollers. + rc = poller_all->add (frontend_, NULL, + ZMQ_POLLIN | ZMQ_POLLOUT); // Everything. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_in->add (frontend_, NULL, ZMQ_POLLIN); // All 'ZMQ_POLLIN's. + CHECK_RC_EXIT_ON_FAILURE (); + + if (frontend_equal_to_backend) { + // If frontend_==backend_ 'poller_send_blocked' and 'poller_receive_blocked' are the same, + // so we don't need 'poller_send_blocked'. We need only 'poller_receive_blocked'. + // We also don't need 'poller_both_blocked', no need to initialize it. + rc = poller_receive_blocked->add (frontend_, NULL, ZMQ_POLLOUT); + CHECK_RC_EXIT_ON_FAILURE (); + } else { + rc = poller_all->add (backend_, NULL, + ZMQ_POLLIN | ZMQ_POLLOUT); // Everything. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_in->add (backend_, NULL, ZMQ_POLLIN); // All 'ZMQ_POLLIN's. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_both_blocked->add ( + frontend_, NULL, ZMQ_POLLOUT); // Waiting only for 'ZMQ_POLLOUT'. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_both_blocked->add ( + backend_, NULL, ZMQ_POLLOUT); // Waiting only for 'ZMQ_POLLOUT'. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_send_blocked->add ( + backend_, NULL, + ZMQ_POLLOUT); // All except 'ZMQ_POLLIN' on 'backend_'. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_send_blocked->add ( + frontend_, NULL, + ZMQ_POLLIN | ZMQ_POLLOUT); // All except 'ZMQ_POLLIN' on 'backend_'. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_receive_blocked->add ( + frontend_, NULL, + ZMQ_POLLOUT); // All except 'ZMQ_POLLIN' on 'frontend_'. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_receive_blocked->add ( + backend_, NULL, + ZMQ_POLLIN | ZMQ_POLLOUT); // All except 'ZMQ_POLLIN' on 'frontend_'. + CHECK_RC_EXIT_ON_FAILURE (); + rc = + poller_frontend_only->add (frontend_, NULL, ZMQ_POLLIN | ZMQ_POLLOUT); + CHECK_RC_EXIT_ON_FAILURE (); + rc = + poller_backend_only->add (backend_, NULL, ZMQ_POLLIN | ZMQ_POLLOUT); + CHECK_RC_EXIT_ON_FAILURE (); + } + + // Register 'control_' with pollers. + if (control_ != NULL) { + rc = poller_all->add (control_, NULL, ZMQ_POLLIN); + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_in->add (control_, NULL, ZMQ_POLLIN); + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_control->add ( + control_, NULL, + ZMQ_POLLIN); // When proxy is paused we wait only for ZMQ_POLLIN on 'control_' socket. + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_receive_blocked->add (control_, NULL, ZMQ_POLLIN); + CHECK_RC_EXIT_ON_FAILURE (); + if (!frontend_equal_to_backend) { + rc = poller_send_blocked->add (control_, NULL, ZMQ_POLLIN); + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_both_blocked->add (control_, NULL, ZMQ_POLLIN); + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_frontend_only->add (control_, NULL, ZMQ_POLLIN); + CHECK_RC_EXIT_ON_FAILURE (); + rc = poller_backend_only->add (control_, NULL, ZMQ_POLLIN); + CHECK_RC_EXIT_ON_FAILURE (); + } + } + + bool request_processed, reply_processed; + + while (state != terminated) { + // Blocking wait initially only for 'ZMQ_POLLIN' - 'poller_wait' points to 'poller_in'. + // If one of receiving end's queue is full ('ZMQ_POLLOUT' not available), + // 'poller_wait' is pointed to 'poller_receive_blocked', 'poller_send_blocked' or 'poller_both_blocked'. + rc = poller_wait->wait (events, 3, -1); + if (rc < 0 && errno == EAGAIN) + rc = 0; + CHECK_RC_EXIT_ON_FAILURE (); + + // Some of events waited for by 'poller_wait' have arrived, now poll for everything without blocking. + rc = poller_all->wait (events, 3, 0); + if (rc < 0 && errno == EAGAIN) + rc = 0; + CHECK_RC_EXIT_ON_FAILURE (); + + // Process events. + for (int i = 0; i < rc; i++) { + if (events[i].socket == frontend_) { + frontend_in = (events[i].events & ZMQ_POLLIN) != 0; + frontend_out = (events[i].events & ZMQ_POLLOUT) != 0; + } else + // This 'if' needs to be after check for 'frontend_' in order never + // to be reached in case frontend_==backend_, so we ensure backend_in=false in that case. + if (events[i].socket == backend_) { + backend_in = (events[i].events & ZMQ_POLLIN) != 0; + backend_out = (events[i].events & ZMQ_POLLOUT) != 0; + } else if (events[i].socket == control_) + control_in = (events[i].events & ZMQ_POLLIN) != 0; + } + + + // Process a control command if any. + if (control_in) { + rc = control_->recv (&msg, 0); + CHECK_RC_EXIT_ON_FAILURE (); + rc = control_->getsockopt (ZMQ_RCVMORE, &more, &moresz); + if (unlikely (rc < 0) || more) { + PROXY_CLEANUP (); + return close_and_return (&msg, -1); + } + + // Copy message to capture socket if any. + rc = capture (capture_, &msg); + CHECK_RC_EXIT_ON_FAILURE (); + + if (msg.size () == 5 && memcmp (msg.data (), "PAUSE", 5) == 0) { + state = paused; + poller_wait = poller_control; + } else if (msg.size () == 6 + && memcmp (msg.data (), "RESUME", 6) == 0) { + state = active; + poller_wait = poller_in; + } else { + if (msg.size () == 9 + && memcmp (msg.data (), "TERMINATE", 9) == 0) + state = terminated; + else { + if (msg.size () == 10 + && memcmp (msg.data (), "STATISTICS", 10) == 0) { + rc = reply_stats (control_, &frontend_stats, + &backend_stats); + CHECK_RC_EXIT_ON_FAILURE (); + } else { + // This is an API error, we assert + puts ("E: invalid command sent to proxy"); + zmq_assert (false); + } + } + } + control_in = false; + } + + if (state == active) { + // Process a request, 'ZMQ_POLLIN' on 'frontend_' and 'ZMQ_POLLOUT' on 'backend_'. + // In case of frontend_==backend_ there's no 'ZMQ_POLLOUT' event. + if (frontend_in && (backend_out || frontend_equal_to_backend)) { + rc = forward (frontend_, &frontend_stats, backend_, + &backend_stats, capture_, &msg); + CHECK_RC_EXIT_ON_FAILURE (); + request_processed = true; + frontend_in = backend_out = false; + } else + request_processed = false; + + // Process a reply, 'ZMQ_POLLIN' on 'backend_' and 'ZMQ_POLLOUT' on 'frontend_'. + // If 'frontend_' and 'backend_' are the same this is not needed because previous processing + // covers all of the cases. 'backend_in' is always false if frontend_==backend_ due to + // design in 'for' event processing loop. + if (backend_in && frontend_out) { + rc = forward (backend_, &backend_stats, frontend_, + &frontend_stats, capture_, &msg); + CHECK_RC_EXIT_ON_FAILURE (); + reply_processed = true; + backend_in = frontend_out = false; + } else + reply_processed = false; + + if (request_processed || reply_processed) { + // If request/reply is processed that means we had at least one 'ZMQ_POLLOUT' event. + // Enable corresponding 'ZMQ_POLLIN' for blocking wait if any was disabled. + if (poller_wait != poller_in) { + if (request_processed) { // 'frontend_' -> 'backend_' + if (poller_wait == poller_both_blocked) + poller_wait = poller_send_blocked; + else if (poller_wait == poller_receive_blocked + || poller_wait == poller_frontend_only) + poller_wait = poller_in; + } + if (reply_processed) { // 'backend_' -> 'frontend_' + if (poller_wait == poller_both_blocked) + poller_wait = poller_receive_blocked; + else if (poller_wait == poller_send_blocked + || poller_wait == poller_backend_only) + poller_wait = poller_in; + } + } + } else { + // No requests have been processed, there were no 'ZMQ_POLLIN' with corresponding 'ZMQ_POLLOUT' events. + // That means that out queue(s) is/are full or one out queue is full and second one has no messages to process. + // Disable receiving 'ZMQ_POLLIN' for sockets for which there's no 'ZMQ_POLLOUT', + // or wait only on both 'backend_''s or 'frontend_''s 'ZMQ_POLLIN' and 'ZMQ_POLLOUT'. + if (frontend_in) { + if (frontend_out) + // If frontend_in and frontend_out are true, obviously backend_in and backend_out are both false. + // In that case we need to wait for both 'ZMQ_POLLIN' and 'ZMQ_POLLOUT' only on 'backend_'. + // We'll never get here in case of frontend_==backend_ because then frontend_out will always be false. + poller_wait = poller_backend_only; + else { + if (poller_wait == poller_send_blocked) + poller_wait = poller_both_blocked; + else if (poller_wait == poller_in) + poller_wait = poller_receive_blocked; + } + } + if (backend_in) { + // Will never be reached if frontend_==backend_, 'backend_in' will + // always be false due to design in 'for' event processing loop. + if (backend_out) + // If backend_in and backend_out are true, obviously frontend_in and frontend_out are both false. + // In that case we need to wait for both 'ZMQ_POLLIN' and 'ZMQ_POLLOUT' only on 'frontend_'. + poller_wait = poller_frontend_only; + else { + if (poller_wait == poller_receive_blocked) + poller_wait = poller_both_blocked; + else if (poller_wait == poller_in) + poller_wait = poller_send_blocked; + } + } + } + } + } + PROXY_CLEANUP (); + return close_and_return (&msg, 0); +} + +#else // ZMQ_HAVE_POLLER + +int zmq::proxy (class socket_base_t *frontend_, + class socket_base_t *backend_, + class socket_base_t *capture_, + class socket_base_t *control_) +{ + msg_t msg; + int rc = msg.init (); + if (rc != 0) + return -1; + + // The algorithm below assumes ratio of requests and replies processed + // under full load to be 1:1. + + int more; + size_t moresz; + zmq_pollitem_t items[] = {{frontend_, 0, ZMQ_POLLIN, 0}, + {backend_, 0, ZMQ_POLLIN, 0}, + {control_, 0, ZMQ_POLLIN, 0}}; + int qt_poll_items = (control_ ? 3 : 2); + zmq_pollitem_t itemsout[] = {{frontend_, 0, ZMQ_POLLOUT, 0}, + {backend_, 0, ZMQ_POLLOUT, 0}}; + + zmq_socket_stats_t frontend_stats; + memset (&frontend_stats, 0, sizeof (frontend_stats)); + zmq_socket_stats_t backend_stats; + memset (&backend_stats, 0, sizeof (backend_stats)); + + // Proxy can be in these three states + enum + { + active, + paused, + terminated + } state = active; + + while (state != terminated) { + // Wait while there are either requests or replies to process. + rc = zmq_poll (&items[0], qt_poll_items, -1); + if (unlikely (rc < 0)) + return close_and_return (&msg, -1); + + // Get the pollout separately because when combining this with pollin it maxes the CPU + // because pollout shall most of the time return directly. + // POLLOUT is only checked when frontend and backend sockets are not the same. + if (frontend_ != backend_) { + rc = zmq_poll (&itemsout[0], 2, 0); + if (unlikely (rc < 0)) { + return close_and_return (&msg, -1); + } + } + + // Process a control command if any + if (control_ && items[2].revents & ZMQ_POLLIN) { + rc = control_->recv (&msg, 0); + if (unlikely (rc < 0)) + return close_and_return (&msg, -1); + + moresz = sizeof more; + rc = control_->getsockopt (ZMQ_RCVMORE, &more, &moresz); + if (unlikely (rc < 0) || more) + return close_and_return (&msg, -1); + + // Copy message to capture socket if any + rc = capture (capture_, &msg); + if (unlikely (rc < 0)) + return close_and_return (&msg, -1); + + if (msg.size () == 5 && memcmp (msg.data (), "PAUSE", 5) == 0) + state = paused; + else if (msg.size () == 6 && memcmp (msg.data (), "RESUME", 6) == 0) + state = active; + else if (msg.size () == 9 + && memcmp (msg.data (), "TERMINATE", 9) == 0) + state = terminated; + else { + if (msg.size () == 10 + && memcmp (msg.data (), "STATISTICS", 10) == 0) { + rc = + reply_stats (control_, &frontend_stats, &backend_stats); + if (unlikely (rc < 0)) + return close_and_return (&msg, -1); + } else { + // This is an API error, we assert + puts ("E: invalid command sent to proxy"); + zmq_assert (false); + } + } + } + // Process a request + if (state == active && items[0].revents & ZMQ_POLLIN + && (frontend_ == backend_ || itemsout[1].revents & ZMQ_POLLOUT)) { + rc = forward (frontend_, &frontend_stats, backend_, &backend_stats, + capture_, &msg); + if (unlikely (rc < 0)) + return close_and_return (&msg, -1); + } + // Process a reply + if (state == active && frontend_ != backend_ + && items[1].revents & ZMQ_POLLIN + && itemsout[0].revents & ZMQ_POLLOUT) { + rc = forward (backend_, &backend_stats, frontend_, &frontend_stats, + capture_, &msg); + if (unlikely (rc < 0)) + return close_and_return (&msg, -1); + } + } + + return close_and_return (&msg, 0); +} + +#endif // ZMQ_HAVE_POLLER diff --git a/3rd/libzmq/src/proxy.hpp b/3rd/libzmq/src/proxy.hpp new file mode 100644 index 00000000..f078c6ae --- /dev/null +++ b/3rd/libzmq/src/proxy.hpp @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PROXY_HPP_INCLUDED__ +#define __ZMQ_PROXY_HPP_INCLUDED__ + +namespace zmq +{ +int proxy (class socket_base_t *frontend_, + class socket_base_t *backend_, + class socket_base_t *capture_, + class socket_base_t *control_ = + NULL); // backward compatibility without this argument +} + +#endif diff --git a/3rd/libzmq/src/pub.cpp b/3rd/libzmq/src/pub.cpp new file mode 100644 index 00000000..fe42982e --- /dev/null +++ b/3rd/libzmq/src/pub.cpp @@ -0,0 +1,69 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "pub.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::pub_t::pub_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + xpub_t (parent_, tid_, sid_) +{ + options.type = ZMQ_PUB; +} + +zmq::pub_t::~pub_t () +{ +} + +void zmq::pub_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + zmq_assert (pipe_); + + // Don't delay pipe termination as there is no one + // to receive the delimiter. + pipe_->set_nodelay (); + + xpub_t::xattach_pipe (pipe_, subscribe_to_all_, locally_initiated_); +} + +int zmq::pub_t::xrecv (class msg_t *) +{ + // Messages cannot be received from PUB socket. + errno = ENOTSUP; + return -1; +} + +bool zmq::pub_t::xhas_in () +{ + return false; +} diff --git a/3rd/libzmq/src/pub.hpp b/3rd/libzmq/src/pub.hpp new file mode 100644 index 00000000..b423da02 --- /dev/null +++ b/3rd/libzmq/src/pub.hpp @@ -0,0 +1,59 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PUB_HPP_INCLUDED__ +#define __ZMQ_PUB_HPP_INCLUDED__ + +#include "xpub.hpp" + +namespace zmq +{ +class ctx_t; +class io_thread_t; +class socket_base_t; +class msg_t; + +class pub_t ZMQ_FINAL : public xpub_t +{ + public: + pub_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~pub_t (); + + // Implementations of virtual functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_ = false, + bool locally_initiated_ = false); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (pub_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/pull.cpp b/3rd/libzmq/src/pull.cpp new file mode 100644 index 00000000..9c9a843e --- /dev/null +++ b/3rd/libzmq/src/pull.cpp @@ -0,0 +1,76 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "pull.hpp" +#include "err.hpp" +#include "msg.hpp" +#include "pipe.hpp" + +zmq::pull_t::pull_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_) +{ + options.type = ZMQ_PULL; +} + +zmq::pull_t::~pull_t () +{ +} + +void zmq::pull_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + _fq.attach (pipe_); +} + +void zmq::pull_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +void zmq::pull_t::xpipe_terminated (pipe_t *pipe_) +{ + _fq.pipe_terminated (pipe_); +} + +int zmq::pull_t::xrecv (msg_t *msg_) +{ + return _fq.recv (msg_); +} + +bool zmq::pull_t::xhas_in () +{ + return _fq.has_in (); +} diff --git a/3rd/libzmq/src/pull.hpp b/3rd/libzmq/src/pull.hpp new file mode 100644 index 00000000..4634d82c --- /dev/null +++ b/3rd/libzmq/src/pull.hpp @@ -0,0 +1,68 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PULL_HPP_INCLUDED__ +#define __ZMQ_PULL_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "fq.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; +class msg_t; +class io_thread_t; + +class pull_t ZMQ_FINAL : public socket_base_t +{ + public: + pull_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~pull_t (); + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + void xread_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + // Fair queueing object for inbound pipes. + fq_t _fq; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (pull_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/push.cpp b/3rd/libzmq/src/push.cpp new file mode 100644 index 00000000..42184807 --- /dev/null +++ b/3rd/libzmq/src/push.cpp @@ -0,0 +1,80 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "push.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::push_t::push_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_) +{ + options.type = ZMQ_PUSH; +} + +zmq::push_t::~push_t () +{ +} + +void zmq::push_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + // Don't delay pipe termination as there is no one + // to receive the delimiter. + pipe_->set_nodelay (); + + zmq_assert (pipe_); + _lb.attach (pipe_); +} + +void zmq::push_t::xwrite_activated (pipe_t *pipe_) +{ + _lb.activated (pipe_); +} + +void zmq::push_t::xpipe_terminated (pipe_t *pipe_) +{ + _lb.pipe_terminated (pipe_); +} + +int zmq::push_t::xsend (msg_t *msg_) +{ + return _lb.send (msg_); +} + +bool zmq::push_t::xhas_out () +{ + return _lb.has_out (); +} diff --git a/3rd/libzmq/src/push.hpp b/3rd/libzmq/src/push.hpp new file mode 100644 index 00000000..3b908c10 --- /dev/null +++ b/3rd/libzmq/src/push.hpp @@ -0,0 +1,68 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_PUSH_HPP_INCLUDED__ +#define __ZMQ_PUSH_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "lb.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; +class msg_t; +class io_thread_t; + +class push_t ZMQ_FINAL : public socket_base_t +{ + public: + push_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~push_t (); + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + bool xhas_out (); + void xwrite_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + // Load balancer managing the outbound pipes. + lb_t _lb; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (push_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/radio.cpp b/3rd/libzmq/src/radio.cpp new file mode 100644 index 00000000..aa9f100a --- /dev/null +++ b/3rd/libzmq/src/radio.cpp @@ -0,0 +1,286 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include "radio.hpp" +#include "macros.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::radio_t::radio_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_, true), + _lossy (true) +{ + options.type = ZMQ_RADIO; +} + +zmq::radio_t::~radio_t () +{ +} + +void zmq::radio_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + + // Don't delay pipe termination as there is no one + // to receive the delimiter. + pipe_->set_nodelay (); + + _dist.attach (pipe_); + + if (subscribe_to_all_) + _udp_pipes.push_back (pipe_); + // The pipe is active when attached. Let's read the subscriptions from + // it, if any. + else + xread_activated (pipe_); +} + +void zmq::radio_t::xread_activated (pipe_t *pipe_) +{ + // There are some subscriptions waiting. Let's process them. + msg_t msg; + while (pipe_->read (&msg)) { + // Apply the subscription to the trie + if (msg.is_join () || msg.is_leave ()) { + std::string group = std::string (msg.group ()); + + if (msg.is_join ()) + _subscriptions.ZMQ_MAP_INSERT_OR_EMPLACE (ZMQ_MOVE (group), + pipe_); + else { + std::pair + range = _subscriptions.equal_range (group); + + for (subscriptions_t::iterator it = range.first; + it != range.second; ++it) { + if (it->second == pipe_) { + _subscriptions.erase (it); + break; + } + } + } + } + msg.close (); + } +} + +void zmq::radio_t::xwrite_activated (pipe_t *pipe_) +{ + _dist.activated (pipe_); +} +int zmq::radio_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + if (optvallen_ != sizeof (int) || *static_cast (optval_) < 0) { + errno = EINVAL; + return -1; + } + if (option_ == ZMQ_XPUB_NODROP) + _lossy = (*static_cast (optval_) == 0); + else { + errno = EINVAL; + return -1; + } + return 0; +} + +void zmq::radio_t::xpipe_terminated (pipe_t *pipe_) +{ + for (subscriptions_t::iterator it = _subscriptions.begin (), + end = _subscriptions.end (); + it != end;) { + if (it->second == pipe_) { +#if __cplusplus >= 201103L || (defined _MSC_VER && _MSC_VER >= 1700) + it = _subscriptions.erase (it); +#else + _subscriptions.erase (it++); +#endif + } else { + ++it; + } + } + + { + const udp_pipes_t::iterator end = _udp_pipes.end (); + const udp_pipes_t::iterator it = + std::find (_udp_pipes.begin (), end, pipe_); + if (it != end) + _udp_pipes.erase (it); + } + + _dist.pipe_terminated (pipe_); +} + +int zmq::radio_t::xsend (msg_t *msg_) +{ + // Radio sockets do not allow multipart data (ZMQ_SNDMORE) + if (msg_->flags () & msg_t::more) { + errno = EINVAL; + return -1; + } + + _dist.unmatch (); + + const std::pair + range = _subscriptions.equal_range (std::string (msg_->group ())); + + for (subscriptions_t::iterator it = range.first; it != range.second; ++it) + _dist.match (it->second); + + for (udp_pipes_t::iterator it = _udp_pipes.begin (), + end = _udp_pipes.end (); + it != end; ++it) + _dist.match (*it); + + int rc = -1; + if (_lossy || _dist.check_hwm ()) { + if (_dist.send_to_matching (msg_) == 0) { + rc = 0; // Yay, sent successfully + } + } else + errno = EAGAIN; + + return rc; +} + +bool zmq::radio_t::xhas_out () +{ + return _dist.has_out (); +} + +int zmq::radio_t::xrecv (msg_t *msg_) +{ + // Messages cannot be received from PUB socket. + LIBZMQ_UNUSED (msg_); + errno = ENOTSUP; + return -1; +} + +bool zmq::radio_t::xhas_in () +{ + return false; +} + +zmq::radio_session_t::radio_session_t (io_thread_t *io_thread_, + bool connect_, + socket_base_t *socket_, + const options_t &options_, + address_t *addr_) : + session_base_t (io_thread_, connect_, socket_, options_, addr_), + _state (group) +{ +} + +zmq::radio_session_t::~radio_session_t () +{ +} + +int zmq::radio_session_t::push_msg (msg_t *msg_) +{ + if (msg_->flags () & msg_t::command) { + char *command_data = static_cast (msg_->data ()); + const size_t data_size = msg_->size (); + + int group_length; + const char *group; + + msg_t join_leave_msg; + int rc; + + // Set the msg type to either JOIN or LEAVE + if (data_size >= 5 && memcmp (command_data, "\4JOIN", 5) == 0) { + group_length = static_cast (data_size) - 5; + group = command_data + 5; + rc = join_leave_msg.init_join (); + } else if (data_size >= 6 && memcmp (command_data, "\5LEAVE", 6) == 0) { + group_length = static_cast (data_size) - 6; + group = command_data + 6; + rc = join_leave_msg.init_leave (); + } + // If it is not a JOIN or LEAVE just push the message + else + return session_base_t::push_msg (msg_); + + errno_assert (rc == 0); + + // Set the group + rc = join_leave_msg.set_group (group, group_length); + errno_assert (rc == 0); + + // Close the current command + rc = msg_->close (); + errno_assert (rc == 0); + + // Push the join or leave command + *msg_ = join_leave_msg; + return session_base_t::push_msg (msg_); + } + return session_base_t::push_msg (msg_); +} + +int zmq::radio_session_t::pull_msg (msg_t *msg_) +{ + if (_state == group) { + int rc = session_base_t::pull_msg (&_pending_msg); + if (rc != 0) + return rc; + + const char *group = _pending_msg.group (); + const int length = static_cast (strlen (group)); + + // First frame is the group + rc = msg_->init_size (length); + errno_assert (rc == 0); + msg_->set_flags (msg_t::more); + memcpy (msg_->data (), group, length); + + // Next status is the body + _state = body; + return 0; + } + *msg_ = _pending_msg; + _state = group; + return 0; +} + +void zmq::radio_session_t::reset () +{ + session_base_t::reset (); + _state = group; +} diff --git a/3rd/libzmq/src/radio.hpp b/3rd/libzmq/src/radio.hpp new file mode 100644 index 00000000..8b3bd641 --- /dev/null +++ b/3rd/libzmq/src/radio.hpp @@ -0,0 +1,113 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_RADIO_HPP_INCLUDED__ +#define __ZMQ_RADIO_HPP_INCLUDED__ + +#include +#include +#include + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "dist.hpp" +#include "msg.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; +class io_thread_t; + +class radio_t ZMQ_FINAL : public socket_base_t +{ + public: + radio_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~radio_t (); + + // Implementations of virtual functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_ = false, + bool locally_initiated_ = false); + int xsend (zmq::msg_t *msg_); + bool xhas_out (); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + void xread_activated (zmq::pipe_t *pipe_); + void xwrite_activated (zmq::pipe_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + // List of all subscriptions mapped to corresponding pipes. + typedef std::multimap subscriptions_t; + subscriptions_t _subscriptions; + + // List of udp pipes + typedef std::vector udp_pipes_t; + udp_pipes_t _udp_pipes; + + // Distributor of messages holding the list of outbound pipes. + dist_t _dist; + + // Drop messages if HWM reached, otherwise return with EAGAIN + bool _lossy; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (radio_t) +}; + +class radio_session_t ZMQ_FINAL : public session_base_t +{ + public: + radio_session_t (zmq::io_thread_t *io_thread_, + bool connect_, + zmq::socket_base_t *socket_, + const options_t &options_, + address_t *addr_); + ~radio_session_t (); + + // Overrides of the functions from session_base_t. + int push_msg (msg_t *msg_); + int pull_msg (msg_t *msg_); + void reset (); + + private: + enum + { + group, + body + } _state; + + msg_t _pending_msg; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (radio_session_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/radix_tree.cpp b/3rd/libzmq/src/radix_tree.cpp new file mode 100644 index 00000000..04374eec --- /dev/null +++ b/3rd/libzmq/src/radix_tree.cpp @@ -0,0 +1,578 @@ +/* + Copyright (c) 2018 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "err.hpp" +#include "radix_tree.hpp" + +#include +#include +#include +#include + +node_t::node_t (unsigned char *data_) : _data (data_) +{ +} + +uint32_t node_t::refcount () +{ + uint32_t u32; + memcpy (&u32, _data, sizeof (u32)); + return u32; +} + +void node_t::set_refcount (uint32_t value_) +{ + memcpy (_data, &value_, sizeof (value_)); +} + +uint32_t node_t::prefix_length () +{ + uint32_t u32; + memcpy (&u32, _data + sizeof (uint32_t), sizeof (u32)); + return u32; +} + +void node_t::set_prefix_length (uint32_t value_) +{ + memcpy (_data + sizeof (value_), &value_, sizeof (value_)); +} + +uint32_t node_t::edgecount () +{ + uint32_t u32; + memcpy (&u32, _data + 2 * sizeof (uint32_t), sizeof (u32)); + return u32; +} + +void node_t::set_edgecount (uint32_t value_) +{ + memcpy (_data + 2 * sizeof (value_), &value_, sizeof (value_)); +} + +unsigned char *node_t::prefix () +{ + return _data + 3 * sizeof (uint32_t); +} + +void node_t::set_prefix (const unsigned char *bytes_) +{ + memcpy (prefix (), bytes_, prefix_length ()); +} + +unsigned char *node_t::first_bytes () +{ + return prefix () + prefix_length (); +} + +void node_t::set_first_bytes (const unsigned char *bytes_) +{ + memcpy (first_bytes (), bytes_, edgecount ()); +} + +unsigned char node_t::first_byte_at (size_t index_) +{ + zmq_assert (index_ < edgecount ()); + return first_bytes ()[index_]; +} + +void node_t::set_first_byte_at (size_t index_, unsigned char byte_) +{ + zmq_assert (index_ < edgecount ()); + first_bytes ()[index_] = byte_; +} + +unsigned char *node_t::node_pointers () +{ + return prefix () + prefix_length () + edgecount (); +} + +void node_t::set_node_pointers (const unsigned char *pointers_) +{ + memcpy (node_pointers (), pointers_, edgecount () * sizeof (void *)); +} + +node_t node_t::node_at (size_t index_) +{ + zmq_assert (index_ < edgecount ()); + + unsigned char *data; + memcpy (&data, node_pointers () + index_ * sizeof (void *), sizeof (data)); + return node_t (data); +} + +void node_t::set_node_at (size_t index_, node_t node_) +{ + zmq_assert (index_ < edgecount ()); + memcpy (node_pointers () + index_ * sizeof (void *), &node_._data, + sizeof (node_._data)); +} + +void node_t::set_edge_at (size_t index_, + unsigned char first_byte_, + node_t node_) +{ + set_first_byte_at (index_, first_byte_); + set_node_at (index_, node_); +} + +bool node_t::operator== (node_t other_) const +{ + return _data == other_._data; +} + +bool node_t::operator!= (node_t other_) const +{ + return !(*this == other_); +} + +void node_t::resize (size_t prefix_length_, size_t edgecount_) +{ + const size_t node_size = 3 * sizeof (uint32_t) + prefix_length_ + + edgecount_ * (1 + sizeof (void *)); + unsigned char *new_data = + static_cast (realloc (_data, node_size)); + zmq_assert (new_data); + _data = new_data; + set_prefix_length (static_cast (prefix_length_)); + set_edgecount (static_cast (edgecount_)); +} + +node_t make_node (size_t refcount_, size_t prefix_length_, size_t edgecount_) +{ + const size_t node_size = 3 * sizeof (uint32_t) + prefix_length_ + + edgecount_ * (1 + sizeof (void *)); + + unsigned char *data = static_cast (malloc (node_size)); + zmq_assert (data); + + node_t node (data); + node.set_refcount (static_cast (refcount_)); + node.set_prefix_length (static_cast (prefix_length_)); + node.set_edgecount (static_cast (edgecount_)); + return node; +} + +// ---------------------------------------------------------------------- + +zmq::radix_tree_t::radix_tree_t () : _root (make_node (0, 0, 0)), _size (0) +{ +} + +static void free_nodes (node_t node_) +{ + for (size_t i = 0, count = node_.edgecount (); i < count; ++i) + free_nodes (node_.node_at (i)); + free (node_._data); +} + +zmq::radix_tree_t::~radix_tree_t () +{ + free_nodes (_root); +} + +match_result_t::match_result_t (size_t key_bytes_matched_, + size_t prefix_bytes_matched_, + size_t edge_index_, + size_t parent_edge_index_, + node_t current_, + node_t parent_, + node_t grandparent_) : + _key_bytes_matched (key_bytes_matched_), + _prefix_bytes_matched (prefix_bytes_matched_), + _edge_index (edge_index_), + _parent_edge_index (parent_edge_index_), + _current_node (current_), + _parent_node (parent_), + _grandparent_node (grandparent_) +{ +} + +match_result_t zmq::radix_tree_t::match (const unsigned char *key_, + size_t key_size_, + bool is_lookup_ = false) const +{ + zmq_assert (key_); + + // Node we're currently at in the traversal and its predecessors. + node_t current_node = _root; + node_t parent_node = current_node; + node_t grandparent_node = current_node; + // Index of the next byte to match in the key. + size_t key_byte_index = 0; + // Index of the next byte to match in the current node's prefix. + size_t prefix_byte_index = 0; + // Index of the edge from parent to current node. + size_t edge_index = 0; + // Index of the edge from grandparent to parent. + size_t parent_edge_index = 0; + + while (current_node.prefix_length () > 0 || current_node.edgecount () > 0) { + const unsigned char *const prefix = current_node.prefix (); + const size_t prefix_length = current_node.prefix_length (); + + for (prefix_byte_index = 0; + prefix_byte_index < prefix_length && key_byte_index < key_size_; + ++prefix_byte_index, ++key_byte_index) { + if (prefix[prefix_byte_index] != key_[key_byte_index]) + break; + } + + // Even if a prefix of the key matches and we're doing a + // lookup, this means we've found a matching subscription. + if (is_lookup_ && prefix_byte_index == prefix_length + && current_node.refcount () > 0) { + key_byte_index = key_size_; + break; + } + + // There was a mismatch or we've matched the whole key, so + // there's nothing more to do. + if (prefix_byte_index != prefix_length || key_byte_index == key_size_) + break; + + // We need to match the rest of the key. Check if there's an + // outgoing edge from this node. + node_t next_node = current_node; + for (size_t i = 0, edgecount = current_node.edgecount (); i < edgecount; + ++i) { + if (current_node.first_byte_at (i) == key_[key_byte_index]) { + parent_edge_index = edge_index; + edge_index = i; + next_node = current_node.node_at (i); + break; + } + } + + if (next_node == current_node) + break; // No outgoing edge. + grandparent_node = parent_node; + parent_node = current_node; + current_node = next_node; + } + + return match_result_t (key_byte_index, prefix_byte_index, edge_index, + parent_edge_index, current_node, parent_node, + grandparent_node); +} + +bool zmq::radix_tree_t::add (const unsigned char *key_, size_t key_size_) +{ + const match_result_t match_result = match (key_, key_size_); + const size_t key_bytes_matched = match_result._key_bytes_matched; + const size_t prefix_bytes_matched = match_result._prefix_bytes_matched; + const size_t edge_index = match_result._edge_index; + node_t current_node = match_result._current_node; + node_t parent_node = match_result._parent_node; + + if (key_bytes_matched != key_size_) { + // Not all characters match, we might have to split the node. + if (prefix_bytes_matched == current_node.prefix_length ()) { + // The mismatch is at one of the outgoing edges, so we + // create an edge from the current node to a new leaf node + // that has the rest of the key as the prefix. + node_t key_node = make_node (1, key_size_ - key_bytes_matched, 0); + key_node.set_prefix (key_ + key_bytes_matched); + + // Reallocate for one more edge. + current_node.resize (current_node.prefix_length (), + current_node.edgecount () + 1); + + // Make room for the new edge. We need to shift the chunk + // of node pointers one byte to the right. Since resize() + // increments the edgecount by 1, node_pointers() tells us the + // destination address. The chunk of node pointers starts + // at one byte to the left of this destination. + // + // Since the regions can overlap, we use memmove. + memmove (current_node.node_pointers (), + current_node.node_pointers () - 1, + (current_node.edgecount () - 1) * sizeof (void *)); + + // Add an edge to the new node. + current_node.set_edge_at (current_node.edgecount () - 1, + key_[key_bytes_matched], key_node); + + // We need to update all pointers to the current node + // after the call to resize(). + if (current_node.prefix_length () == 0) + _root._data = current_node._data; + else + parent_node.set_node_at (edge_index, current_node); + ++_size; + return true; + } + + // There was a mismatch, so we need to split this node. + // + // Create two nodes that will be reachable from the parent. + // One node will have the rest of the characters from the key, + // and the other node will have the rest of the characters + // from the current node's prefix. + node_t key_node = make_node (1, key_size_ - key_bytes_matched, 0); + node_t split_node = + make_node (current_node.refcount (), + current_node.prefix_length () - prefix_bytes_matched, + current_node.edgecount ()); + + // Copy the prefix chunks to the new nodes. + key_node.set_prefix (key_ + key_bytes_matched); + split_node.set_prefix (current_node.prefix () + prefix_bytes_matched); + + // Copy the current node's edges to the new node. + split_node.set_first_bytes (current_node.first_bytes ()); + split_node.set_node_pointers (current_node.node_pointers ()); + + // Resize the current node to accommodate a prefix comprising + // the matched characters and 2 outgoing edges to the above + // nodes. Set the refcount to 0 since this node doesn't hold a + // key. + current_node.resize (prefix_bytes_matched, 2); + current_node.set_refcount (0); + + // Add links to the new nodes. We don't need to copy the + // prefix since resize() retains it in the current node. + current_node.set_edge_at (0, key_node.prefix ()[0], key_node); + current_node.set_edge_at (1, split_node.prefix ()[0], split_node); + + ++_size; + parent_node.set_node_at (edge_index, current_node); + return true; + } + + // All characters in the key match, but we still might need to split. + if (prefix_bytes_matched != current_node.prefix_length ()) { + // All characters in the key match, but not all characters + // from the current node's prefix match. + + // Create a node that contains the rest of the characters from + // the current node's prefix and the outgoing edges from the + // current node. + node_t split_node = + make_node (current_node.refcount (), + current_node.prefix_length () - prefix_bytes_matched, + current_node.edgecount ()); + split_node.set_prefix (current_node.prefix () + prefix_bytes_matched); + split_node.set_first_bytes (current_node.first_bytes ()); + split_node.set_node_pointers (current_node.node_pointers ()); + + // Resize the current node to hold only the matched characters + // from its prefix and one edge to the new node. + current_node.resize (prefix_bytes_matched, 1); + + // Add an edge to the split node and set the refcount to 1 + // since this key wasn't inserted earlier. We don't need to + // set the prefix because the first `prefix_bytes_matched` bytes + // in the prefix are preserved by resize(). + current_node.set_edge_at (0, split_node.prefix ()[0], split_node); + current_node.set_refcount (1); + + ++_size; + parent_node.set_node_at (edge_index, current_node); + return true; + } + + zmq_assert (key_bytes_matched == key_size_); + zmq_assert (prefix_bytes_matched == current_node.prefix_length ()); + + ++_size; + current_node.set_refcount (current_node.refcount () + 1); + return current_node.refcount () == 1; +} + +bool zmq::radix_tree_t::rm (const unsigned char *key_, size_t key_size_) +{ + const match_result_t match_result = match (key_, key_size_); + const size_t key_bytes_matched = match_result._key_bytes_matched; + const size_t prefix_bytes_matched = match_result._prefix_bytes_matched; + const size_t edge_index = match_result._edge_index; + const size_t parent_edge_index = match_result._parent_edge_index; + node_t current_node = match_result._current_node; + node_t parent_node = match_result._parent_node; + node_t grandparent_node = match_result._grandparent_node; + + if (key_bytes_matched != key_size_ + || prefix_bytes_matched != current_node.prefix_length () + || current_node.refcount () == 0) + return false; + + current_node.set_refcount (current_node.refcount () - 1); + --_size; + if (current_node.refcount () > 0) + return false; + + // Don't delete the root node. + if (current_node == _root) + return true; + + const size_t outgoing_edges = current_node.edgecount (); + if (outgoing_edges > 1) + // This node can't be merged with any other node, so there's + // nothing more to do. + return true; + + if (outgoing_edges == 1) { + // Merge this node with the single child node. + node_t child = current_node.node_at (0); + + // Make room for the child node's prefix and edges. We need to + // keep the old prefix length since resize() will overwrite + // it. + const uint32_t old_prefix_length = current_node.prefix_length (); + current_node.resize (old_prefix_length + child.prefix_length (), + child.edgecount ()); + + // Append the child node's prefix to the current node. + memcpy (current_node.prefix () + old_prefix_length, child.prefix (), + child.prefix_length ()); + + // Copy the rest of child node's data to the current node. + current_node.set_first_bytes (child.first_bytes ()); + current_node.set_node_pointers (child.node_pointers ()); + current_node.set_refcount (child.refcount ()); + + free (child._data); + parent_node.set_node_at (edge_index, current_node); + return true; + } + + if (parent_node.edgecount () == 2 && parent_node.refcount () == 0 + && parent_node != _root) { + // Removing this node leaves the parent with one child. + // If the parent doesn't hold a key or if it isn't the root, + // we can merge it with its single child node. + zmq_assert (edge_index < 2); + node_t other_child = parent_node.node_at (!edge_index); + + // Make room for the child node's prefix and edges. We need to + // keep the old prefix length since resize() will overwrite + // it. + const uint32_t old_prefix_length = parent_node.prefix_length (); + parent_node.resize (old_prefix_length + other_child.prefix_length (), + other_child.edgecount ()); + + // Append the child node's prefix to the current node. + memcpy (parent_node.prefix () + old_prefix_length, + other_child.prefix (), other_child.prefix_length ()); + + // Copy the rest of child node's data to the current node. + parent_node.set_first_bytes (other_child.first_bytes ()); + parent_node.set_node_pointers (other_child.node_pointers ()); + parent_node.set_refcount (other_child.refcount ()); + + free (current_node._data); + free (other_child._data); + grandparent_node.set_node_at (parent_edge_index, parent_node); + return true; + } + + // This is a leaf node that doesn't leave its parent with one + // outgoing edge. Remove the outgoing edge to this node from the + // parent. + zmq_assert (outgoing_edges == 0); + + // Replace the edge to the current node with the last edge. An + // edge consists of a byte and a pointer to the next node. First + // replace the byte. + const size_t last_index = parent_node.edgecount () - 1; + const unsigned char last_byte = parent_node.first_byte_at (last_index); + const node_t last_node = parent_node.node_at (last_index); + parent_node.set_edge_at (edge_index, last_byte, last_node); + + // Move the chunk of pointers one byte to the left, effectively + // deleting the last byte in the region of first bytes by + // overwriting it. + memmove (parent_node.node_pointers () - 1, parent_node.node_pointers (), + parent_node.edgecount () * sizeof (void *)); + + // Shrink the parent node to the new size, which "deletes" the + // last pointer in the chunk of node pointers. + parent_node.resize (parent_node.prefix_length (), + parent_node.edgecount () - 1); + + // Nothing points to this node now, so we can reclaim it. + free (current_node._data); + + if (parent_node.prefix_length () == 0) + _root._data = parent_node._data; + else + grandparent_node.set_node_at (parent_edge_index, parent_node); + return true; +} + +bool zmq::radix_tree_t::check (const unsigned char *key_, size_t key_size_) +{ + if (_root.refcount () > 0) + return true; + + match_result_t match_result = match (key_, key_size_, true); + return match_result._key_bytes_matched == key_size_ + && match_result._prefix_bytes_matched + == match_result._current_node.prefix_length () + && match_result._current_node.refcount () > 0; +} + +static void +visit_keys (node_t node_, + std::vector &buffer_, + void (*func_) (unsigned char *data_, size_t size_, void *arg_), + void *arg_) +{ + const size_t prefix_length = node_.prefix_length (); + buffer_.reserve (buffer_.size () + prefix_length); + std::copy (node_.prefix (), node_.prefix () + prefix_length, + std::back_inserter (buffer_)); + + if (node_.refcount () > 0) { + zmq_assert (!buffer_.empty ()); + func_ (&buffer_[0], buffer_.size (), arg_); + } + + for (size_t i = 0, edgecount = node_.edgecount (); i < edgecount; ++i) { + visit_keys (node_.node_at (i), buffer_, func_, arg_); + } + buffer_.resize (buffer_.size () - prefix_length); +} + +void zmq::radix_tree_t::apply ( + void (*func_) (unsigned char *data_, size_t size_, void *arg_), void *arg_) +{ + if (_root.refcount () > 0) + func_ (NULL, 0, arg_); // Root node is always empty. + + std::vector buffer; + for (size_t i = 0; i < _root.edgecount (); ++i) + visit_keys (_root.node_at (i), buffer, func_, arg_); +} + +size_t zmq::radix_tree_t::size () const +{ + return _size; +} diff --git a/3rd/libzmq/src/radix_tree.hpp b/3rd/libzmq/src/radix_tree.hpp new file mode 100644 index 00000000..02e74969 --- /dev/null +++ b/3rd/libzmq/src/radix_tree.hpp @@ -0,0 +1,147 @@ +/* + Copyright (c) 2018 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 . +*/ + +#ifndef RADIX_TREE_HPP +#define RADIX_TREE_HPP + +#include + +#include "stdint.hpp" + +// Wrapper type for a node's data layout. +// +// There are 3 32-bit unsigned integers that act as a header. These +// integers represent the following values in this order: +// +// (1) The reference count of the key held by the node. This is 0 if +// the node doesn't hold a key. +// +// (2) The number of characters in the node's prefix. The prefix is a +// part of one or more keys in the tree, e.g. the prefix of each node +// in a trie consists of a single character. +// +// (3) The number of outgoing edges from this node. +// +// The rest of the layout consists of 3 chunks in this order: +// +// (1) The node's prefix as a sequence of one or more bytes. The root +// node always has an empty prefix, unlike other nodes in the tree. +// +// (2) The first byte of the prefix of each of this node's children. +// +// (3) The pointer to each child node. +// +// The link to each child is looked up using its index, e.g. the child +// with index 0 will have its first byte and node pointer at the start +// of the chunk of first bytes and node pointers respectively. +struct node_t +{ + explicit node_t (unsigned char *data_); + + bool operator== (node_t other_) const; + bool operator!= (node_t other_) const; + + uint32_t refcount (); + uint32_t prefix_length (); + uint32_t edgecount (); + unsigned char *prefix (); + unsigned char *first_bytes (); + unsigned char first_byte_at (size_t index_); + unsigned char *node_pointers (); + node_t node_at (size_t index_); + void set_refcount (uint32_t value_); + void set_prefix_length (uint32_t value_); + void set_edgecount (uint32_t value_); + void set_prefix (const unsigned char *bytes_); + void set_first_bytes (const unsigned char *bytes_); + void set_first_byte_at (size_t index_, unsigned char byte_); + void set_node_pointers (const unsigned char *pointers_); + void set_node_at (size_t index_, node_t node_); + void set_edge_at (size_t index_, unsigned char first_byte_, node_t node_); + void resize (size_t prefix_length_, size_t edgecount_); + + unsigned char *_data; +}; + +node_t make_node (size_t refcount_, size_t prefix_length_, size_t edgecount_); + +struct match_result_t +{ + match_result_t (size_t key_bytes_matched_, + size_t prefix_bytes_matched_, + size_t edge_index_, + size_t parent_edge_index_, + node_t current_, + node_t parent_, + node_t grandparent); + + size_t _key_bytes_matched; + size_t _prefix_bytes_matched; + size_t _edge_index; + size_t _parent_edge_index; + node_t _current_node; + node_t _parent_node; + node_t _grandparent_node; +}; + +namespace zmq +{ +class radix_tree_t +{ + public: + radix_tree_t (); + ~radix_tree_t (); + + // Add key to the tree. Returns true if this was a new key rather + // than a duplicate. + bool add (const unsigned char *key_, size_t key_size_); + + // Remove key from the tree. Returns true if the item is actually + // removed from the tree. + bool rm (const unsigned char *key_, size_t key_size_); + + // Check whether particular key is in the tree. + bool check (const unsigned char *key_, size_t key_size_); + + // Apply the function supplied to each key in the tree. + void apply (void (*func_) (unsigned char *data, size_t size, void *arg), + void *arg_); + + size_t size () const; + + private: + match_result_t + match (const unsigned char *key_, size_t key_size_, bool is_lookup_) const; + + node_t _root; + size_t _size; +}; +} + +#endif diff --git a/3rd/libzmq/src/random.cpp b/3rd/libzmq/src/random.cpp new file mode 100644 index 00000000..17c3537d --- /dev/null +++ b/3rd/libzmq/src/random.cpp @@ -0,0 +1,170 @@ +/* + Copyright (c) 2007-2017 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 . +*/ + +#include "precompiled.hpp" +#include + +#if !defined ZMQ_HAVE_WINDOWS +#include +#endif + +#include "random.hpp" +#include "stdint.hpp" +#include "clock.hpp" +#include "mutex.hpp" +#include "macros.hpp" + +#if defined(ZMQ_USE_TWEETNACL) +#include "tweetnacl.h" +#elif defined(ZMQ_USE_LIBSODIUM) +#include "sodium.h" +#endif + +void zmq::seed_random () +{ +#if defined ZMQ_HAVE_WINDOWS + const int pid = static_cast (GetCurrentProcessId ()); +#else + int pid = static_cast (getpid ()); +#endif + srand (static_cast (clock_t::now_us () + pid)); +} + +uint32_t zmq::generate_random () +{ + // Compensate for the fact that rand() returns signed integer. + const uint32_t low = static_cast (rand ()); + uint32_t high = static_cast (rand ()); + high <<= (sizeof (int) * 8 - 1); + return high | low; +} + +// When different threads have their own context the file descriptor +// variable is shared and is subject to race conditions in tweetnacl, +// that lead to file descriptors leaks. In long-running programs with +// ephemeral threads this is a problem as it accumulates. +// thread-local storage cannot be used to initialise the file descriptor +// as it is perfectly legal to share a context among many threads, each +// of which might call curve APIs. +// Also libsodium documentation specifically states that sodium_init +// must not be called concurrently from multiple threads, for the +// same reason. Inspecting the code also reveals that the close API is +// not thread safe. +// The context class cannot be used with static variables as the curve +// utility APIs like zmq_curve_keypair also call into the crypto +// library. +// The safest solution for all use cases therefore is to have a +// static lock to serialize calls into an initialiser and a finaliser, +// using refcounts to make sure that a thread does not close the library +// while another is still using it. To avoid the static initialization +// order fiasco, this is done using function-local statics, if the +// compiler implementation supports thread-safe initialization of those. +// Otherwise, we fall back to global statics. +// HOWEVER, this initialisation code imposes ordering constraints, which +// are not obvious to users of libzmq, and may lead to problems if atexit +// or similar methods are used for cleanup. +// In that case, a strict ordering is imposed whereas the contexts MUST +// be initialised BEFORE registering the cleanup with atexit. CZMQ is an +// example. Hence we make the choice to restrict this global transition +// mechanism ONLY to Tweenacl + *NIX (when using /dev/urandom) as it is +// the less risky option. + +// TODO if there is some other user of libsodium besides libzmq, this must +// be synchronized by the application. This should probably also be +// configurable via config.h + +// TODO this should probably be done via config.h +#if __cplusplus >= 201103L \ + || (defined(__cpp_threadsafe_static_init) \ + && __cpp_threadsafe_static_init >= 200806) \ + || (defined(_MSC_VER) && _MSC_VER >= 1900) +#define ZMQ_HAVE_THREADSAFE_STATIC_LOCAL_INIT 1 +// TODO this might probably also be set if a sufficiently recent gcc is used +// without -fno-threadsafe-statics, but this cannot be determined at +// compile-time, so it must be set via config.h +#else +#define ZMQ_HAVE_THREADSAFE_STATIC_LOCAL_INIT 0 +#endif + +#if !ZMQ_HAVE_THREADSAFE_STATIC_LOCAL_INIT \ + && (defined(ZMQ_USE_TWEETNACL) && !defined(ZMQ_HAVE_WINDOWS) \ + && !defined(ZMQ_HAVE_GETRANDOM)) +static unsigned int random_refcount = 0; +static zmq::mutex_t random_sync; +#endif + +static void manage_random (bool init_) +{ +#if defined(ZMQ_USE_TWEETNACL) && !defined(ZMQ_HAVE_WINDOWS) \ + && !defined(ZMQ_HAVE_GETRANDOM) + +#if ZMQ_HAVE_THREADSAFE_STATIC_LOCAL_INIT + static int random_refcount = 0; + static zmq::mutex_t random_sync; +#endif + + if (init_) { + zmq::scoped_lock_t locker (random_sync); + + if (random_refcount == 0) { + int rc = sodium_init (); + zmq_assert (rc != -1); + } + + ++random_refcount; + } else { + zmq::scoped_lock_t locker (random_sync); + --random_refcount; + + if (random_refcount == 0) { + randombytes_close (); + } + } + +#elif defined(ZMQ_USE_LIBSODIUM) + if (init_) { + int rc = sodium_init (); + zmq_assert (rc != -1); + } else { + randombytes_close (); + } +#else + LIBZMQ_UNUSED (init_); +#endif +} + +void zmq::random_open () +{ + manage_random (true); +} + +void zmq::random_close () +{ + manage_random (false); +} diff --git a/3rd/libzmq/src/random.hpp b/3rd/libzmq/src/random.hpp new file mode 100644 index 00000000..0b57adb9 --- /dev/null +++ b/3rd/libzmq/src/random.hpp @@ -0,0 +1,51 @@ +/* + Copyright (c) 2007-2017 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 . +*/ + +#ifndef __ZMQ_RANDOM_HPP_INCLUDED__ +#define __ZMQ_RANDOM_HPP_INCLUDED__ + +#include "stdint.hpp" + +namespace zmq +{ +// Seeds the random number generator. +void seed_random (); + +// Generates random value. +uint32_t generate_random (); + +// [De-]Initialise crypto library, if needed. +// Serialised and refcounted, so that it can be called +// from multiple threads, each with its own context, and from +// the various zmq_utils curve functions safely. +void random_open (); +void random_close (); +} + +#endif diff --git a/3rd/libzmq/src/raw_decoder.cpp b/3rd/libzmq/src/raw_decoder.cpp new file mode 100644 index 00000000..97ed7972 --- /dev/null +++ b/3rd/libzmq/src/raw_decoder.cpp @@ -0,0 +1,74 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "raw_decoder.hpp" +#include "err.hpp" + +zmq::raw_decoder_t::raw_decoder_t (size_t bufsize_) : _allocator (bufsize_, 1) +{ + const int rc = _in_progress.init (); + errno_assert (rc == 0); +} + +zmq::raw_decoder_t::~raw_decoder_t () +{ + const int rc = _in_progress.close (); + errno_assert (rc == 0); +} + +void zmq::raw_decoder_t::get_buffer (unsigned char **data_, size_t *size_) +{ + *data_ = _allocator.allocate (); + *size_ = _allocator.size (); +} + +int zmq::raw_decoder_t::decode (const uint8_t *data_, + size_t size_, + size_t &bytes_used_) +{ + const int rc = + _in_progress.init (const_cast (data_), size_, + shared_message_memory_allocator::call_dec_ref, + _allocator.buffer (), _allocator.provide_content ()); + + // if the buffer serves as memory for a zero-copy message, release it + // and allocate a new buffer in get_buffer for the next decode + if (_in_progress.is_zcmsg ()) { + _allocator.advance_content (); + _allocator.release (); + } + + errno_assert (rc != -1); + bytes_used_ = size_; + return 1; +} diff --git a/3rd/libzmq/src/raw_decoder.hpp b/3rd/libzmq/src/raw_decoder.hpp new file mode 100644 index 00000000..4f6e8341 --- /dev/null +++ b/3rd/libzmq/src/raw_decoder.hpp @@ -0,0 +1,67 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_RAW_DECODER_HPP_INCLUDED__ +#define __ZMQ_RAW_DECODER_HPP_INCLUDED__ + +#include "msg.hpp" +#include "i_decoder.hpp" +#include "stdint.hpp" +#include "decoder_allocators.hpp" + +namespace zmq +{ +// Decoder for 0MQ v1 framing protocol. Converts data stream into messages. + +class raw_decoder_t ZMQ_FINAL : public i_decoder +{ + public: + raw_decoder_t (size_t bufsize_); + ~raw_decoder_t (); + + // i_decoder interface. + + void get_buffer (unsigned char **data_, size_t *size_); + + int decode (const unsigned char *data_, size_t size_, size_t &bytes_used_); + + msg_t *msg () { return &_in_progress; } + + void resize_buffer (size_t) {} + + private: + msg_t _in_progress; + + shared_message_memory_allocator _allocator; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (raw_decoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/raw_encoder.cpp b/3rd/libzmq/src/raw_encoder.cpp new file mode 100644 index 00000000..39c7633e --- /dev/null +++ b/3rd/libzmq/src/raw_encoder.cpp @@ -0,0 +1,50 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "encoder.hpp" +#include "raw_encoder.hpp" +#include "msg.hpp" + +zmq::raw_encoder_t::raw_encoder_t (size_t bufsize_) : + encoder_base_t (bufsize_) +{ + // Write 0 bytes to the batch and go to message_ready state. + next_step (NULL, 0, &raw_encoder_t::raw_message_ready, true); +} + +zmq::raw_encoder_t::~raw_encoder_t () +{ +} + +void zmq::raw_encoder_t::raw_message_ready () +{ + next_step (in_progress ()->data (), in_progress ()->size (), + &raw_encoder_t::raw_message_ready, true); +} diff --git a/3rd/libzmq/src/raw_encoder.hpp b/3rd/libzmq/src/raw_encoder.hpp new file mode 100644 index 00000000..0485e64a --- /dev/null +++ b/3rd/libzmq/src/raw_encoder.hpp @@ -0,0 +1,56 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_RAW_ENCODER_HPP_INCLUDED__ +#define __ZMQ_RAW_ENCODER_HPP_INCLUDED__ + +#include +#include +#include + +#include "encoder.hpp" + +namespace zmq +{ +// Encoder for 0MQ framing protocol. Converts messages into data batches. + +class raw_encoder_t ZMQ_FINAL : public encoder_base_t +{ + public: + raw_encoder_t (size_t bufsize_); + ~raw_encoder_t (); + + private: + void raw_message_ready (); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (raw_encoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/raw_engine.cpp b/3rd/libzmq/src/raw_engine.cpp new file mode 100644 index 00000000..5910cd99 --- /dev/null +++ b/3rd/libzmq/src/raw_engine.cpp @@ -0,0 +1,138 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" + +#include +#include + +#ifndef ZMQ_HAVE_WINDOWS +#include +#endif + +#include +#include + +#include "raw_engine.hpp" +#include "io_thread.hpp" +#include "session_base.hpp" +#include "v1_encoder.hpp" +#include "v1_decoder.hpp" +#include "v2_encoder.hpp" +#include "v2_decoder.hpp" +#include "null_mechanism.hpp" +#include "plain_client.hpp" +#include "plain_server.hpp" +#include "gssapi_client.hpp" +#include "gssapi_server.hpp" +#include "curve_client.hpp" +#include "curve_server.hpp" +#include "raw_decoder.hpp" +#include "raw_encoder.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "likely.hpp" +#include "wire.hpp" + +zmq::raw_engine_t::raw_engine_t ( + fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_) : + stream_engine_base_t (fd_, options_, endpoint_uri_pair_, false) +{ +} + +zmq::raw_engine_t::~raw_engine_t () +{ +} + +void zmq::raw_engine_t::plug_internal () +{ + // no handshaking for raw sock, instantiate raw encoder and decoders + _encoder = new (std::nothrow) raw_encoder_t (_options.out_batch_size); + alloc_assert (_encoder); + + _decoder = new (std::nothrow) raw_decoder_t (_options.in_batch_size); + alloc_assert (_decoder); + + _next_msg = &raw_engine_t::pull_msg_from_session; + _process_msg = static_cast ( + &raw_engine_t::push_raw_msg_to_session); + + properties_t properties; + if (init_properties (properties)) { + // Compile metadata. + zmq_assert (_metadata == NULL); + _metadata = new (std::nothrow) metadata_t (properties); + alloc_assert (_metadata); + } + + if (_options.raw_notify) { + // For raw sockets, send an initial 0-length message to the + // application so that it knows a peer has connected. + msg_t connector; + connector.init (); + push_raw_msg_to_session (&connector); + connector.close (); + session ()->flush (); + } + + set_pollin (); + set_pollout (); + // Flush all the data that may have been already received downstream. + in_event (); +} + +bool zmq::raw_engine_t::handshake () +{ + return true; +} + +void zmq::raw_engine_t::error (error_reason_t reason_) +{ + if (_options.raw_socket && _options.raw_notify) { + // For raw sockets, send a final 0-length message to the application + // so that it knows the peer has been disconnected. + msg_t terminator; + terminator.init (); + (this->*_process_msg) (&terminator); + terminator.close (); + } + stream_engine_base_t::error (reason_); +} + +int zmq::raw_engine_t::push_raw_msg_to_session (msg_t *msg_) +{ + if (_metadata && _metadata != msg_->metadata ()) + msg_->set_metadata (_metadata); + return push_msg_to_session (msg_); +} diff --git a/3rd/libzmq/src/raw_engine.hpp b/3rd/libzmq/src/raw_engine.hpp new file mode 100644 index 00000000..cf60bb67 --- /dev/null +++ b/3rd/libzmq/src/raw_engine.hpp @@ -0,0 +1,77 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_RAW_ENGINE_HPP_INCLUDED__ +#define __ZMQ_RAW_ENGINE_HPP_INCLUDED__ + +#include + +#include "fd.hpp" +#include "i_engine.hpp" +#include "io_object.hpp" +#include "i_encoder.hpp" +#include "i_decoder.hpp" +#include "options.hpp" +#include "socket_base.hpp" +#include "metadata.hpp" +#include "msg.hpp" +#include "stream_engine_base.hpp" + +namespace zmq +{ +// Protocol revisions + +class io_thread_t; +class session_base_t; +class mechanism_t; + +// This engine handles any socket with SOCK_STREAM semantics, +// e.g. TCP socket or an UNIX domain socket. + +class raw_engine_t ZMQ_FINAL : public stream_engine_base_t +{ + public: + raw_engine_t (fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_); + ~raw_engine_t (); + + protected: + void error (error_reason_t reason_); + void plug_internal (); + bool handshake (); + + private: + int push_raw_msg_to_session (msg_t *msg_); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (raw_engine_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/reaper.cpp b/3rd/libzmq/src/reaper.cpp new file mode 100644 index 00000000..525bf578 --- /dev/null +++ b/3rd/libzmq/src/reaper.cpp @@ -0,0 +1,149 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "reaper.hpp" +#include "socket_base.hpp" +#include "err.hpp" + +zmq::reaper_t::reaper_t (class ctx_t *ctx_, uint32_t tid_) : + object_t (ctx_, tid_), + _mailbox_handle (static_cast (NULL)), + _poller (NULL), + _sockets (0), + _terminating (false) +{ + if (!_mailbox.valid ()) + return; + + _poller = new (std::nothrow) poller_t (*ctx_); + alloc_assert (_poller); + + if (_mailbox.get_fd () != retired_fd) { + _mailbox_handle = _poller->add_fd (_mailbox.get_fd (), this); + _poller->set_pollin (_mailbox_handle); + } + +#ifdef HAVE_FORK + _pid = getpid (); +#endif +} + +zmq::reaper_t::~reaper_t () +{ + LIBZMQ_DELETE (_poller); +} + +zmq::mailbox_t *zmq::reaper_t::get_mailbox () +{ + return &_mailbox; +} + +void zmq::reaper_t::start () +{ + zmq_assert (_mailbox.valid ()); + + // Start the thread. + _poller->start ("Reaper"); +} + +void zmq::reaper_t::stop () +{ + if (get_mailbox ()->valid ()) { + send_stop (); + } +} + +void zmq::reaper_t::in_event () +{ + while (true) { +#ifdef HAVE_FORK + if (unlikely (_pid != getpid ())) { + //printf("zmq::reaper_t::in_event return in child process %d\n", (int)getpid()); + return; + } +#endif + + // Get the next command. If there is none, exit. + command_t cmd; + const int rc = _mailbox.recv (&cmd, 0); + if (rc != 0 && errno == EINTR) + continue; + if (rc != 0 && errno == EAGAIN) + break; + errno_assert (rc == 0); + + // Process the command. + cmd.destination->process_command (cmd); + } +} + +void zmq::reaper_t::out_event () +{ + zmq_assert (false); +} + +void zmq::reaper_t::timer_event (int) +{ + zmq_assert (false); +} + +void zmq::reaper_t::process_stop () +{ + _terminating = true; + + // If there are no sockets being reaped finish immediately. + if (!_sockets) { + send_done (); + _poller->rm_fd (_mailbox_handle); + _poller->stop (); + } +} + +void zmq::reaper_t::process_reap (socket_base_t *socket_) +{ + // Add the socket to the poller. + socket_->start_reaping (_poller); + + ++_sockets; +} + +void zmq::reaper_t::process_reaped () +{ + --_sockets; + + // If reaped was already asked to terminate and there are no more sockets, + // finish immediately. + if (!_sockets && _terminating) { + send_done (); + _poller->rm_fd (_mailbox_handle); + _poller->stop (); + } +} diff --git a/3rd/libzmq/src/reaper.hpp b/3rd/libzmq/src/reaper.hpp new file mode 100644 index 00000000..7b2d51cc --- /dev/null +++ b/3rd/libzmq/src/reaper.hpp @@ -0,0 +1,89 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_REAPER_HPP_INCLUDED__ +#define __ZMQ_REAPER_HPP_INCLUDED__ + +#include "object.hpp" +#include "mailbox.hpp" +#include "poller.hpp" +#include "i_poll_events.hpp" + +namespace zmq +{ +class ctx_t; +class socket_base_t; + +class reaper_t ZMQ_FINAL : public object_t, public i_poll_events +{ + public: + reaper_t (zmq::ctx_t *ctx_, uint32_t tid_); + ~reaper_t (); + + mailbox_t *get_mailbox (); + + void start (); + void stop (); + + // i_poll_events implementation. + void in_event (); + void out_event (); + void timer_event (int id_); + + private: + // Command handlers. + void process_stop (); + void process_reap (zmq::socket_base_t *socket_); + void process_reaped (); + + // Reaper thread accesses incoming commands via this mailbox. + mailbox_t _mailbox; + + // Handle associated with mailbox' file descriptor. + poller_t::handle_t _mailbox_handle; + + // I/O multiplexing is performed using a poller object. + poller_t *_poller; + + // Number of sockets being reaped at the moment. + int _sockets; + + // If true, we were already asked to terminate. + bool _terminating; + +#ifdef HAVE_FORK + // the process that created this context. Used to detect forking. + pid_t _pid; +#endif + + ZMQ_NON_COPYABLE_NOR_MOVABLE (reaper_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/rep.cpp b/3rd/libzmq/src/rep.cpp new file mode 100644 index 00000000..99707f89 --- /dev/null +++ b/3rd/libzmq/src/rep.cpp @@ -0,0 +1,133 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "rep.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::rep_t::rep_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + router_t (parent_, tid_, sid_), + _sending_reply (false), + _request_begins (true) +{ + options.type = ZMQ_REP; +} + +zmq::rep_t::~rep_t () +{ +} + +int zmq::rep_t::xsend (msg_t *msg_) +{ + // If we are in the middle of receiving a request, we cannot send reply. + if (!_sending_reply) { + errno = EFSM; + return -1; + } + + const bool more = (msg_->flags () & msg_t::more) != 0; + + // Push message to the reply pipe. + const int rc = router_t::xsend (msg_); + if (rc != 0) + return rc; + + // If the reply is complete flip the FSM back to request receiving state. + if (!more) + _sending_reply = false; + + return 0; +} + +int zmq::rep_t::xrecv (msg_t *msg_) +{ + // If we are in middle of sending a reply, we cannot receive next request. + if (_sending_reply) { + errno = EFSM; + return -1; + } + + // First thing to do when receiving a request is to copy all the labels + // to the reply pipe. + if (_request_begins) { + while (true) { + int rc = router_t::xrecv (msg_); + if (rc != 0) + return rc; + + if ((msg_->flags () & msg_t::more)) { + // Empty message part delimits the traceback stack. + const bool bottom = (msg_->size () == 0); + + // Push it to the reply pipe. + rc = router_t::xsend (msg_); + errno_assert (rc == 0); + + if (bottom) + break; + } else { + // If the traceback stack is malformed, discard anything + // already sent to pipe (we're at end of invalid message). + rc = router_t::rollback (); + errno_assert (rc == 0); + } + } + _request_begins = false; + } + + // Get next message part to return to the user. + const int rc = router_t::xrecv (msg_); + if (rc != 0) + return rc; + + // If whole request is read, flip the FSM to reply-sending state. + if (!(msg_->flags () & msg_t::more)) { + _sending_reply = true; + _request_begins = true; + } + + return 0; +} + +bool zmq::rep_t::xhas_in () +{ + if (_sending_reply) + return false; + + return router_t::xhas_in (); +} + +bool zmq::rep_t::xhas_out () +{ + if (!_sending_reply) + return false; + + return router_t::xhas_out (); +} diff --git a/3rd/libzmq/src/rep.hpp b/3rd/libzmq/src/rep.hpp new file mode 100644 index 00000000..995fe40c --- /dev/null +++ b/3rd/libzmq/src/rep.hpp @@ -0,0 +1,67 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_REP_HPP_INCLUDED__ +#define __ZMQ_REP_HPP_INCLUDED__ + +#include "router.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class io_thread_t; +class socket_base_t; + +class rep_t ZMQ_FINAL : public router_t +{ + public: + rep_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~rep_t (); + + // Overrides of functions from socket_base_t. + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + + private: + // If true, we are in process of sending the reply. If false we are + // in process of receiving a request. + bool _sending_reply; + + // If true, we are starting to receive a request. The beginning + // of the request is the backtrace stack. + bool _request_begins; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (rep_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/req.cpp b/3rd/libzmq/src/req.cpp new file mode 100644 index 00000000..b9bb35b0 --- /dev/null +++ b/3rd/libzmq/src/req.cpp @@ -0,0 +1,319 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "req.hpp" +#include "err.hpp" +#include "msg.hpp" +#include "wire.hpp" +#include "random.hpp" +#include "likely.hpp" + +zmq::req_t::req_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + dealer_t (parent_, tid_, sid_), + _receiving_reply (false), + _message_begins (true), + _reply_pipe (NULL), + _request_id_frames_enabled (false), + _request_id (generate_random ()), + _strict (true) +{ + options.type = ZMQ_REQ; +} + +zmq::req_t::~req_t () +{ +} + +int zmq::req_t::xsend (msg_t *msg_) +{ + // If we've sent a request and we still haven't got the reply, + // we can't send another request unless the strict option is disabled. + if (_receiving_reply) { + if (_strict) { + errno = EFSM; + return -1; + } + + _receiving_reply = false; + _message_begins = true; + } + + // First part of the request is the request routing id. + if (_message_begins) { + _reply_pipe = NULL; + + if (_request_id_frames_enabled) { + _request_id++; + + msg_t id; + int rc = id.init_size (sizeof (uint32_t)); + memcpy (id.data (), &_request_id, sizeof (uint32_t)); + errno_assert (rc == 0); + id.set_flags (msg_t::more); + + rc = dealer_t::sendpipe (&id, &_reply_pipe); + if (rc != 0) { + return -1; + } + } + + msg_t bottom; + int rc = bottom.init (); + errno_assert (rc == 0); + bottom.set_flags (msg_t::more); + + rc = dealer_t::sendpipe (&bottom, &_reply_pipe); + if (rc != 0) + return -1; + zmq_assert (_reply_pipe); + + _message_begins = false; + + // Eat all currently available messages before the request is fully + // sent. This is done to avoid: + // REQ sends request to A, A replies, B replies too. + // A's reply was first and matches, that is used. + // An hour later REQ sends a request to B. B's old reply is used. + msg_t drop; + while (true) { + rc = drop.init (); + errno_assert (rc == 0); + rc = dealer_t::xrecv (&drop); + if (rc != 0) + break; + drop.close (); + } + } + + bool more = (msg_->flags () & msg_t::more) != 0; + + int rc = dealer_t::xsend (msg_); + if (rc != 0) + return rc; + + // If the request was fully sent, flip the FSM into reply-receiving state. + if (!more) { + _receiving_reply = true; + _message_begins = true; + } + + return 0; +} + +int zmq::req_t::xrecv (msg_t *msg_) +{ + // If request wasn't send, we can't wait for reply. + if (!_receiving_reply) { + errno = EFSM; + return -1; + } + + // Skip messages until one with the right first frames is found. + while (_message_begins) { + // If enabled, the first frame must have the correct request_id. + if (_request_id_frames_enabled) { + int rc = recv_reply_pipe (msg_); + if (rc != 0) + return rc; + + if (unlikely (!(msg_->flags () & msg_t::more) + || msg_->size () != sizeof (_request_id) + || *static_cast (msg_->data ()) + != _request_id)) { + // Skip the remaining frames and try the next message + while (msg_->flags () & msg_t::more) { + rc = recv_reply_pipe (msg_); + errno_assert (rc == 0); + } + continue; + } + } + + // The next frame must be 0. + // TODO: Failing this check should also close the connection with the peer! + int rc = recv_reply_pipe (msg_); + if (rc != 0) + return rc; + + if (unlikely (!(msg_->flags () & msg_t::more) || msg_->size () != 0)) { + // Skip the remaining frames and try the next message + while (msg_->flags () & msg_t::more) { + rc = recv_reply_pipe (msg_); + errno_assert (rc == 0); + } + continue; + } + + _message_begins = false; + } + + const int rc = recv_reply_pipe (msg_); + if (rc != 0) + return rc; + + // If the reply is fully received, flip the FSM into request-sending state. + if (!(msg_->flags () & msg_t::more)) { + _receiving_reply = false; + _message_begins = true; + } + + return 0; +} + +bool zmq::req_t::xhas_in () +{ + // TODO: Duplicates should be removed here. + + if (!_receiving_reply) + return false; + + return dealer_t::xhas_in (); +} + +bool zmq::req_t::xhas_out () +{ + if (_receiving_reply && _strict) + return false; + + return dealer_t::xhas_out (); +} + +int zmq::req_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + const bool is_int = (optvallen_ == sizeof (int)); + int value = 0; + if (is_int) + memcpy (&value, optval_, sizeof (int)); + + switch (option_) { + case ZMQ_REQ_CORRELATE: + if (is_int && value >= 0) { + _request_id_frames_enabled = (value != 0); + return 0; + } + break; + + case ZMQ_REQ_RELAXED: + if (is_int && value >= 0) { + _strict = (value == 0); + return 0; + } + break; + + default: + break; + } + + return dealer_t::xsetsockopt (option_, optval_, optvallen_); +} + +void zmq::req_t::xpipe_terminated (pipe_t *pipe_) +{ + if (_reply_pipe == pipe_) + _reply_pipe = NULL; + dealer_t::xpipe_terminated (pipe_); +} + +int zmq::req_t::recv_reply_pipe (msg_t *msg_) +{ + while (true) { + pipe_t *pipe = NULL; + const int rc = dealer_t::recvpipe (msg_, &pipe); + if (rc != 0) + return rc; + if (!_reply_pipe || pipe == _reply_pipe) + return 0; + } +} + +zmq::req_session_t::req_session_t (io_thread_t *io_thread_, + bool connect_, + socket_base_t *socket_, + const options_t &options_, + address_t *addr_) : + session_base_t (io_thread_, connect_, socket_, options_, addr_), + _state (bottom) +{ +} + +zmq::req_session_t::~req_session_t () +{ +} + +int zmq::req_session_t::push_msg (msg_t *msg_) +{ + // Ignore commands, they are processed by the engine and should not + // affect the state machine. + if (unlikely (msg_->flags () & msg_t::command)) + return 0; + + switch (_state) { + case bottom: + if (msg_->flags () == msg_t::more) { + // In case option ZMQ_CORRELATE is on, allow request_id to be + // transfered as first frame (would be too cumbersome to check + // whether the option is actually on or not). + if (msg_->size () == sizeof (uint32_t)) { + _state = request_id; + return session_base_t::push_msg (msg_); + } + if (msg_->size () == 0) { + _state = body; + return session_base_t::push_msg (msg_); + } + } + break; + case request_id: + if (msg_->flags () == msg_t::more && msg_->size () == 0) { + _state = body; + return session_base_t::push_msg (msg_); + } + break; + case body: + if (msg_->flags () == msg_t::more) + return session_base_t::push_msg (msg_); + if (msg_->flags () == 0) { + _state = bottom; + return session_base_t::push_msg (msg_); + } + break; + } + errno = EFAULT; + return -1; +} + +void zmq::req_session_t::reset () +{ + session_base_t::reset (); + _state = bottom; +} diff --git a/3rd/libzmq/src/req.hpp b/3rd/libzmq/src/req.hpp new file mode 100644 index 00000000..0b0084cf --- /dev/null +++ b/3rd/libzmq/src/req.hpp @@ -0,0 +1,115 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_REQ_HPP_INCLUDED__ +#define __ZMQ_REQ_HPP_INCLUDED__ + +#include "dealer.hpp" +#include "stdint.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class io_thread_t; +class socket_base_t; + +class req_t ZMQ_FINAL : public dealer_t +{ + public: + req_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~req_t (); + + // Overrides of functions from socket_base_t. + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + protected: + // Receive only from the pipe the request was sent to, discarding + // frames from other pipes. + int recv_reply_pipe (zmq::msg_t *msg_); + + private: + // If true, request was already sent and reply wasn't received yet or + // was received partially. + bool _receiving_reply; + + // If true, we are starting to send/recv a message. The first part + // of the message must be empty message part (backtrace stack bottom). + bool _message_begins; + + // The pipe the request was sent to and where the reply is expected. + zmq::pipe_t *_reply_pipe; + + // Whether request id frames shall be sent and expected. + bool _request_id_frames_enabled; + + // The current request id. It is incremented every time before a new + // request is sent. + uint32_t _request_id; + + // If false, send() will reset its internal state and terminate the + // reply_pipe's connection instead of failing if a previous request is + // still pending. + bool _strict; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (req_t) +}; + +class req_session_t ZMQ_FINAL : public session_base_t +{ + public: + req_session_t (zmq::io_thread_t *io_thread_, + bool connect_, + zmq::socket_base_t *socket_, + const options_t &options_, + address_t *addr_); + ~req_session_t (); + + // Overrides of the functions from session_base_t. + int push_msg (msg_t *msg_); + void reset (); + + private: + enum + { + bottom, + request_id, + body + } _state; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (req_session_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/router.cpp b/3rd/libzmq/src/router.cpp new file mode 100644 index 00000000..77526e4e --- /dev/null +++ b/3rd/libzmq/src/router.cpp @@ -0,0 +1,526 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "router.hpp" +#include "pipe.hpp" +#include "wire.hpp" +#include "random.hpp" +#include "likely.hpp" +#include "err.hpp" + +zmq::router_t::router_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + routing_socket_base_t (parent_, tid_, sid_), + _prefetched (false), + _routing_id_sent (false), + _current_in (NULL), + _terminate_current_in (false), + _more_in (false), + _current_out (NULL), + _more_out (false), + _next_integral_routing_id (generate_random ()), + _mandatory (false), + // raw_socket functionality in ROUTER is deprecated + _raw_socket (false), + _probe_router (false), + _handover (false) +{ + options.type = ZMQ_ROUTER; + options.recv_routing_id = true; + options.raw_socket = false; + options.can_send_hello_msg = true; + options.can_recv_disconnect_msg = true; + + _prefetched_id.init (); + _prefetched_msg.init (); +} + +zmq::router_t::~router_t () +{ + zmq_assert (_anonymous_pipes.empty ()); + _prefetched_id.close (); + _prefetched_msg.close (); +} + +void zmq::router_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + + zmq_assert (pipe_); + + if (_probe_router) { + msg_t probe_msg; + int rc = probe_msg.init (); + errno_assert (rc == 0); + + rc = pipe_->write (&probe_msg); + // zmq_assert (rc) is not applicable here, since it is not a bug. + LIBZMQ_UNUSED (rc); + + pipe_->flush (); + + rc = probe_msg.close (); + errno_assert (rc == 0); + } + + const bool routing_id_ok = identify_peer (pipe_, locally_initiated_); + if (routing_id_ok) + _fq.attach (pipe_); + else + _anonymous_pipes.insert (pipe_); +} + +int zmq::router_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + const bool is_int = (optvallen_ == sizeof (int)); + int value = 0; + if (is_int) + memcpy (&value, optval_, sizeof (int)); + + switch (option_) { + case ZMQ_ROUTER_RAW: + if (is_int && value >= 0) { + _raw_socket = (value != 0); + if (_raw_socket) { + options.recv_routing_id = false; + options.raw_socket = true; + } + return 0; + } + break; + + case ZMQ_ROUTER_MANDATORY: + if (is_int && value >= 0) { + _mandatory = (value != 0); + return 0; + } + break; + + case ZMQ_PROBE_ROUTER: + if (is_int && value >= 0) { + _probe_router = (value != 0); + return 0; + } + break; + + case ZMQ_ROUTER_HANDOVER: + if (is_int && value >= 0) { + _handover = (value != 0); + return 0; + } + break; + +#ifdef ZMQ_BUILD_DRAFT_API + case ZMQ_ROUTER_NOTIFY: + if (is_int && value >= 0 + && value <= (ZMQ_NOTIFY_CONNECT | ZMQ_NOTIFY_DISCONNECT)) { + options.router_notify = value; + return 0; + } + break; +#endif + + default: + return routing_socket_base_t::xsetsockopt (option_, optval_, + optvallen_); + } + errno = EINVAL; + return -1; +} + + +void zmq::router_t::xpipe_terminated (pipe_t *pipe_) +{ + if (0 == _anonymous_pipes.erase (pipe_)) { + erase_out_pipe (pipe_); + _fq.pipe_terminated (pipe_); + pipe_->rollback (); + if (pipe_ == _current_out) + _current_out = NULL; + } +} + +void zmq::router_t::xread_activated (pipe_t *pipe_) +{ + const std::set::iterator it = _anonymous_pipes.find (pipe_); + if (it == _anonymous_pipes.end ()) + _fq.activated (pipe_); + else { + const bool routing_id_ok = identify_peer (pipe_, false); + if (routing_id_ok) { + _anonymous_pipes.erase (it); + _fq.attach (pipe_); + } + } +} + +int zmq::router_t::xsend (msg_t *msg_) +{ + // If this is the first part of the message it's the ID of the + // peer to send the message to. + if (!_more_out) { + zmq_assert (!_current_out); + + // If we have malformed message (prefix with no subsequent message) + // then just silently ignore it. + // TODO: The connections should be killed instead. + if (msg_->flags () & msg_t::more) { + _more_out = true; + + // Find the pipe associated with the routing id stored in the prefix. + // If there's no such pipe just silently ignore the message, unless + // router_mandatory is set. + out_pipe_t *out_pipe = lookup_out_pipe ( + blob_t (static_cast (msg_->data ()), + msg_->size (), zmq::reference_tag_t ())); + + if (out_pipe) { + _current_out = out_pipe->pipe; + + // Check whether pipe is closed or not + if (!_current_out->check_write ()) { + // Check whether pipe is full or not + const bool pipe_full = !_current_out->check_hwm (); + out_pipe->active = false; + _current_out = NULL; + + if (_mandatory) { + _more_out = false; + if (pipe_full) + errno = EAGAIN; + else + errno = EHOSTUNREACH; + return -1; + } + } + } else if (_mandatory) { + _more_out = false; + errno = EHOSTUNREACH; + return -1; + } + } + + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + return 0; + } + + // Ignore the MORE flag for raw-sock or assert? + if (options.raw_socket) + msg_->reset_flags (msg_t::more); + + // Check whether this is the last part of the message. + _more_out = (msg_->flags () & msg_t::more) != 0; + + // Push the message into the pipe. If there's no out pipe, just drop it. + if (_current_out) { + // Close the remote connection if user has asked to do so + // by sending zero length message. + // Pending messages in the pipe will be dropped (on receiving term- ack) + if (_raw_socket && msg_->size () == 0) { + _current_out->terminate (false); + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + _current_out = NULL; + return 0; + } + + const bool ok = _current_out->write (msg_); + if (unlikely (!ok)) { + // Message failed to send - we must close it ourselves. + const int rc = msg_->close (); + errno_assert (rc == 0); + // HWM was checked before, so the pipe must be gone. Roll back + // messages that were piped, for example REP labels. + _current_out->rollback (); + _current_out = NULL; + } else { + if (!_more_out) { + _current_out->flush (); + _current_out = NULL; + } + } + } else { + const int rc = msg_->close (); + errno_assert (rc == 0); + } + + // Detach the message from the data buffer. + const int rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +int zmq::router_t::xrecv (msg_t *msg_) +{ + if (_prefetched) { + if (!_routing_id_sent) { + const int rc = msg_->move (_prefetched_id); + errno_assert (rc == 0); + _routing_id_sent = true; + } else { + const int rc = msg_->move (_prefetched_msg); + errno_assert (rc == 0); + _prefetched = false; + } + _more_in = (msg_->flags () & msg_t::more) != 0; + + if (!_more_in) { + if (_terminate_current_in) { + _current_in->terminate (true); + _terminate_current_in = false; + } + _current_in = NULL; + } + return 0; + } + + pipe_t *pipe = NULL; + int rc = _fq.recvpipe (msg_, &pipe); + + // It's possible that we receive peer's routing id. That happens + // after reconnection. The current implementation assumes that + // the peer always uses the same routing id. + while (rc == 0 && msg_->is_routing_id ()) + rc = _fq.recvpipe (msg_, &pipe); + + if (rc != 0) + return -1; + + zmq_assert (pipe != NULL); + + // If we are in the middle of reading a message, just return the next part. + if (_more_in) { + _more_in = (msg_->flags () & msg_t::more) != 0; + + if (!_more_in) { + if (_terminate_current_in) { + _current_in->terminate (true); + _terminate_current_in = false; + } + _current_in = NULL; + } + } else { + // We are at the beginning of a message. + // Keep the message part we have in the prefetch buffer + // and return the ID of the peer instead. + rc = _prefetched_msg.move (*msg_); + errno_assert (rc == 0); + _prefetched = true; + _current_in = pipe; + + const blob_t &routing_id = pipe->get_routing_id (); + rc = msg_->init_size (routing_id.size ()); + errno_assert (rc == 0); + memcpy (msg_->data (), routing_id.data (), routing_id.size ()); + msg_->set_flags (msg_t::more); + if (_prefetched_msg.metadata ()) + msg_->set_metadata (_prefetched_msg.metadata ()); + _routing_id_sent = true; + } + + return 0; +} + +int zmq::router_t::rollback () +{ + if (_current_out) { + _current_out->rollback (); + _current_out = NULL; + _more_out = false; + } + return 0; +} + +bool zmq::router_t::xhas_in () +{ + // If we are in the middle of reading the messages, there are + // definitely more parts available. + if (_more_in) + return true; + + // We may already have a message pre-fetched. + if (_prefetched) + return true; + + // Try to read the next message. + // The message, if read, is kept in the pre-fetch buffer. + pipe_t *pipe = NULL; + int rc = _fq.recvpipe (&_prefetched_msg, &pipe); + + // It's possible that we receive peer's routing id. That happens + // after reconnection. The current implementation assumes that + // the peer always uses the same routing id. + // TODO: handle the situation when the peer changes its routing id. + while (rc == 0 && _prefetched_msg.is_routing_id ()) + rc = _fq.recvpipe (&_prefetched_msg, &pipe); + + if (rc != 0) + return false; + + zmq_assert (pipe != NULL); + + const blob_t &routing_id = pipe->get_routing_id (); + rc = _prefetched_id.init_size (routing_id.size ()); + errno_assert (rc == 0); + memcpy (_prefetched_id.data (), routing_id.data (), routing_id.size ()); + _prefetched_id.set_flags (msg_t::more); + + _prefetched = true; + _routing_id_sent = false; + _current_in = pipe; + + return true; +} + +static bool check_pipe_hwm (const zmq::pipe_t &pipe_) +{ + return pipe_.check_hwm (); +} + +bool zmq::router_t::xhas_out () +{ + // In theory, ROUTER socket is always ready for writing (except when + // MANDATORY is set). Whether actual attempt to write succeeds depends + // on whitch pipe the message is going to be routed to. + + if (!_mandatory) + return true; + + return any_of_out_pipes (check_pipe_hwm); +} + +int zmq::router_t::get_peer_state (const void *routing_id_, + size_t routing_id_size_) const +{ + int res = 0; + + // TODO remove the const_cast, see comment in lookup_out_pipe + const blob_t routing_id_blob ( + static_cast (const_cast (routing_id_)), + routing_id_size_, reference_tag_t ()); + const out_pipe_t *out_pipe = lookup_out_pipe (routing_id_blob); + if (!out_pipe) { + errno = EHOSTUNREACH; + return -1; + } + + if (out_pipe->pipe->check_hwm ()) + res |= ZMQ_POLLOUT; + + /** \todo does it make any sense to check the inpipe as well? */ + + return res; +} + +bool zmq::router_t::identify_peer (pipe_t *pipe_, bool locally_initiated_) +{ + msg_t msg; + blob_t routing_id; + + if (locally_initiated_ && connect_routing_id_is_set ()) { + const std::string connect_routing_id = extract_connect_routing_id (); + routing_id.set ( + reinterpret_cast (connect_routing_id.c_str ()), + connect_routing_id.length ()); + // Not allowed to duplicate an existing rid + zmq_assert (!has_out_pipe (routing_id)); + } else if ( + options + .raw_socket) { // Always assign an integral routing id for raw-socket + unsigned char buf[5]; + buf[0] = 0; + put_uint32 (buf + 1, _next_integral_routing_id++); + routing_id.set (buf, sizeof buf); + } else if (!options.raw_socket) { + // Pick up handshake cases and also case where next integral routing id is set + msg.init (); + const bool ok = pipe_->read (&msg); + if (!ok) + return false; + + if (msg.size () == 0) { + // Fall back on the auto-generation + unsigned char buf[5]; + buf[0] = 0; + put_uint32 (buf + 1, _next_integral_routing_id++); + routing_id.set (buf, sizeof buf); + msg.close (); + } else { + routing_id.set (static_cast (msg.data ()), + msg.size ()); + msg.close (); + + // Try to remove an existing routing id entry to allow the new + // connection to take the routing id. + const out_pipe_t *const existing_outpipe = + lookup_out_pipe (routing_id); + + if (existing_outpipe) { + if (!_handover) + // Ignore peers with duplicate ID + return false; + + // We will allow the new connection to take over this + // routing id. Temporarily assign a new routing id to the + // existing pipe so we can terminate it asynchronously. + unsigned char buf[5]; + buf[0] = 0; + put_uint32 (buf + 1, _next_integral_routing_id++); + blob_t new_routing_id (buf, sizeof buf); + + pipe_t *const old_pipe = existing_outpipe->pipe; + + erase_out_pipe (old_pipe); + old_pipe->set_router_socket_routing_id (new_routing_id); + add_out_pipe (ZMQ_MOVE (new_routing_id), old_pipe); + + if (old_pipe == _current_in) + _terminate_current_in = true; + else + old_pipe->terminate (true); + } + } + } + + pipe_->set_router_socket_routing_id (routing_id); + add_out_pipe (ZMQ_MOVE (routing_id), pipe_); + + return true; +} diff --git a/3rd/libzmq/src/router.hpp b/3rd/libzmq/src/router.hpp new file mode 100644 index 00000000..f020672f --- /dev/null +++ b/3rd/libzmq/src/router.hpp @@ -0,0 +1,132 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ROUTER_HPP_INCLUDED__ +#define __ZMQ_ROUTER_HPP_INCLUDED__ + +#include + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "stdint.hpp" +#include "blob.hpp" +#include "msg.hpp" +#include "fq.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; + +// TODO: This class uses O(n) scheduling. Rewrite it to use O(1) algorithm. +class router_t : public routing_socket_base_t +{ + public: + router_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~router_t () ZMQ_OVERRIDE; + + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) ZMQ_FINAL; + int + xsetsockopt (int option_, const void *optval_, size_t optvallen_) ZMQ_FINAL; + int xsend (zmq::msg_t *msg_) ZMQ_OVERRIDE; + int xrecv (zmq::msg_t *msg_) ZMQ_OVERRIDE; + bool xhas_in () ZMQ_OVERRIDE; + bool xhas_out () ZMQ_OVERRIDE; + void xread_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void xpipe_terminated (zmq::pipe_t *pipe_) ZMQ_FINAL; + int get_peer_state (const void *routing_id_, + size_t routing_id_size_) const ZMQ_FINAL; + + protected: + // Rollback any message parts that were sent but not yet flushed. + int rollback (); + + private: + // Receive peer id and update lookup map + bool identify_peer (pipe_t *pipe_, bool locally_initiated_); + + // Fair queueing object for inbound pipes. + fq_t _fq; + + // True iff there is a message held in the pre-fetch buffer. + bool _prefetched; + + // If true, the receiver got the message part with + // the peer's identity. + bool _routing_id_sent; + + // Holds the prefetched identity. + msg_t _prefetched_id; + + // Holds the prefetched message. + msg_t _prefetched_msg; + + // The pipe we are currently reading from + zmq::pipe_t *_current_in; + + // Should current_in should be terminate after all parts received? + bool _terminate_current_in; + + // If true, more incoming message parts are expected. + bool _more_in; + + // We keep a set of pipes that have not been identified yet. + std::set _anonymous_pipes; + + // The pipe we are currently writing to. + zmq::pipe_t *_current_out; + + // If true, more outgoing message parts are expected. + bool _more_out; + + // Routing IDs are generated. It's a simple increment and wrap-over + // algorithm. This value is the next ID to use (if not used already). + uint32_t _next_integral_routing_id; + + // If true, report EAGAIN to the caller instead of silently dropping + // the message targeting an unknown peer. + bool _mandatory; + bool _raw_socket; + + // if true, send an empty message to every connected router peer + bool _probe_router; + + // If true, the router will reassign an identity upon encountering a + // name collision. The new pipe will take the identity, the old pipe + // will be terminated. + bool _handover; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (router_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/scatter.cpp b/3rd/libzmq/src/scatter.cpp new file mode 100644 index 00000000..b951b92e --- /dev/null +++ b/3rd/libzmq/src/scatter.cpp @@ -0,0 +1,86 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "scatter.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" + +zmq::scatter_t::scatter_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_, true) +{ + options.type = ZMQ_SCATTER; +} + +zmq::scatter_t::~scatter_t () +{ +} + +void zmq::scatter_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + // Don't delay pipe termination as there is no one + // to receive the delimiter. + pipe_->set_nodelay (); + + zmq_assert (pipe_); + _lb.attach (pipe_); +} + +void zmq::scatter_t::xwrite_activated (pipe_t *pipe_) +{ + _lb.activated (pipe_); +} + +void zmq::scatter_t::xpipe_terminated (pipe_t *pipe_) +{ + _lb.pipe_terminated (pipe_); +} + +int zmq::scatter_t::xsend (msg_t *msg_) +{ + // SCATTER sockets do not allow multipart data (ZMQ_SNDMORE) + if (msg_->flags () & msg_t::more) { + errno = EINVAL; + return -1; + } + + return _lb.send (msg_); +} + +bool zmq::scatter_t::xhas_out () +{ + return _lb.has_out (); +} diff --git a/3rd/libzmq/src/scatter.hpp b/3rd/libzmq/src/scatter.hpp new file mode 100644 index 00000000..5fd36a2b --- /dev/null +++ b/3rd/libzmq/src/scatter.hpp @@ -0,0 +1,68 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SCATTER_HPP_INCLUDED__ +#define __ZMQ_SCATTER_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "lb.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; +class msg_t; +class io_thread_t; + +class scatter_t ZMQ_FINAL : public socket_base_t +{ + public: + scatter_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~scatter_t (); + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + bool xhas_out (); + void xwrite_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + // Load balancer managing the outbound pipes. + lb_t _lb; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (scatter_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/secure_allocator.hpp b/3rd/libzmq/src/secure_allocator.hpp new file mode 100644 index 00000000..0d37e7ed --- /dev/null +++ b/3rd/libzmq/src/secure_allocator.hpp @@ -0,0 +1,104 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SECURE_ALLOCATOR_HPP_INCLUDED__ +#define __ZMQ_SECURE_ALLOCATOR_HPP_INCLUDED__ + +#include "platform.hpp" +#include "macros.hpp" + +#ifdef ZMQ_HAVE_CURVE + +#if defined(ZMQ_USE_LIBSODIUM) +#include "sodium.h" +#endif + +#include + +namespace zmq +{ +#if defined(ZMQ_USE_LIBSODIUM) +template struct secure_allocator_t +{ + typedef T value_type; + + secure_allocator_t () ZMQ_DEFAULT; + + template + secure_allocator_t (const secure_allocator_t &) ZMQ_NOEXCEPT + { + } + T *allocate (std::size_t n) ZMQ_NOEXCEPT + { + T *res = static_cast (sodium_allocarray (sizeof (T), n)); + alloc_assert (res); + return res; + } + void deallocate (T *p, std::size_t) ZMQ_NOEXCEPT { sodium_free (p); } + + // the following is only required with C++98 + // TODO maybe make this conditionally compiled + typedef T *pointer; + typedef const T *const_pointer; + typedef T &reference; + typedef const T &const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + template struct rebind + { + typedef secure_allocator_t other; + }; + + void construct (pointer p, const_reference val) + { + new ((void *) p) value_type (val); + } + void destroy (pointer p) { p->~value_type (); } + size_type max_size () const { return SIZE_MAX; } +}; +template +bool operator== (const secure_allocator_t &, const secure_allocator_t &) +{ + return true; +} +template +bool operator!= (const secure_allocator_t &, const secure_allocator_t &) +{ + return false; +} +#else +template struct secure_allocator_t : std::allocator +{ +}; +#endif +} + +#endif + +#endif diff --git a/3rd/libzmq/src/select.cpp b/3rd/libzmq/src/select.cpp new file mode 100644 index 00000000..159f44f2 --- /dev/null +++ b/3rd/libzmq/src/select.cpp @@ -0,0 +1,626 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "select.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_SELECT + +#if defined ZMQ_HAVE_WINDOWS +#elif defined ZMQ_HAVE_HPUX +#include +#include +#include +#elif defined ZMQ_HAVE_OPENVMS +#include +#include +#elif defined ZMQ_HAVE_VXWORKS +#include +#include +#include +#else +#include +#endif + +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +#include +#include +#include + +zmq::select_t::select_t (const zmq::thread_ctx_t &ctx_) : + worker_poller_base_t (ctx_), +#if defined ZMQ_HAVE_WINDOWS + // Fine as long as map is not cleared. + _current_family_entry_it (_family_entries.end ()) +#else + _max_fd (retired_fd) +#endif +{ +#if defined ZMQ_HAVE_WINDOWS + for (size_t i = 0; i < fd_family_cache_size; ++i) + _fd_family_cache[i] = std::make_pair (retired_fd, 0); +#endif +} + +zmq::select_t::~select_t () +{ + stop_worker (); +} + +zmq::select_t::handle_t zmq::select_t::add_fd (fd_t fd_, i_poll_events *events_) +{ + check_thread (); + zmq_assert (fd_ != retired_fd); + + fd_entry_t fd_entry; + fd_entry.fd = fd_; + fd_entry.events = events_; + +#if defined ZMQ_HAVE_WINDOWS + u_short family = get_fd_family (fd_); + wsa_assert (family != AF_UNSPEC); + family_entry_t &family_entry = _family_entries[family]; +#else + family_entry_t &family_entry = _family_entry; +#endif + family_entry.fd_entries.push_back (fd_entry); + FD_SET (fd_, &family_entry.fds_set.error); + +#if !defined ZMQ_HAVE_WINDOWS + if (fd_ > _max_fd) + _max_fd = fd_; +#endif + + adjust_load (1); + + return fd_; +} + +zmq::select_t::fd_entries_t::iterator +zmq::select_t::find_fd_entry_by_handle (fd_entries_t &fd_entries_, + handle_t handle_) +{ + fd_entries_t::iterator fd_entry_it; + for (fd_entry_it = fd_entries_.begin (); fd_entry_it != fd_entries_.end (); + ++fd_entry_it) + if (fd_entry_it->fd == handle_) + break; + + return fd_entry_it; +} + +void zmq::select_t::trigger_events (const fd_entries_t &fd_entries_, + const fds_set_t &local_fds_set_, + int event_count_) +{ + // Size is cached to avoid iteration through recently added descriptors. + for (fd_entries_t::size_type i = 0, size = fd_entries_.size (); + i < size && event_count_ > 0; ++i) { + // fd_entries_[i] may not be stored, since calls to + // in_event/out_event may reallocate the vector + + if (is_retired_fd (fd_entries_[i])) + continue; + + if (FD_ISSET (fd_entries_[i].fd, &local_fds_set_.read)) { + fd_entries_[i].events->in_event (); + --event_count_; + } + + // TODO: can the is_retired_fd be true at this point? if it + // was retired before, we would already have continued, and I + // don't see where it might have been modified + // And if rc == 0, we can break instead of continuing + if (is_retired_fd (fd_entries_[i]) || event_count_ == 0) + continue; + + if (FD_ISSET (fd_entries_[i].fd, &local_fds_set_.write)) { + fd_entries_[i].events->out_event (); + --event_count_; + } + + // TODO: same as above + if (is_retired_fd (fd_entries_[i]) || event_count_ == 0) + continue; + + if (FD_ISSET (fd_entries_[i].fd, &local_fds_set_.error)) { + fd_entries_[i].events->in_event (); + --event_count_; + } + } +} + +#if defined ZMQ_HAVE_WINDOWS +int zmq::select_t::try_retire_fd_entry ( + family_entries_t::iterator family_entry_it_, zmq::fd_t &handle_) +{ + family_entry_t &family_entry = family_entry_it_->second; + + fd_entries_t::iterator fd_entry_it = + find_fd_entry_by_handle (family_entry.fd_entries, handle_); + + if (fd_entry_it == family_entry.fd_entries.end ()) + return 0; + + fd_entry_t &fd_entry = *fd_entry_it; + zmq_assert (fd_entry.fd != retired_fd); + + if (family_entry_it_ != _current_family_entry_it) { + // Family is not currently being iterated and can be safely + // modified in-place. So later it can be skipped without + // re-verifying its content. + family_entry.fd_entries.erase (fd_entry_it); + } else { + // Otherwise mark removed entries as retired. It will be cleaned up + // at the end of the iteration. See zmq::select_t::loop + fd_entry.fd = retired_fd; + family_entry.has_retired = true; + } + family_entry.fds_set.remove_fd (handle_); + return 1; +} +#endif + +void zmq::select_t::rm_fd (handle_t handle_) +{ + check_thread (); + int retired = 0; +#if defined ZMQ_HAVE_WINDOWS + u_short family = get_fd_family (handle_); + if (family != AF_UNSPEC) { + family_entries_t::iterator family_entry_it = + _family_entries.find (family); + + retired += try_retire_fd_entry (family_entry_it, handle_); + } else { + // get_fd_family may fail and return AF_UNSPEC if the socket was not + // successfully connected. In that case, we need to look for the + // socket in all family_entries. + family_entries_t::iterator end = _family_entries.end (); + for (family_entries_t::iterator family_entry_it = + _family_entries.begin (); + family_entry_it != end; ++family_entry_it) { + if (retired += try_retire_fd_entry (family_entry_it, handle_)) { + break; + } + } + } +#else + fd_entries_t::iterator fd_entry_it = + find_fd_entry_by_handle (_family_entry.fd_entries, handle_); + assert (fd_entry_it != _family_entry.fd_entries.end ()); + + zmq_assert (fd_entry_it->fd != retired_fd); + fd_entry_it->fd = retired_fd; + _family_entry.fds_set.remove_fd (handle_); + + ++retired; + + if (handle_ == _max_fd) { + _max_fd = retired_fd; + for (fd_entry_it = _family_entry.fd_entries.begin (); + fd_entry_it != _family_entry.fd_entries.end (); ++fd_entry_it) + if (fd_entry_it->fd > _max_fd) + _max_fd = fd_entry_it->fd; + } + + _family_entry.has_retired = true; +#endif + zmq_assert (retired == 1); + adjust_load (-1); +} + +void zmq::select_t::set_pollin (handle_t handle_) +{ + check_thread (); +#if defined ZMQ_HAVE_WINDOWS + u_short family = get_fd_family (handle_); + wsa_assert (family != AF_UNSPEC); + family_entry_t &family_entry = _family_entries[family]; +#else + family_entry_t &family_entry = _family_entry; +#endif + FD_SET (handle_, &family_entry.fds_set.read); +} + +void zmq::select_t::reset_pollin (handle_t handle_) +{ + check_thread (); +#if defined ZMQ_HAVE_WINDOWS + u_short family = get_fd_family (handle_); + wsa_assert (family != AF_UNSPEC); + family_entry_t &family_entry = _family_entries[family]; +#else + family_entry_t &family_entry = _family_entry; +#endif + FD_CLR (handle_, &family_entry.fds_set.read); +} + +void zmq::select_t::set_pollout (handle_t handle_) +{ + check_thread (); +#if defined ZMQ_HAVE_WINDOWS + u_short family = get_fd_family (handle_); + wsa_assert (family != AF_UNSPEC); + family_entry_t &family_entry = _family_entries[family]; +#else + family_entry_t &family_entry = _family_entry; +#endif + FD_SET (handle_, &family_entry.fds_set.write); +} + +void zmq::select_t::reset_pollout (handle_t handle_) +{ + check_thread (); +#if defined ZMQ_HAVE_WINDOWS + u_short family = get_fd_family (handle_); + wsa_assert (family != AF_UNSPEC); + family_entry_t &family_entry = _family_entries[family]; +#else + family_entry_t &family_entry = _family_entry; +#endif + FD_CLR (handle_, &family_entry.fds_set.write); +} + +void zmq::select_t::stop () +{ + check_thread (); + // no-op... thread is stopped when no more fds or timers are registered +} + +int zmq::select_t::max_fds () +{ + return FD_SETSIZE; +} + +void zmq::select_t::loop () +{ + while (true) { + // Execute any due timers. + int timeout = static_cast (execute_timers ()); + + cleanup_retired (); + +#ifdef _WIN32 + if (_family_entries.empty ()) { +#else + if (_family_entry.fd_entries.empty ()) { +#endif + zmq_assert (get_load () == 0); + + if (timeout == 0) + break; + + // TODO sleep for timeout + continue; + } + +#if defined ZMQ_HAVE_OSX + struct timeval tv = {(long) (timeout / 1000), timeout % 1000 * 1000}; +#else + struct timeval tv = {static_cast (timeout / 1000), + static_cast (timeout % 1000 * 1000)}; +#endif + +#if defined ZMQ_HAVE_WINDOWS + /* + On Windows select does not allow to mix descriptors from different + service providers. It seems to work for AF_INET and AF_INET6, + but fails for AF_INET and VMCI. The workaround is to use + WSAEventSelect and WSAWaitForMultipleEvents to wait, then use + select to find out what actually changed. WSAWaitForMultipleEvents + cannot be used alone, because it does not support more than 64 events + which is not enough. + + To reduce unnecessary overhead, WSA is only used when there are more + than one family. Moreover, AF_INET and AF_INET6 are considered the same + family because Windows seems to handle them properly. + See get_fd_family for details. + */ + + // If there is just one family, there is no reason to use WSA events. + int rc = 0; + const bool use_wsa_events = _family_entries.size () > 1; + if (use_wsa_events) { + // TODO: I don't really understand why we are doing this. If any of + // the events was signaled, we will call select for each fd_family + // afterwards. The only benefit is if none of the events was + // signaled, then we continue early. + // IMHO, either WSAEventSelect/WSAWaitForMultipleEvents or select + // should be used, but not both + + wsa_events_t wsa_events; + + for (family_entries_t::iterator family_entry_it = + _family_entries.begin (); + family_entry_it != _family_entries.end (); ++family_entry_it) { + family_entry_t &family_entry = family_entry_it->second; + + for (fd_entries_t::iterator fd_entry_it = + family_entry.fd_entries.begin (); + fd_entry_it != family_entry.fd_entries.end (); + ++fd_entry_it) { + fd_t fd = fd_entry_it->fd; + + // http://stackoverflow.com/q/35043420/188530 + if (FD_ISSET (fd, &family_entry.fds_set.read) + && FD_ISSET (fd, &family_entry.fds_set.write)) + rc = WSAEventSelect (fd, wsa_events.events[3], + FD_READ | FD_ACCEPT | FD_CLOSE + | FD_WRITE | FD_CONNECT); + else if (FD_ISSET (fd, &family_entry.fds_set.read)) + rc = WSAEventSelect (fd, wsa_events.events[0], + FD_READ | FD_ACCEPT | FD_CLOSE); + else if (FD_ISSET (fd, &family_entry.fds_set.write)) + rc = WSAEventSelect (fd, wsa_events.events[1], + FD_WRITE | FD_CONNECT); + else + rc = 0; + + wsa_assert (rc != SOCKET_ERROR); + } + } + + rc = WSAWaitForMultipleEvents (4, wsa_events.events, FALSE, + timeout ? timeout : INFINITE, FALSE); + wsa_assert (rc != (int) WSA_WAIT_FAILED); + zmq_assert (rc != WSA_WAIT_IO_COMPLETION); + + if (rc == WSA_WAIT_TIMEOUT) + continue; + } + + for (_current_family_entry_it = _family_entries.begin (); + _current_family_entry_it != _family_entries.end (); + ++_current_family_entry_it) { + family_entry_t &family_entry = _current_family_entry_it->second; + + + if (use_wsa_events) { + // There is no reason to wait again after WSAWaitForMultipleEvents. + // Simply collect what is ready. + struct timeval tv_nodelay = {0, 0}; + select_family_entry (family_entry, 0, true, tv_nodelay); + } else { + select_family_entry (family_entry, 0, timeout > 0, tv); + } + } +#else + select_family_entry (_family_entry, _max_fd + 1, timeout > 0, tv); +#endif + } +} + +void zmq::select_t::select_family_entry (family_entry_t &family_entry_, + const int max_fd_, + const bool use_timeout_, + struct timeval &tv_) +{ + // select will fail when run with empty sets. + fd_entries_t &fd_entries = family_entry_.fd_entries; + if (fd_entries.empty ()) + return; + + fds_set_t local_fds_set = family_entry_.fds_set; + int rc = select (max_fd_, &local_fds_set.read, &local_fds_set.write, + &local_fds_set.error, use_timeout_ ? &tv_ : NULL); + +#if defined ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + if (rc == -1) { + errno_assert (errno == EINTR); + return; + } +#endif + + trigger_events (fd_entries, local_fds_set, rc); + + cleanup_retired (family_entry_); +} + +zmq::select_t::fds_set_t::fds_set_t () +{ + FD_ZERO (&read); + FD_ZERO (&write); + FD_ZERO (&error); +} + +zmq::select_t::fds_set_t::fds_set_t (const fds_set_t &other_) +{ +#if defined ZMQ_HAVE_WINDOWS + // On Windows we don't need to copy the whole fd_set. + // SOCKETS are continuous from the beginning of fd_array in fd_set. + // We just need to copy fd_count elements of fd_array. + // We gain huge memcpy() improvement if number of used SOCKETs is much lower than FD_SETSIZE. + memcpy (&read, &other_.read, + (char *) (other_.read.fd_array + other_.read.fd_count) + - (char *) &other_.read); + memcpy (&write, &other_.write, + (char *) (other_.write.fd_array + other_.write.fd_count) + - (char *) &other_.write); + memcpy (&error, &other_.error, + (char *) (other_.error.fd_array + other_.error.fd_count) + - (char *) &other_.error); +#else + memcpy (&read, &other_.read, sizeof other_.read); + memcpy (&write, &other_.write, sizeof other_.write); + memcpy (&error, &other_.error, sizeof other_.error); +#endif +} + +zmq::select_t::fds_set_t &zmq::select_t::fds_set_t:: +operator= (const fds_set_t &other_) +{ +#if defined ZMQ_HAVE_WINDOWS + // On Windows we don't need to copy the whole fd_set. + // SOCKETS are continuous from the beginning of fd_array in fd_set. + // We just need to copy fd_count elements of fd_array. + // We gain huge memcpy() improvement if number of used SOCKETs is much lower than FD_SETSIZE. + memcpy (&read, &other_.read, + (char *) (other_.read.fd_array + other_.read.fd_count) + - (char *) &other_.read); + memcpy (&write, &other_.write, + (char *) (other_.write.fd_array + other_.write.fd_count) + - (char *) &other_.write); + memcpy (&error, &other_.error, + (char *) (other_.error.fd_array + other_.error.fd_count) + - (char *) &other_.error); +#else + memcpy (&read, &other_.read, sizeof other_.read); + memcpy (&write, &other_.write, sizeof other_.write); + memcpy (&error, &other_.error, sizeof other_.error); +#endif + return *this; +} + +void zmq::select_t::fds_set_t::remove_fd (const fd_t &fd_) +{ + FD_CLR (fd_, &read); + FD_CLR (fd_, &write); + FD_CLR (fd_, &error); +} + +bool zmq::select_t::cleanup_retired (family_entry_t &family_entry_) +{ + if (family_entry_.has_retired) { + family_entry_.has_retired = false; + family_entry_.fd_entries.erase ( + std::remove_if (family_entry_.fd_entries.begin (), + family_entry_.fd_entries.end (), is_retired_fd), + family_entry_.fd_entries.end ()); + } + return family_entry_.fd_entries.empty (); +} + +void zmq::select_t::cleanup_retired () +{ +#ifdef _WIN32 + for (family_entries_t::iterator it = _family_entries.begin (); + it != _family_entries.end ();) { + if (cleanup_retired (it->second)) + it = _family_entries.erase (it); + else + ++it; + } +#else + cleanup_retired (_family_entry); +#endif +} + +bool zmq::select_t::is_retired_fd (const fd_entry_t &entry_) +{ + return entry_.fd == retired_fd; +} + +zmq::select_t::family_entry_t::family_entry_t () : has_retired (false) +{ +} + + +#if defined ZMQ_HAVE_WINDOWS +u_short zmq::select_t::get_fd_family (fd_t fd_) +{ + // cache the results of determine_fd_family, as this is frequently called + // for the same sockets, and determine_fd_family is expensive + size_t i; + for (i = 0; i < fd_family_cache_size; ++i) { + const std::pair &entry = _fd_family_cache[i]; + if (entry.first == fd_) { + return entry.second; + } + if (entry.first == retired_fd) + break; + } + + std::pair res = + std::make_pair (fd_, determine_fd_family (fd_)); + if (i < fd_family_cache_size) { + _fd_family_cache[i] = res; + } else { + // just overwrite a random entry + // could be optimized by some LRU strategy + _fd_family_cache[rand () % fd_family_cache_size] = res; + } + + return res.second; +} + +u_short zmq::select_t::determine_fd_family (fd_t fd_) +{ + // Use sockaddr_storage instead of sockaddr to accommodate different structure sizes + sockaddr_storage addr = {0}; + int addr_size = sizeof addr; + + int type; + int type_length = sizeof (int); + + int rc = getsockopt (fd_, SOL_SOCKET, SO_TYPE, + reinterpret_cast (&type), &type_length); + + if (rc == 0) { + if (type == SOCK_DGRAM) + return AF_INET; + + rc = + getsockname (fd_, reinterpret_cast (&addr), &addr_size); + + // AF_INET and AF_INET6 can be mixed in select + // TODO: If proven otherwise, should simply return addr.sa_family + if (rc != SOCKET_ERROR) + return addr.ss_family == AF_INET6 ? AF_INET : addr.ss_family; + } + + return AF_UNSPEC; +} + +zmq::select_t::wsa_events_t::wsa_events_t () +{ + events[0] = WSACreateEvent (); + wsa_assert (events[0] != WSA_INVALID_EVENT); + events[1] = WSACreateEvent (); + wsa_assert (events[1] != WSA_INVALID_EVENT); + events[2] = WSACreateEvent (); + wsa_assert (events[2] != WSA_INVALID_EVENT); + events[3] = WSACreateEvent (); + wsa_assert (events[3] != WSA_INVALID_EVENT); +} + +zmq::select_t::wsa_events_t::~wsa_events_t () +{ + wsa_assert (WSACloseEvent (events[0])); + wsa_assert (WSACloseEvent (events[1])); + wsa_assert (WSACloseEvent (events[2])); + wsa_assert (WSACloseEvent (events[3])); +} +#endif + +#endif diff --git a/3rd/libzmq/src/select.hpp b/3rd/libzmq/src/select.hpp new file mode 100644 index 00000000..8f18a383 --- /dev/null +++ b/3rd/libzmq/src/select.hpp @@ -0,0 +1,171 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SELECT_HPP_INCLUDED__ +#define __ZMQ_SELECT_HPP_INCLUDED__ + +// poller.hpp decides which polling mechanism to use. +#include "poller.hpp" +#if defined ZMQ_IOTHREAD_POLLER_USE_SELECT + +#include +#include +#include + +#if defined ZMQ_HAVE_WINDOWS +#elif defined ZMQ_HAVE_OPENVMS +#include +#include +#else +#include +#endif + +#include "ctx.hpp" +#include "fd.hpp" +#include "poller_base.hpp" + +namespace zmq +{ +struct i_poll_events; + +// Implements socket polling mechanism using POSIX.1-2001 select() +// function. + +class select_t ZMQ_FINAL : public worker_poller_base_t +{ + public: + typedef fd_t handle_t; + + select_t (const thread_ctx_t &ctx_); + ~select_t () ZMQ_FINAL; + + // "poller" concept. + handle_t add_fd (fd_t fd_, zmq::i_poll_events *events_); + void rm_fd (handle_t handle_); + void set_pollin (handle_t handle_); + void reset_pollin (handle_t handle_); + void set_pollout (handle_t handle_); + void reset_pollout (handle_t handle_); + void stop (); + + static int max_fds (); + + private: + // Main event loop. + void loop () ZMQ_FINAL; + + // Internal state. + struct fds_set_t + { + fds_set_t (); + fds_set_t (const fds_set_t &other_); + fds_set_t &operator= (const fds_set_t &other_); + // Convenience method to descriptor from all sets. + void remove_fd (const fd_t &fd_); + + fd_set read; + fd_set write; + fd_set error; + }; + + struct fd_entry_t + { + fd_t fd; + zmq::i_poll_events *events; + }; + typedef std::vector fd_entries_t; + + void trigger_events (const fd_entries_t &fd_entries_, + const fds_set_t &local_fds_set_, + int event_count_); + + struct family_entry_t + { + family_entry_t (); + + fd_entries_t fd_entries; + fds_set_t fds_set; + bool has_retired; + }; + + void select_family_entry (family_entry_t &family_entry_, + int max_fd_, + bool use_timeout_, + struct timeval &tv_); + +#if defined ZMQ_HAVE_WINDOWS + typedef std::map family_entries_t; + + struct wsa_events_t + { + wsa_events_t (); + ~wsa_events_t (); + + // read, write, error and readwrite + WSAEVENT events[4]; + }; + + family_entries_t _family_entries; + // See loop for details. + family_entries_t::iterator _current_family_entry_it; + + int try_retire_fd_entry (family_entries_t::iterator family_entry_it_, + zmq::fd_t &handle_); + + static const size_t fd_family_cache_size = 8; + std::pair _fd_family_cache[fd_family_cache_size]; + + u_short get_fd_family (fd_t fd_); + + // Socket's family or AF_UNSPEC on error. + static u_short determine_fd_family (fd_t fd_); +#else + // on non-Windows, we can treat all fds as one family + family_entry_t _family_entry; + fd_t _max_fd; +#endif + + void cleanup_retired (); + bool cleanup_retired (family_entry_t &family_entry_); + + // Checks if an fd_entry_t is retired. + static bool is_retired_fd (const fd_entry_t &entry_); + + static fd_entries_t::iterator + find_fd_entry_by_handle (fd_entries_t &fd_entries_, handle_t handle_); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (select_t) +}; + +typedef select_t poller_t; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/server.cpp b/3rd/libzmq/src/server.cpp new file mode 100644 index 00000000..6f0cf269 --- /dev/null +++ b/3rd/libzmq/src/server.cpp @@ -0,0 +1,184 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "server.hpp" +#include "pipe.hpp" +#include "wire.hpp" +#include "random.hpp" +#include "likely.hpp" +#include "err.hpp" + +zmq::server_t::server_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_, true), + _next_routing_id (generate_random ()) +{ + options.type = ZMQ_SERVER; + options.can_send_hello_msg = true; + options.can_recv_disconnect_msg = true; +} + +zmq::server_t::~server_t () +{ + zmq_assert (_out_pipes.empty ()); +} + +void zmq::server_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + + uint32_t routing_id = _next_routing_id++; + if (!routing_id) + routing_id = _next_routing_id++; // Never use Routing ID zero + + pipe_->set_server_socket_routing_id (routing_id); + // Add the record into output pipes lookup table + outpipe_t outpipe = {pipe_, true}; + const bool ok = + _out_pipes.ZMQ_MAP_INSERT_OR_EMPLACE (routing_id, outpipe).second; + zmq_assert (ok); + + _fq.attach (pipe_); +} + +void zmq::server_t::xpipe_terminated (pipe_t *pipe_) +{ + const out_pipes_t::iterator it = + _out_pipes.find (pipe_->get_server_socket_routing_id ()); + zmq_assert (it != _out_pipes.end ()); + _out_pipes.erase (it); + _fq.pipe_terminated (pipe_); +} + +void zmq::server_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +void zmq::server_t::xwrite_activated (pipe_t *pipe_) +{ + const out_pipes_t::iterator end = _out_pipes.end (); + out_pipes_t::iterator it; + for (it = _out_pipes.begin (); it != end; ++it) + if (it->second.pipe == pipe_) + break; + + zmq_assert (it != _out_pipes.end ()); + zmq_assert (!it->second.active); + it->second.active = true; +} + +int zmq::server_t::xsend (msg_t *msg_) +{ + // SERVER sockets do not allow multipart data (ZMQ_SNDMORE) + if (msg_->flags () & msg_t::more) { + errno = EINVAL; + return -1; + } + // Find the pipe associated with the routing stored in the message. + const uint32_t routing_id = msg_->get_routing_id (); + out_pipes_t::iterator it = _out_pipes.find (routing_id); + + if (it != _out_pipes.end ()) { + if (!it->second.pipe->check_write ()) { + it->second.active = false; + errno = EAGAIN; + return -1; + } + } else { + errno = EHOSTUNREACH; + return -1; + } + + // Message might be delivered over inproc, so we reset routing id + int rc = msg_->reset_routing_id (); + errno_assert (rc == 0); + + const bool ok = it->second.pipe->write (msg_); + if (unlikely (!ok)) { + // Message failed to send - we must close it ourselves. + rc = msg_->close (); + errno_assert (rc == 0); + } else + it->second.pipe->flush (); + + // Detach the message from the data buffer. + rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +int zmq::server_t::xrecv (msg_t *msg_) +{ + pipe_t *pipe = NULL; + int rc = _fq.recvpipe (msg_, &pipe); + + // Drop any messages with more flag + while (rc == 0 && msg_->flags () & msg_t::more) { + // drop all frames of the current multi-frame message + rc = _fq.recvpipe (msg_, NULL); + + while (rc == 0 && msg_->flags () & msg_t::more) + rc = _fq.recvpipe (msg_, NULL); + + // get the new message + if (rc == 0) + rc = _fq.recvpipe (msg_, &pipe); + } + + if (rc != 0) + return rc; + + zmq_assert (pipe != NULL); + + const uint32_t routing_id = pipe->get_server_socket_routing_id (); + msg_->set_routing_id (routing_id); + + return 0; +} + +bool zmq::server_t::xhas_in () +{ + return _fq.has_in (); +} + +bool zmq::server_t::xhas_out () +{ + // In theory, SERVER socket is always ready for writing. Whether actual + // attempt to write succeeds depends on which pipe the message is going + // to be routed to. + return true; +} diff --git a/3rd/libzmq/src/server.hpp b/3rd/libzmq/src/server.hpp new file mode 100644 index 00000000..cef1e4d2 --- /dev/null +++ b/3rd/libzmq/src/server.hpp @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SERVER_HPP_INCLUDED__ +#define __ZMQ_SERVER_HPP_INCLUDED__ + +#include + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "stdint.hpp" +#include "blob.hpp" +#include "fq.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; + +// TODO: This class uses O(n) scheduling. Rewrite it to use O(1) algorithm. +class server_t : public socket_base_t +{ + public: + server_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~server_t (); + + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + void xread_activated (zmq::pipe_t *pipe_); + void xwrite_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + + private: + // Fair queueing object for inbound pipes. + fq_t _fq; + + struct outpipe_t + { + zmq::pipe_t *pipe; + bool active; + }; + + // Outbound pipes indexed by the peer IDs. + typedef std::map out_pipes_t; + out_pipes_t _out_pipes; + + // Routing IDs are generated. It's a simple increment and wrap-over + // algorithm. This value is the next ID to use (if not used already). + uint32_t _next_routing_id; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (server_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/session_base.cpp b/3rd/libzmq/src/session_base.cpp new file mode 100644 index 00000000..551ae318 --- /dev/null +++ b/3rd/libzmq/src/session_base.cpp @@ -0,0 +1,811 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "session_base.hpp" +#include "i_engine.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "likely.hpp" +#include "tcp_connecter.hpp" +#include "ws_connecter.hpp" +#include "ipc_connecter.hpp" +#include "tipc_connecter.hpp" +#include "socks_connecter.hpp" +#include "vmci_connecter.hpp" +#include "pgm_sender.hpp" +#include "pgm_receiver.hpp" +#include "address.hpp" +#include "norm_engine.hpp" +#include "udp_engine.hpp" + +#include "ctx.hpp" +#include "req.hpp" +#include "radio.hpp" +#include "dish.hpp" + +zmq::session_base_t *zmq::session_base_t::create (class io_thread_t *io_thread_, + bool active_, + class socket_base_t *socket_, + const options_t &options_, + address_t *addr_) +{ + session_base_t *s = NULL; + switch (options_.type) { + case ZMQ_REQ: + s = new (std::nothrow) + req_session_t (io_thread_, active_, socket_, options_, addr_); + break; + case ZMQ_RADIO: + s = new (std::nothrow) + radio_session_t (io_thread_, active_, socket_, options_, addr_); + break; + case ZMQ_DISH: + s = new (std::nothrow) + dish_session_t (io_thread_, active_, socket_, options_, addr_); + break; + case ZMQ_DEALER: + case ZMQ_REP: + case ZMQ_ROUTER: + case ZMQ_PUB: + case ZMQ_XPUB: + case ZMQ_SUB: + case ZMQ_XSUB: + case ZMQ_PUSH: + case ZMQ_PULL: + case ZMQ_PAIR: + case ZMQ_STREAM: + case ZMQ_SERVER: + case ZMQ_CLIENT: + case ZMQ_GATHER: + case ZMQ_SCATTER: + case ZMQ_DGRAM: + case ZMQ_PEER: + case ZMQ_CHANNEL: +#ifdef ZMQ_BUILD_DRAFT_API + if (options_.can_send_hello_msg && options_.hello_msg.size () > 0) + s = new (std::nothrow) hello_msg_session_t ( + io_thread_, active_, socket_, options_, addr_); + else + s = new (std::nothrow) session_base_t ( + io_thread_, active_, socket_, options_, addr_); + + break; +#else + s = new (std::nothrow) + session_base_t (io_thread_, active_, socket_, options_, addr_); + break; +#endif + + default: + errno = EINVAL; + return NULL; + } + alloc_assert (s); + return s; +} + +zmq::session_base_t::session_base_t (class io_thread_t *io_thread_, + bool active_, + class socket_base_t *socket_, + const options_t &options_, + address_t *addr_) : + own_t (io_thread_, options_), + io_object_t (io_thread_), + _active (active_), + _pipe (NULL), + _zap_pipe (NULL), + _incomplete_in (false), + _pending (false), + _engine (NULL), + _socket (socket_), + _io_thread (io_thread_), + _has_linger_timer (false), + _addr (addr_) +#ifdef ZMQ_HAVE_WSS + , + _wss_hostname (options_.wss_hostname) +#endif +{ +} + +const zmq::endpoint_uri_pair_t &zmq::session_base_t::get_endpoint () const +{ + return _engine->get_endpoint (); +} + +zmq::session_base_t::~session_base_t () +{ + zmq_assert (!_pipe); + zmq_assert (!_zap_pipe); + + // If there's still a pending linger timer, remove it. + if (_has_linger_timer) { + cancel_timer (linger_timer_id); + _has_linger_timer = false; + } + + // Close the engine. + if (_engine) + _engine->terminate (); + + LIBZMQ_DELETE (_addr); +} + +void zmq::session_base_t::attach_pipe (pipe_t *pipe_) +{ + zmq_assert (!is_terminating ()); + zmq_assert (!_pipe); + zmq_assert (pipe_); + _pipe = pipe_; + _pipe->set_event_sink (this); +} + +int zmq::session_base_t::pull_msg (msg_t *msg_) +{ + if (!_pipe || !_pipe->read (msg_)) { + errno = EAGAIN; + return -1; + } + + _incomplete_in = (msg_->flags () & msg_t::more) != 0; + + return 0; +} + +int zmq::session_base_t::push_msg (msg_t *msg_) +{ + // pass subscribe/cancel to the sockets + if ((msg_->flags () & msg_t::command) && !msg_->is_subscribe () + && !msg_->is_cancel ()) + return 0; + if (_pipe && _pipe->write (msg_)) { + const int rc = msg_->init (); + errno_assert (rc == 0); + return 0; + } + + errno = EAGAIN; + return -1; +} + +int zmq::session_base_t::read_zap_msg (msg_t *msg_) +{ + if (_zap_pipe == NULL) { + errno = ENOTCONN; + return -1; + } + + if (!_zap_pipe->read (msg_)) { + errno = EAGAIN; + return -1; + } + + return 0; +} + +int zmq::session_base_t::write_zap_msg (msg_t *msg_) +{ + if (_zap_pipe == NULL || !_zap_pipe->write (msg_)) { + errno = ENOTCONN; + return -1; + } + + if ((msg_->flags () & msg_t::more) == 0) + _zap_pipe->flush (); + + const int rc = msg_->init (); + errno_assert (rc == 0); + return 0; +} + +void zmq::session_base_t::reset () +{ +} + +void zmq::session_base_t::flush () +{ + if (_pipe) + _pipe->flush (); +} + +void zmq::session_base_t::rollback () +{ + if (_pipe) + _pipe->rollback (); +} + +void zmq::session_base_t::clean_pipes () +{ + zmq_assert (_pipe != NULL); + + // Get rid of half-processed messages in the out pipe. Flush any + // unflushed messages upstream. + _pipe->rollback (); + _pipe->flush (); + + // Remove any half-read message from the in pipe. + while (_incomplete_in) { + msg_t msg; + int rc = msg.init (); + errno_assert (rc == 0); + rc = pull_msg (&msg); + errno_assert (rc == 0); + rc = msg.close (); + errno_assert (rc == 0); + } +} + +void zmq::session_base_t::pipe_terminated (pipe_t *pipe_) +{ + // Drop the reference to the deallocated pipe if required. + zmq_assert (pipe_ == _pipe || pipe_ == _zap_pipe + || _terminating_pipes.count (pipe_) == 1); + + if (pipe_ == _pipe) { + // If this is our current pipe, remove it + _pipe = NULL; + if (_has_linger_timer) { + cancel_timer (linger_timer_id); + _has_linger_timer = false; + } + } else if (pipe_ == _zap_pipe) + _zap_pipe = NULL; + else + // Remove the pipe from the detached pipes set + _terminating_pipes.erase (pipe_); + + if (!is_terminating () && options.raw_socket) { + if (_engine) { + _engine->terminate (); + _engine = NULL; + } + terminate (); + } + + // If we are waiting for pending messages to be sent, at this point + // we are sure that there will be no more messages and we can proceed + // with termination safely. + if (_pending && !_pipe && !_zap_pipe && _terminating_pipes.empty ()) { + _pending = false; + own_t::process_term (0); + } +} + +void zmq::session_base_t::read_activated (pipe_t *pipe_) +{ + // Skip activating if we're detaching this pipe + if (unlikely (pipe_ != _pipe && pipe_ != _zap_pipe)) { + zmq_assert (_terminating_pipes.count (pipe_) == 1); + return; + } + + if (unlikely (_engine == NULL)) { + if (_pipe) + _pipe->check_read (); + return; + } + + if (likely (pipe_ == _pipe)) + _engine->restart_output (); + else { + // i.e. pipe_ == zap_pipe + _engine->zap_msg_available (); + } +} + +void zmq::session_base_t::write_activated (pipe_t *pipe_) +{ + // Skip activating if we're detaching this pipe + if (_pipe != pipe_) { + zmq_assert (_terminating_pipes.count (pipe_) == 1); + return; + } + + if (_engine) + _engine->restart_input (); +} + +void zmq::session_base_t::hiccuped (pipe_t *) +{ + // Hiccups are always sent from session to socket, not the other + // way round. + zmq_assert (false); +} + +zmq::socket_base_t *zmq::session_base_t::get_socket () const +{ + return _socket; +} + +void zmq::session_base_t::process_plug () +{ + if (_active) + start_connecting (false); +} + +// This functions can return 0 on success or -1 and errno=ECONNREFUSED if ZAP +// is not setup (IE: inproc://zeromq.zap.01 does not exist in the same context) +// or it aborts on any other error. In other words, either ZAP is not +// configured or if it is configured it MUST be configured correctly and it +// MUST work, otherwise authentication cannot be guaranteed and it would be a +// security flaw. +int zmq::session_base_t::zap_connect () +{ + if (_zap_pipe != NULL) + return 0; + + endpoint_t peer = find_endpoint ("inproc://zeromq.zap.01"); + if (peer.socket == NULL) { + errno = ECONNREFUSED; + return -1; + } + zmq_assert (peer.options.type == ZMQ_REP || peer.options.type == ZMQ_ROUTER + || peer.options.type == ZMQ_SERVER); + + // Create a bi-directional pipe that will connect + // session with zap socket. + object_t *parents[2] = {this, peer.socket}; + pipe_t *new_pipes[2] = {NULL, NULL}; + int hwms[2] = {0, 0}; + bool conflates[2] = {false, false}; + int rc = pipepair (parents, new_pipes, hwms, conflates); + errno_assert (rc == 0); + + // Attach local end of the pipe to this socket object. + _zap_pipe = new_pipes[0]; + _zap_pipe->set_nodelay (); + _zap_pipe->set_event_sink (this); + + send_bind (peer.socket, new_pipes[1], false); + + // Send empty routing id if required by the peer. + if (peer.options.recv_routing_id) { + msg_t id; + rc = id.init (); + errno_assert (rc == 0); + id.set_flags (msg_t::routing_id); + bool ok = _zap_pipe->write (&id); + zmq_assert (ok); + _zap_pipe->flush (); + } + + return 0; +} + +bool zmq::session_base_t::zap_enabled () const +{ + return (options.mechanism != ZMQ_NULL || !options.zap_domain.empty ()); +} + +void zmq::session_base_t::process_attach (i_engine *engine_) +{ + zmq_assert (engine_ != NULL); + zmq_assert (!_engine); + _engine = engine_; + + if (!engine_->has_handshake_stage ()) + engine_ready (); + + // Plug in the engine. + _engine->plug (_io_thread, this); +} + +void zmq::session_base_t::engine_ready () +{ + // Create the pipe if it does not exist yet. + if (!_pipe && !is_terminating ()) { + object_t *parents[2] = {this, _socket}; + pipe_t *pipes[2] = {NULL, NULL}; + + const bool conflate = get_effective_conflate_option (options); + + int hwms[2] = {conflate ? -1 : options.rcvhwm, + conflate ? -1 : options.sndhwm}; + bool conflates[2] = {conflate, conflate}; + const int rc = pipepair (parents, pipes, hwms, conflates); + errno_assert (rc == 0); + + // Plug the local end of the pipe. + pipes[0]->set_event_sink (this); + + // Remember the local end of the pipe. + zmq_assert (!_pipe); + _pipe = pipes[0]; + + // The endpoints strings are not set on bind, set them here so that + // events can use them. + pipes[0]->set_endpoint_pair (_engine->get_endpoint ()); + pipes[1]->set_endpoint_pair (_engine->get_endpoint ()); + + // Ask socket to plug into the remote end of the pipe. + send_bind (_socket, pipes[1]); + } +} + +void zmq::session_base_t::engine_error (bool handshaked_, + zmq::i_engine::error_reason_t reason_) +{ + // Engine is dead. Let's forget about it. + _engine = NULL; + + // Remove any half-done messages from the pipes. + if (_pipe) { + clean_pipes (); + +#ifdef ZMQ_BUILD_DRAFT_API + // Only send disconnect message if socket was accepted and handshake was completed + if (!_active && handshaked_ && options.can_recv_disconnect_msg + && !options.disconnect_msg.empty ()) { + _pipe->set_disconnect_msg (options.disconnect_msg); + _pipe->send_disconnect_msg (); + } +#endif + } + + zmq_assert (reason_ == i_engine::connection_error + || reason_ == i_engine::timeout_error + || reason_ == i_engine::protocol_error); + + switch (reason_) { + case i_engine::timeout_error: + /* FALLTHROUGH */ + case i_engine::connection_error: + if (_active) { + reconnect (); + break; + } + + case i_engine::protocol_error: + if (_pending) { + if (_pipe) + _pipe->terminate (false); + if (_zap_pipe) + _zap_pipe->terminate (false); + } else { + terminate (); + } + break; + } + + // Just in case there's only a delimiter in the pipe. + if (_pipe) + _pipe->check_read (); + + if (_zap_pipe) + _zap_pipe->check_read (); +} + +void zmq::session_base_t::process_term (int linger_) +{ + zmq_assert (!_pending); + + // If the termination of the pipe happens before the term command is + // delivered there's nothing much to do. We can proceed with the + // standard termination immediately. + if (!_pipe && !_zap_pipe && _terminating_pipes.empty ()) { + own_t::process_term (0); + return; + } + + _pending = true; + + if (_pipe != NULL) { + // If there's finite linger value, delay the termination. + // If linger is infinite (negative) we don't even have to set + // the timer. + if (linger_ > 0) { + zmq_assert (!_has_linger_timer); + add_timer (linger_, linger_timer_id); + _has_linger_timer = true; + } + + // Start pipe termination process. Delay the termination till all messages + // are processed in case the linger time is non-zero. + _pipe->terminate (linger_ != 0); + + // TODO: Should this go into pipe_t::terminate ? + // In case there's no engine and there's only delimiter in the + // pipe it wouldn't be ever read. Thus we check for it explicitly. + if (!_engine) + _pipe->check_read (); + } + + if (_zap_pipe != NULL) + _zap_pipe->terminate (false); +} + +void zmq::session_base_t::timer_event (int id_) +{ + // Linger period expired. We can proceed with termination even though + // there are still pending messages to be sent. + zmq_assert (id_ == linger_timer_id); + _has_linger_timer = false; + + // Ask pipe to terminate even though there may be pending messages in it. + zmq_assert (_pipe); + _pipe->terminate (false); +} + +void zmq::session_base_t::process_conn_failed () +{ + std::string *ep = new (std::string); + _addr->to_string (*ep); + send_term_endpoint (_socket, ep); +} + +void zmq::session_base_t::reconnect () +{ + // For delayed connect situations, terminate the pipe + // and reestablish later on + if (_pipe && options.immediate == 1 +#ifdef ZMQ_HAVE_OPENPGM + && _addr->protocol != protocol_name::pgm + && _addr->protocol != protocol_name::epgm +#endif +#ifdef ZMQ_HAVE_NORM + && _addr->protocol != protocol_name::norm +#endif + && _addr->protocol != protocol_name::udp) { + _pipe->hiccup (); + _pipe->terminate (false); + _terminating_pipes.insert (_pipe); + _pipe = NULL; + + if (_has_linger_timer) { + cancel_timer (linger_timer_id); + _has_linger_timer = false; + } + } + + reset (); + + // Reconnect. + if (options.reconnect_ivl > 0) + start_connecting (true); + else { + std::string *ep = new (std::string); + _addr->to_string (*ep); + send_term_endpoint (_socket, ep); + } + + // For subscriber sockets we hiccup the inbound pipe, which will cause + // the socket object to resend all the subscriptions. + if (_pipe + && (options.type == ZMQ_SUB || options.type == ZMQ_XSUB + || options.type == ZMQ_DISH)) + _pipe->hiccup (); +} + +void zmq::session_base_t::start_connecting (bool wait_) +{ + zmq_assert (_active); + + // Choose I/O thread to run connecter in. Given that we are already + // running in an I/O thread, there must be at least one available. + io_thread_t *io_thread = choose_io_thread (options.affinity); + zmq_assert (io_thread); + + // Create the connecter object. + own_t *connecter = NULL; + if (_addr->protocol == protocol_name::tcp) { + if (!options.socks_proxy_address.empty ()) { + address_t *proxy_address = new (std::nothrow) + address_t (protocol_name::tcp, options.socks_proxy_address, + this->get_ctx ()); + alloc_assert (proxy_address); + connecter = new (std::nothrow) socks_connecter_t ( + io_thread, this, options, _addr, proxy_address, wait_); + alloc_assert (connecter); + if (!options.socks_proxy_username.empty ()) { + reinterpret_cast (connecter) + ->set_auth_method_basic (options.socks_proxy_username, + options.socks_proxy_password); + } + } else { + connecter = new (std::nothrow) + tcp_connecter_t (io_thread, this, options, _addr, wait_); + } + } +#if defined ZMQ_HAVE_IPC + else if (_addr->protocol == protocol_name::ipc) { + connecter = new (std::nothrow) + ipc_connecter_t (io_thread, this, options, _addr, wait_); + } +#endif +#if defined ZMQ_HAVE_TIPC + else if (_addr->protocol == protocol_name::tipc) { + connecter = new (std::nothrow) + tipc_connecter_t (io_thread, this, options, _addr, wait_); + } +#endif +#if defined ZMQ_HAVE_VMCI + else if (_addr->protocol == protocol_name::vmci) { + connecter = new (std::nothrow) + vmci_connecter_t (io_thread, this, options, _addr, wait_); + } +#endif +#if defined ZMQ_HAVE_WS + else if (_addr->protocol == protocol_name::ws) { + connecter = new (std::nothrow) ws_connecter_t ( + io_thread, this, options, _addr, wait_, false, std::string ()); + } +#endif +#if defined ZMQ_HAVE_WSS + else if (_addr->protocol == protocol_name::wss) { + connecter = new (std::nothrow) ws_connecter_t ( + io_thread, this, options, _addr, wait_, true, _wss_hostname); + } +#endif + if (connecter != NULL) { + alloc_assert (connecter); + launch_child (connecter); + return; + } + + if (_addr->protocol == protocol_name::udp) { + zmq_assert (options.type == ZMQ_DISH || options.type == ZMQ_RADIO + || options.type == ZMQ_DGRAM); + + udp_engine_t *engine = new (std::nothrow) udp_engine_t (options); + alloc_assert (engine); + + bool recv = false; + bool send = false; + + if (options.type == ZMQ_RADIO) { + send = true; + recv = false; + } else if (options.type == ZMQ_DISH) { + send = false; + recv = true; + } else if (options.type == ZMQ_DGRAM) { + send = true; + recv = true; + } + + int rc = engine->init (_addr, send, recv); + errno_assert (rc == 0); + + send_attach (this, engine); + + return; + } + +#ifdef ZMQ_HAVE_OPENPGM + + // Both PGM and EPGM transports are using the same infrastructure. + if (_addr->protocol == "pgm" || _addr->protocol == "epgm") { + zmq_assert (options.type == ZMQ_PUB || options.type == ZMQ_XPUB + || options.type == ZMQ_SUB || options.type == ZMQ_XSUB); + + // For EPGM transport with UDP encapsulation of PGM is used. + bool const udp_encapsulation = _addr->protocol == "epgm"; + + // At this point we'll create message pipes to the session straight + // away. There's no point in delaying it as no concept of 'connect' + // exists with PGM anyway. + if (options.type == ZMQ_PUB || options.type == ZMQ_XPUB) { + // PGM sender. + pgm_sender_t *pgm_sender = + new (std::nothrow) pgm_sender_t (io_thread, options); + alloc_assert (pgm_sender); + + int rc = + pgm_sender->init (udp_encapsulation, _addr->address.c_str ()); + errno_assert (rc == 0); + + send_attach (this, pgm_sender); + } else { + // PGM receiver. + pgm_receiver_t *pgm_receiver = + new (std::nothrow) pgm_receiver_t (io_thread, options); + alloc_assert (pgm_receiver); + + int rc = + pgm_receiver->init (udp_encapsulation, _addr->address.c_str ()); + errno_assert (rc == 0); + + send_attach (this, pgm_receiver); + } + + return; + } +#endif + +#ifdef ZMQ_HAVE_NORM + if (_addr->protocol == "norm") { + // At this point we'll create message pipes to the session straight + // away. There's no point in delaying it as no concept of 'connect' + // exists with NORM anyway. + if (options.type == ZMQ_PUB || options.type == ZMQ_XPUB) { + // NORM sender. + norm_engine_t *norm_sender = + new (std::nothrow) norm_engine_t (io_thread, options); + alloc_assert (norm_sender); + + int rc = norm_sender->init (_addr->address.c_str (), true, false); + errno_assert (rc == 0); + + send_attach (this, norm_sender); + } else { // ZMQ_SUB or ZMQ_XSUB + + // NORM receiver. + norm_engine_t *norm_receiver = + new (std::nothrow) norm_engine_t (io_thread, options); + alloc_assert (norm_receiver); + + int rc = norm_receiver->init (_addr->address.c_str (), false, true); + errno_assert (rc == 0); + + send_attach (this, norm_receiver); + } + return; + } +#endif // ZMQ_HAVE_NORM + + zmq_assert (false); +} + +zmq::hello_msg_session_t::hello_msg_session_t (io_thread_t *io_thread_, + bool connect_, + socket_base_t *socket_, + const options_t &options_, + address_t *addr_) : + session_base_t (io_thread_, connect_, socket_, options_, addr_), + _new_pipe (true) +{ +} + +zmq::hello_msg_session_t::~hello_msg_session_t () +{ +} + + +int zmq::hello_msg_session_t::pull_msg (msg_t *msg_) +{ + if (_new_pipe) { + _new_pipe = false; + + const int rc = + msg_->init_buffer (&options.hello_msg[0], options.hello_msg.size ()); + errno_assert (rc == 0); + + return 0; + } + + return session_base_t::pull_msg (msg_); +} + +void zmq::hello_msg_session_t::reset () +{ + session_base_t::reset (); + _new_pipe = true; +} diff --git a/3rd/libzmq/src/session_base.hpp b/3rd/libzmq/src/session_base.hpp new file mode 100644 index 00000000..c2e6d098 --- /dev/null +++ b/3rd/libzmq/src/session_base.hpp @@ -0,0 +1,198 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SESSION_BASE_HPP_INCLUDED__ +#define __ZMQ_SESSION_BASE_HPP_INCLUDED__ + +#include + +#include "own.hpp" +#include "io_object.hpp" +#include "pipe.hpp" +#include "socket_base.hpp" +#include "i_engine.hpp" +#include "msg.hpp" + +namespace zmq +{ +class io_thread_t; +struct i_engine; +struct address_t; + +class session_base_t : public own_t, public io_object_t, public i_pipe_events +{ + public: + // Create a session of the particular type. + static session_base_t *create (zmq::io_thread_t *io_thread_, + bool active_, + zmq::socket_base_t *socket_, + const options_t &options_, + address_t *addr_); + + // To be used once only, when creating the session. + void attach_pipe (zmq::pipe_t *pipe_); + + // Following functions are the interface exposed towards the engine. + virtual void reset (); + void flush (); + void rollback (); + void engine_error (bool handshaked_, zmq::i_engine::error_reason_t reason_); + void engine_ready (); + + // i_pipe_events interface implementation. + void read_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void write_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void hiccuped (zmq::pipe_t *pipe_) ZMQ_FINAL; + void pipe_terminated (zmq::pipe_t *pipe_) ZMQ_FINAL; + + // Delivers a message. Returns 0 if successful; -1 otherwise. + // The function takes ownership of the message. + virtual int push_msg (msg_t *msg_); + + int zap_connect (); + bool zap_enabled () const; + + // Fetches a message. Returns 0 if successful; -1 otherwise. + // The caller is responsible for freeing the message when no + // longer used. + virtual int pull_msg (msg_t *msg_); + + // Receives message from ZAP socket. + // Returns 0 on success; -1 otherwise. + // The caller is responsible for freeing the message. + int read_zap_msg (msg_t *msg_); + + // Sends message to ZAP socket. + // Returns 0 on success; -1 otherwise. + // The function takes ownership of the message. + int write_zap_msg (msg_t *msg_); + + socket_base_t *get_socket () const; + const endpoint_uri_pair_t &get_endpoint () const; + + protected: + session_base_t (zmq::io_thread_t *io_thread_, + bool active_, + zmq::socket_base_t *socket_, + const options_t &options_, + address_t *addr_); + ~session_base_t () ZMQ_OVERRIDE; + + private: + void start_connecting (bool wait_); + + void reconnect (); + + // Handlers for incoming commands. + void process_plug () ZMQ_FINAL; + void process_attach (zmq::i_engine *engine_) ZMQ_FINAL; + void process_term (int linger_) ZMQ_FINAL; + void process_conn_failed (); + + // i_poll_events handlers. + void timer_event (int id_) ZMQ_FINAL; + + // Remove any half processed messages. Flush unflushed messages. + // Call this function when engine disconnect to get rid of leftovers. + void clean_pipes (); + + // If true, this session (re)connects to the peer. Otherwise, it's + // a transient session created by the listener. + const bool _active; + + // Pipe connecting the session to its socket. + zmq::pipe_t *_pipe; + + // Pipe used to exchange messages with ZAP socket. + zmq::pipe_t *_zap_pipe; + + // This set is added to with pipes we are disconnecting, but haven't yet completed + std::set _terminating_pipes; + + // This flag is true if the remainder of the message being processed + // is still in the in pipe. + bool _incomplete_in; + + // True if termination have been suspended to push the pending + // messages to the network. + bool _pending; + + // The protocol I/O engine connected to the session. + zmq::i_engine *_engine; + + // The socket the session belongs to. + zmq::socket_base_t *_socket; + + // I/O thread the session is living in. It will be used to plug in + // the engines into the same thread. + zmq::io_thread_t *_io_thread; + + // ID of the linger timer + enum + { + linger_timer_id = 0x20 + }; + + // True is linger timer is running. + bool _has_linger_timer; + + // Protocol and address to use when connecting. + address_t *_addr; + +#ifdef ZMQ_HAVE_WSS + // TLS handshake, we need to take a copy when the session is created, + // in order to maintain the value at the creation time + const std::string _wss_hostname; +#endif + + ZMQ_NON_COPYABLE_NOR_MOVABLE (session_base_t) +}; + +class hello_msg_session_t ZMQ_FINAL : public session_base_t +{ + public: + hello_msg_session_t (zmq::io_thread_t *io_thread_, + bool connect_, + zmq::socket_base_t *socket_, + const options_t &options_, + address_t *addr_); + ~hello_msg_session_t (); + + // Overrides of the functions from session_base_t. + int pull_msg (msg_t *msg_); + void reset (); + + private: + bool _new_pipe; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (hello_msg_session_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/signaler.cpp b/3rd/libzmq/src/signaler.cpp new file mode 100644 index 00000000..93fb248e --- /dev/null +++ b/3rd/libzmq/src/signaler.cpp @@ -0,0 +1,414 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "poller.hpp" +#include "polling_util.hpp" + +#if defined ZMQ_POLL_BASED_ON_POLL +#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_AIX +#include +#endif +#elif defined ZMQ_POLL_BASED_ON_SELECT +#if defined ZMQ_HAVE_WINDOWS +#elif defined ZMQ_HAVE_HPUX +#include +#include +#include +#elif defined ZMQ_HAVE_OPENVMS +#include +#include +#elif defined ZMQ_HAVE_VXWORKS +#include +#include +#include +#include +#else +#include +#endif +#endif + +#include "signaler.hpp" +#include "likely.hpp" +#include "stdint.hpp" +#include "config.hpp" +#include "err.hpp" +#include "fd.hpp" +#include "ip.hpp" +#include "tcp.hpp" + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#endif + +#if !defined(ZMQ_HAVE_WINDOWS) +// Helper to sleep for specific number of milliseconds (or until signal) +// +static int sleep_ms (unsigned int ms_) +{ + if (ms_ == 0) + return 0; +#if defined ZMQ_HAVE_ANDROID + usleep (ms_ * 1000); + return 0; +#elif defined ZMQ_HAVE_VXWORKS + struct timespec ns_; + ns_.tv_sec = ms_ / 1000; + ns_.tv_nsec = ms_ % 1000 * 1000000; + return nanosleep (&ns_, 0); +#else + return usleep (ms_ * 1000); +#endif +} + +// Helper to wait on close(), for non-blocking sockets, until it completes +// If EAGAIN is received, will sleep briefly (1-100ms) then try again, until +// the overall timeout is reached. +// +static int close_wait_ms (int fd_, unsigned int max_ms_ = 2000) +{ + unsigned int ms_so_far = 0; + const unsigned int min_step_ms = 1; + const unsigned int max_step_ms = 100; + const unsigned int step_ms = + std::min (std::max (min_step_ms, max_ms_ / 10), max_step_ms); + + int rc = 0; // do not sleep on first attempt + do { + if (rc == -1 && errno == EAGAIN) { + sleep_ms (step_ms); + ms_so_far += step_ms; + } + rc = close (fd_); + } while (ms_so_far < max_ms_ && rc == -1 && errno == EAGAIN); + + return rc; +} +#endif + +zmq::signaler_t::signaler_t () +{ + // Create the socketpair for signaling. + if (make_fdpair (&_r, &_w) == 0) { + unblock_socket (_w); + unblock_socket (_r); + } +#ifdef HAVE_FORK + pid = getpid (); +#endif +} + +// This might get run after some part of construction failed, leaving one or +// both of _r and _w retired_fd. +zmq::signaler_t::~signaler_t () +{ +#if defined ZMQ_HAVE_EVENTFD + if (_r == retired_fd) + return; + int rc = close_wait_ms (_r); + errno_assert (rc == 0); +#elif defined ZMQ_HAVE_WINDOWS + if (_w != retired_fd) { + const struct linger so_linger = {1, 0}; + int rc = setsockopt (_w, SOL_SOCKET, SO_LINGER, + reinterpret_cast (&so_linger), + sizeof so_linger); + // Only check shutdown if WSASTARTUP was previously done + if (rc == 0 || WSAGetLastError () != WSANOTINITIALISED) { + wsa_assert (rc != SOCKET_ERROR); + rc = closesocket (_w); + wsa_assert (rc != SOCKET_ERROR); + if (_r == retired_fd) + return; + rc = closesocket (_r); + wsa_assert (rc != SOCKET_ERROR); + } + } +#else + if (_w != retired_fd) { + int rc = close_wait_ms (_w); + errno_assert (rc == 0); + } + if (_r != retired_fd) { + int rc = close_wait_ms (_r); + errno_assert (rc == 0); + } +#endif +} + +zmq::fd_t zmq::signaler_t::get_fd () const +{ + return _r; +} + +void zmq::signaler_t::send () +{ +#if defined HAVE_FORK + if (unlikely (pid != getpid ())) { + //printf("Child process %d signaler_t::send returning without sending #1\n", getpid()); + return; // do not send anything in forked child context + } +#endif +#if defined ZMQ_HAVE_EVENTFD + const uint64_t inc = 1; + ssize_t sz = write (_w, &inc, sizeof (inc)); + errno_assert (sz == sizeof (inc)); +#elif defined ZMQ_HAVE_WINDOWS + const char dummy = 0; + int nbytes; + do { + nbytes = ::send (_w, &dummy, sizeof (dummy), 0); + wsa_assert (nbytes != SOCKET_ERROR); + // wsa_assert does not abort on WSAEWOULDBLOCK. If we get this, we retry. + } while (nbytes == SOCKET_ERROR); + // Given the small size of dummy (should be 1) expect that send was able to send everything. + zmq_assert (nbytes == sizeof (dummy)); +#elif defined ZMQ_HAVE_VXWORKS + unsigned char dummy = 0; + while (true) { + ssize_t nbytes = ::send (_w, (char *) &dummy, sizeof (dummy), 0); + if (unlikely (nbytes == -1 && errno == EINTR)) + continue; +#if defined(HAVE_FORK) + if (unlikely (pid != getpid ())) { + //printf("Child process %d signaler_t::send returning without sending #2\n", getpid()); + errno = EINTR; + break; + } +#endif + zmq_assert (nbytes == sizeof dummy); + break; + } +#else + unsigned char dummy = 0; + while (true) { + ssize_t nbytes = ::send (_w, &dummy, sizeof (dummy), 0); + if (unlikely (nbytes == -1 && errno == EINTR)) + continue; +#if defined(HAVE_FORK) + if (unlikely (pid != getpid ())) { + //printf("Child process %d signaler_t::send returning without sending #2\n", getpid()); + errno = EINTR; + break; + } +#endif + zmq_assert (nbytes == sizeof dummy); + break; + } +#endif +} + +int zmq::signaler_t::wait (int timeout_) const +{ +#ifdef HAVE_FORK + if (unlikely (pid != getpid ())) { + // we have forked and the file descriptor is closed. Emulate an interrupt + // response. + //printf("Child process %d signaler_t::wait returning simulating interrupt #1\n", getpid()); + errno = EINTR; + return -1; + } +#endif + +#ifdef ZMQ_POLL_BASED_ON_POLL + struct pollfd pfd; + pfd.fd = _r; + pfd.events = POLLIN; + const int rc = poll (&pfd, 1, timeout_); + if (unlikely (rc < 0)) { + errno_assert (errno == EINTR); + return -1; + } + if (unlikely (rc == 0)) { + errno = EAGAIN; + return -1; + } +#ifdef HAVE_FORK + if (unlikely (pid != getpid ())) { + // we have forked and the file descriptor is closed. Emulate an interrupt + // response. + //printf("Child process %d signaler_t::wait returning simulating interrupt #2\n", getpid()); + errno = EINTR; + return -1; + } +#endif + zmq_assert (rc == 1); + zmq_assert (pfd.revents & POLLIN); + return 0; + +#elif defined ZMQ_POLL_BASED_ON_SELECT + + optimized_fd_set_t fds (1); + FD_ZERO (fds.get ()); + FD_SET (_r, fds.get ()); + struct timeval timeout; + if (timeout_ >= 0) { + timeout.tv_sec = timeout_ / 1000; + timeout.tv_usec = timeout_ % 1000 * 1000; + } +#ifdef ZMQ_HAVE_WINDOWS + int rc = + select (0, fds.get (), NULL, NULL, timeout_ >= 0 ? &timeout : NULL); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = + select (_r + 1, fds.get (), NULL, NULL, timeout_ >= 0 ? &timeout : NULL); + if (unlikely (rc < 0)) { + errno_assert (errno == EINTR); + return -1; + } +#endif + if (unlikely (rc == 0)) { + errno = EAGAIN; + return -1; + } + zmq_assert (rc == 1); + return 0; + +#else +#error +#endif +} + +void zmq::signaler_t::recv () +{ +// Attempt to read a signal. +#if defined ZMQ_HAVE_EVENTFD + uint64_t dummy; + ssize_t sz = read (_r, &dummy, sizeof (dummy)); + errno_assert (sz == sizeof (dummy)); + + // If we accidentally grabbed the next signal(s) along with the current + // one, return it back to the eventfd object. + if (unlikely (dummy > 1)) { + const uint64_t inc = dummy - 1; + ssize_t sz2 = write (_w, &inc, sizeof (inc)); + errno_assert (sz2 == sizeof (inc)); + return; + } + + zmq_assert (dummy == 1); +#else + unsigned char dummy; +#if defined ZMQ_HAVE_WINDOWS + const int nbytes = + ::recv (_r, reinterpret_cast (&dummy), sizeof (dummy), 0); + wsa_assert (nbytes != SOCKET_ERROR); +#elif defined ZMQ_HAVE_VXWORKS + ssize_t nbytes = ::recv (_r, (char *) &dummy, sizeof (dummy), 0); + errno_assert (nbytes >= 0); +#else + ssize_t nbytes = ::recv (_r, &dummy, sizeof (dummy), 0); + errno_assert (nbytes >= 0); +#endif + zmq_assert (nbytes == sizeof (dummy)); + zmq_assert (dummy == 0); +#endif +} + +int zmq::signaler_t::recv_failable () +{ +// Attempt to read a signal. +#if defined ZMQ_HAVE_EVENTFD + uint64_t dummy; + ssize_t sz = read (_r, &dummy, sizeof (dummy)); + if (sz == -1) { + errno_assert (errno == EAGAIN); + return -1; + } + errno_assert (sz == sizeof (dummy)); + + // If we accidentally grabbed the next signal(s) along with the current + // one, return it back to the eventfd object. + if (unlikely (dummy > 1)) { + const uint64_t inc = dummy - 1; + ssize_t sz2 = write (_w, &inc, sizeof (inc)); + errno_assert (sz2 == sizeof (inc)); + return 0; + } + + zmq_assert (dummy == 1); + +#else + unsigned char dummy; +#if defined ZMQ_HAVE_WINDOWS + const int nbytes = + ::recv (_r, reinterpret_cast (&dummy), sizeof (dummy), 0); + if (nbytes == SOCKET_ERROR) { + const int last_error = WSAGetLastError (); + if (last_error == WSAEWOULDBLOCK) { + errno = EAGAIN; + return -1; + } + wsa_assert (last_error == WSAEWOULDBLOCK); + } +#elif defined ZMQ_HAVE_VXWORKS + ssize_t nbytes = ::recv (_r, (char *) &dummy, sizeof (dummy), 0); + if (nbytes == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + errno = EAGAIN; + return -1; + } + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK + || errno == EINTR); + } +#else + ssize_t nbytes = ::recv (_r, &dummy, sizeof (dummy), 0); + if (nbytes == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + errno = EAGAIN; + return -1; + } + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK + || errno == EINTR); + } +#endif + zmq_assert (nbytes == sizeof (dummy)); + zmq_assert (dummy == 0); +#endif + return 0; +} + +bool zmq::signaler_t::valid () const +{ + return _w != retired_fd; +} + +#ifdef HAVE_FORK +void zmq::signaler_t::forked () +{ + // Close file descriptors created in the parent and create new pair + close (_r); + close (_w); + make_fdpair (&_r, &_w); +} +#endif diff --git a/3rd/libzmq/src/signaler.hpp b/3rd/libzmq/src/signaler.hpp new file mode 100644 index 00000000..49fac1ba --- /dev/null +++ b/3rd/libzmq/src/signaler.hpp @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SIGNALER_HPP_INCLUDED__ +#define __ZMQ_SIGNALER_HPP_INCLUDED__ + +#ifdef HAVE_FORK +#include +#endif + +#include "fd.hpp" +#include "macros.hpp" + +namespace zmq +{ +// This is a cross-platform equivalent to signal_fd. However, as opposed +// to signal_fd there can be at most one signal in the signaler at any +// given moment. Attempt to send a signal before receiving the previous +// one will result in undefined behaviour. + +class signaler_t +{ + public: + signaler_t (); + ~signaler_t (); + + // Returns the socket/file descriptor + // May return retired_fd if the signaler could not be initialized. + fd_t get_fd () const; + void send (); + int wait (int timeout_) const; + void recv (); + int recv_failable (); + + bool valid () const; + +#ifdef HAVE_FORK + // close the file descriptors in a forked child process so that they + // do not interfere with the context in the parent process. + void forked (); +#endif + + private: + // Underlying write & read file descriptor + // Will be -1 if an error occurred during initialization, e.g. we + // exceeded the number of available handles + fd_t _w; + fd_t _r; + +#ifdef HAVE_FORK + // the process that created this context. Used to detect forking. + pid_t pid; + // idempotent close of file descriptors that is safe to use by destructor + // and forked(). + void close_internal (); +#endif + + ZMQ_NON_COPYABLE_NOR_MOVABLE (signaler_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/socket_base.cpp b/3rd/libzmq/src/socket_base.cpp new file mode 100644 index 00000000..bbb378cc --- /dev/null +++ b/3rd/libzmq/src/socket_base.cpp @@ -0,0 +1,2168 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include +#include +#include + +#include "macros.hpp" + +#if defined ZMQ_HAVE_WINDOWS +#if defined _MSC_VER +#if defined _WIN32_WCE +#include +#else +#include +#endif +#endif +#else +#include +#include +#endif + +#include "socket_base.hpp" +#include "tcp_listener.hpp" +#include "ws_listener.hpp" +#include "ipc_listener.hpp" +#include "tipc_listener.hpp" +#include "tcp_connecter.hpp" +#ifdef ZMQ_HAVE_WS +#include "ws_address.hpp" +#endif +#include "io_thread.hpp" +#include "session_base.hpp" +#include "config.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "ctx.hpp" +#include "likely.hpp" +#include "msg.hpp" +#include "address.hpp" +#include "ipc_address.hpp" +#include "tcp_address.hpp" +#include "udp_address.hpp" +#include "tipc_address.hpp" +#include "mailbox.hpp" +#include "mailbox_safe.hpp" + +#ifdef ZMQ_HAVE_WSS +#include "wss_address.hpp" +#endif +#if defined ZMQ_HAVE_VMCI +#include "vmci_address.hpp" +#include "vmci_listener.hpp" +#endif + +#ifdef ZMQ_HAVE_OPENPGM +#include "pgm_socket.hpp" +#endif + +#include "pair.hpp" +#include "pub.hpp" +#include "sub.hpp" +#include "req.hpp" +#include "rep.hpp" +#include "pull.hpp" +#include "push.hpp" +#include "dealer.hpp" +#include "router.hpp" +#include "xpub.hpp" +#include "xsub.hpp" +#include "stream.hpp" +#include "server.hpp" +#include "client.hpp" +#include "radio.hpp" +#include "dish.hpp" +#include "gather.hpp" +#include "scatter.hpp" +#include "dgram.hpp" +#include "peer.hpp" +#include "channel.hpp" + +void zmq::socket_base_t::inprocs_t::emplace (const char *endpoint_uri_, + pipe_t *pipe_) +{ + _inprocs.ZMQ_MAP_INSERT_OR_EMPLACE (std::string (endpoint_uri_), pipe_); +} + +int zmq::socket_base_t::inprocs_t::erase_pipes ( + const std::string &endpoint_uri_str_) +{ + const std::pair range = + _inprocs.equal_range (endpoint_uri_str_); + if (range.first == range.second) { + errno = ENOENT; + return -1; + } + + for (map_t::iterator it = range.first; it != range.second; ++it) { + it->second->send_disconnect_msg (); + it->second->terminate (true); + } + _inprocs.erase (range.first, range.second); + return 0; +} + +void zmq::socket_base_t::inprocs_t::erase_pipe (const pipe_t *pipe_) +{ + for (map_t::iterator it = _inprocs.begin (), end = _inprocs.end (); + it != end; ++it) + if (it->second == pipe_) { + _inprocs.erase (it); + break; + } +} + +bool zmq::socket_base_t::check_tag () const +{ + return _tag == 0xbaddecaf; +} + +bool zmq::socket_base_t::is_thread_safe () const +{ + return _thread_safe; +} + +zmq::socket_base_t *zmq::socket_base_t::create (int type_, + class ctx_t *parent_, + uint32_t tid_, + int sid_) +{ + socket_base_t *s = NULL; + switch (type_) { + case ZMQ_PAIR: + s = new (std::nothrow) pair_t (parent_, tid_, sid_); + break; + case ZMQ_PUB: + s = new (std::nothrow) pub_t (parent_, tid_, sid_); + break; + case ZMQ_SUB: + s = new (std::nothrow) sub_t (parent_, tid_, sid_); + break; + case ZMQ_REQ: + s = new (std::nothrow) req_t (parent_, tid_, sid_); + break; + case ZMQ_REP: + s = new (std::nothrow) rep_t (parent_, tid_, sid_); + break; + case ZMQ_DEALER: + s = new (std::nothrow) dealer_t (parent_, tid_, sid_); + break; + case ZMQ_ROUTER: + s = new (std::nothrow) router_t (parent_, tid_, sid_); + break; + case ZMQ_PULL: + s = new (std::nothrow) pull_t (parent_, tid_, sid_); + break; + case ZMQ_PUSH: + s = new (std::nothrow) push_t (parent_, tid_, sid_); + break; + case ZMQ_XPUB: + s = new (std::nothrow) xpub_t (parent_, tid_, sid_); + break; + case ZMQ_XSUB: + s = new (std::nothrow) xsub_t (parent_, tid_, sid_); + break; + case ZMQ_STREAM: + s = new (std::nothrow) stream_t (parent_, tid_, sid_); + break; + case ZMQ_SERVER: + s = new (std::nothrow) server_t (parent_, tid_, sid_); + break; + case ZMQ_CLIENT: + s = new (std::nothrow) client_t (parent_, tid_, sid_); + break; + case ZMQ_RADIO: + s = new (std::nothrow) radio_t (parent_, tid_, sid_); + break; + case ZMQ_DISH: + s = new (std::nothrow) dish_t (parent_, tid_, sid_); + break; + case ZMQ_GATHER: + s = new (std::nothrow) gather_t (parent_, tid_, sid_); + break; + case ZMQ_SCATTER: + s = new (std::nothrow) scatter_t (parent_, tid_, sid_); + break; + case ZMQ_DGRAM: + s = new (std::nothrow) dgram_t (parent_, tid_, sid_); + break; + case ZMQ_PEER: + s = new (std::nothrow) peer_t (parent_, tid_, sid_); + break; + case ZMQ_CHANNEL: + s = new (std::nothrow) channel_t (parent_, tid_, sid_); + break; + default: + errno = EINVAL; + return NULL; + } + + alloc_assert (s); + + if (s->_mailbox == NULL) { + s->_destroyed = true; + LIBZMQ_DELETE (s); + return NULL; + } + + return s; +} + +zmq::socket_base_t::socket_base_t (ctx_t *parent_, + uint32_t tid_, + int sid_, + bool thread_safe_) : + own_t (parent_, tid_), + _sync (), + _tag (0xbaddecaf), + _ctx_terminated (false), + _destroyed (false), + _poller (NULL), + _handle (static_cast (NULL)), + _last_tsc (0), + _ticks (0), + _rcvmore (false), + _monitor_socket (NULL), + _monitor_events (0), + _thread_safe (thread_safe_), + _reaper_signaler (NULL), + _monitor_sync () +{ + options.socket_id = sid_; + options.ipv6 = (parent_->get (ZMQ_IPV6) != 0); + options.linger.store (parent_->get (ZMQ_BLOCKY) ? -1 : 0); + options.zero_copy = parent_->get (ZMQ_ZERO_COPY_RECV) != 0; + + if (_thread_safe) { + _mailbox = new (std::nothrow) mailbox_safe_t (&_sync); + zmq_assert (_mailbox); + } else { + mailbox_t *m = new (std::nothrow) mailbox_t (); + zmq_assert (m); + + if (m->get_fd () != retired_fd) + _mailbox = m; + else { + LIBZMQ_DELETE (m); + _mailbox = NULL; + } + } +} + +int zmq::socket_base_t::get_peer_state (const void *routing_id_, + size_t routing_id_size_) const +{ + LIBZMQ_UNUSED (routing_id_); + LIBZMQ_UNUSED (routing_id_size_); + + // Only ROUTER sockets support this + errno = ENOTSUP; + return -1; +} + +zmq::socket_base_t::~socket_base_t () +{ + if (_mailbox) + LIBZMQ_DELETE (_mailbox); + + if (_reaper_signaler) + LIBZMQ_DELETE (_reaper_signaler); + + scoped_lock_t lock (_monitor_sync); + stop_monitor (); + + zmq_assert (_destroyed); +} + +zmq::i_mailbox *zmq::socket_base_t::get_mailbox () const +{ + return _mailbox; +} + +void zmq::socket_base_t::stop () +{ + // Called by ctx when it is terminated (zmq_ctx_term). + // 'stop' command is sent from the threads that called zmq_ctx_term to + // the thread owning the socket. This way, blocking call in the + // owner thread can be interrupted. + send_stop (); +} + +// TODO consider renaming protocol_ to scheme_ in conformance with RFC 3986 +// terminology, but this requires extensive changes to be consistent +int zmq::socket_base_t::parse_uri (const char *uri_, + std::string &protocol_, + std::string &path_) +{ + zmq_assert (uri_ != NULL); + + const std::string uri (uri_); + const std::string::size_type pos = uri.find ("://"); + if (pos == std::string::npos) { + errno = EINVAL; + return -1; + } + protocol_ = uri.substr (0, pos); + path_ = uri.substr (pos + 3); + + if (protocol_.empty () || path_.empty ()) { + errno = EINVAL; + return -1; + } + return 0; +} + +int zmq::socket_base_t::check_protocol (const std::string &protocol_) const +{ + // First check out whether the protocol is something we are aware of. + if (protocol_ != protocol_name::inproc +#if defined ZMQ_HAVE_IPC + && protocol_ != protocol_name::ipc +#endif + && protocol_ != protocol_name::tcp +#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_ != protocol_name::pgm + && protocol_ != protocol_name::epgm +#endif +#if defined ZMQ_HAVE_TIPC + // TIPC transport is only available on Linux. + && protocol_ != protocol_name::tipc +#endif +#if defined ZMQ_HAVE_NORM + && protocol_ != protocol_name::norm +#endif +#if defined ZMQ_HAVE_VMCI + && protocol_ != protocol_name::vmci +#endif + && protocol_ != protocol_name::udp) { + errno = EPROTONOSUPPORT; + return -1; + } + + // Check whether socket type and transport protocol match. + // Specifically, multicast protocols can't be combined with + // bi-directional messaging patterns (socket types). +#if defined ZMQ_HAVE_OPENPGM || defined ZMQ_HAVE_NORM +#if defined ZMQ_HAVE_OPENPGM && defined ZMQ_HAVE_NORM + if ((protocol_ == protocol_name::pgm || protocol_ == protocol_name::epgm + || protocol_ == protocol_name::norm) +#elif defined ZMQ_HAVE_OPENPGM + if ((protocol_ == protocol_name::pgm || protocol_ == protocol_name::epgm) +#else // defined ZMQ_HAVE_NORM + if (protocol_ == protocol_name::norm +#endif + && options.type != ZMQ_PUB && options.type != ZMQ_SUB + && options.type != ZMQ_XPUB && options.type != ZMQ_XSUB) { + errno = ENOCOMPATPROTO; + return -1; + } +#endif + + if (protocol_ == protocol_name::udp + && (options.type != ZMQ_DISH && options.type != ZMQ_RADIO + && options.type != ZMQ_DGRAM)) { + errno = ENOCOMPATPROTO; + return -1; + } + + // Protocol is available. + return 0; +} + +void zmq::socket_base_t::attach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + // First, register the pipe so that we can terminate it later on. + pipe_->set_event_sink (this); + _pipes.push_back (pipe_); + + // Let the derived socket type know about new pipe. + xattach_pipe (pipe_, subscribe_to_all_, locally_initiated_); + + // If the socket is already being closed, ask any new pipes to terminate + // straight away. + if (is_terminating ()) { + register_term_acks (1); + pipe_->terminate (false); + } +} + +int zmq::socket_base_t::setsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + // First, check whether specific socket type overloads the option. + int rc = xsetsockopt (option_, optval_, optvallen_); + if (rc == 0 || errno != EINVAL) { + return rc; + } + + // If the socket type doesn't support the option, pass it to + // the generic option parser. + rc = options.setsockopt (option_, optval_, optvallen_); + update_pipe_options (option_); + + return rc; +} + +int zmq::socket_base_t::getsockopt (int option_, + void *optval_, + size_t *optvallen_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + if (option_ == ZMQ_RCVMORE) { + return do_getsockopt (optval_, optvallen_, _rcvmore ? 1 : 0); + } + + if (option_ == ZMQ_FD) { + if (_thread_safe) { + // thread safe socket doesn't provide file descriptor + errno = EINVAL; + return -1; + } + + return do_getsockopt ( + optval_, optvallen_, + (static_cast (_mailbox))->get_fd ()); + } + + if (option_ == ZMQ_EVENTS) { + const int rc = process_commands (0, false); + if (rc != 0 && (errno == EINTR || errno == ETERM)) { + return -1; + } + errno_assert (rc == 0); + + return do_getsockopt (optval_, optvallen_, + (has_out () ? ZMQ_POLLOUT : 0) + | (has_in () ? ZMQ_POLLIN : 0)); + } + + if (option_ == ZMQ_LAST_ENDPOINT) { + return do_getsockopt (optval_, optvallen_, _last_endpoint); + } + + if (option_ == ZMQ_THREAD_SAFE) { + return do_getsockopt (optval_, optvallen_, _thread_safe ? 1 : 0); + } + + return options.getsockopt (option_, optval_, optvallen_); +} + +int zmq::socket_base_t::join (const char *group_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + return xjoin (group_); +} + +int zmq::socket_base_t::leave (const char *group_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + return xleave (group_); +} + +void zmq::socket_base_t::add_signaler (signaler_t *s_) +{ + zmq_assert (_thread_safe); + + scoped_lock_t sync_lock (_sync); + (static_cast (_mailbox))->add_signaler (s_); +} + +void zmq::socket_base_t::remove_signaler (signaler_t *s_) +{ + zmq_assert (_thread_safe); + + scoped_lock_t sync_lock (_sync); + (static_cast (_mailbox))->remove_signaler (s_); +} + +int zmq::socket_base_t::bind (const char *endpoint_uri_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + // Process pending commands, if any. + int rc = process_commands (0, false); + if (unlikely (rc != 0)) { + return -1; + } + + // Parse endpoint_uri_ string. + std::string protocol; + std::string address; + if (parse_uri (endpoint_uri_, protocol, address) + || check_protocol (protocol)) { + return -1; + } + + if (protocol == protocol_name::inproc) { + const endpoint_t endpoint = {this, options}; + rc = register_endpoint (endpoint_uri_, endpoint); + if (rc == 0) { + connect_pending (endpoint_uri_, this); + _last_endpoint.assign (endpoint_uri_); + options.connected = true; + } + return rc; + } + +#if defined ZMQ_HAVE_OPENPGM || defined ZMQ_HAVE_NORM +#if defined ZMQ_HAVE_OPENPGM && defined ZMQ_HAVE_NORM + if (protocol == protocol_name::pgm || protocol == protocol_name::epgm + || protocol == protocol_name::norm) { +#elif defined ZMQ_HAVE_OPENPGM + if (protocol == protocol_name::pgm || protocol == protocol_name::epgm) { +#else // defined ZMQ_HAVE_NORM + if (protocol == protocol_name::norm) { +#endif + // For convenience's sake, bind can be used interchangeable with + // connect for PGM, EPGM, NORM transports. + rc = connect (endpoint_uri_); + if (rc != -1) + options.connected = true; + return rc; + } +#endif + + if (protocol == protocol_name::udp) { + if (!(options.type == ZMQ_DGRAM || options.type == ZMQ_DISH)) { + errno = ENOCOMPATPROTO; + return -1; + } + + // Choose the I/O thread to run the session in. + io_thread_t *io_thread = choose_io_thread (options.affinity); + if (!io_thread) { + errno = EMTHREAD; + return -1; + } + + address_t *paddr = + new (std::nothrow) address_t (protocol, address, this->get_ctx ()); + alloc_assert (paddr); + + paddr->resolved.udp_addr = new (std::nothrow) udp_address_t (); + alloc_assert (paddr->resolved.udp_addr); + rc = paddr->resolved.udp_addr->resolve (address.c_str (), true, + options.ipv6); + if (rc != 0) { + LIBZMQ_DELETE (paddr); + return -1; + } + + session_base_t *session = + session_base_t::create (io_thread, true, this, options, paddr); + errno_assert (session); + + // Create a bi-directional pipe. + object_t *parents[2] = {this, session}; + pipe_t *new_pipes[2] = {NULL, NULL}; + + int hwms[2] = {options.sndhwm, options.rcvhwm}; + bool conflates[2] = {false, false}; + rc = pipepair (parents, new_pipes, hwms, conflates); + errno_assert (rc == 0); + + // Attach local end of the pipe to the socket object. + attach_pipe (new_pipes[0], true, true); + pipe_t *const newpipe = new_pipes[0]; + + // Attach remote end of the pipe to the session object later on. + session->attach_pipe (new_pipes[1]); + + // Save last endpoint URI + paddr->to_string (_last_endpoint); + + // TODO shouldn't this use _last_endpoint instead of endpoint_uri_? as in the other cases + add_endpoint (endpoint_uri_pair_t (endpoint_uri_, std::string (), + endpoint_type_none), + static_cast (session), newpipe); + + return 0; + } + + // Remaining transports require to be run in an I/O thread, so at this + // point we'll choose one. + io_thread_t *io_thread = choose_io_thread (options.affinity); + if (!io_thread) { + errno = EMTHREAD; + return -1; + } + + if (protocol == protocol_name::tcp) { + tcp_listener_t *listener = + new (std::nothrow) tcp_listener_t (io_thread, this, options); + alloc_assert (listener); + rc = listener->set_local_address (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (listener); + event_bind_failed (make_unconnected_bind_endpoint_pair (address), + zmq_errno ()); + return -1; + } + + // Save last endpoint URI + listener->get_local_address (_last_endpoint); + + add_endpoint (make_unconnected_bind_endpoint_pair (_last_endpoint), + static_cast (listener), NULL); + options.connected = true; + return 0; + } + +#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, false); +#endif + alloc_assert (listener); + rc = listener->set_local_address (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (listener); + event_bind_failed (make_unconnected_bind_endpoint_pair (address), + zmq_errno ()); + return -1; + } + + // Save last endpoint URI + listener->get_local_address (_last_endpoint); + + add_endpoint (make_unconnected_bind_endpoint_pair (_last_endpoint), + static_cast (listener), NULL); + options.connected = true; + return 0; + } +#endif + +#if defined ZMQ_HAVE_IPC + if (protocol == protocol_name::ipc) { + ipc_listener_t *listener = + new (std::nothrow) ipc_listener_t (io_thread, this, options); + alloc_assert (listener); + int rc = listener->set_local_address (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (listener); + event_bind_failed (make_unconnected_bind_endpoint_pair (address), + zmq_errno ()); + return -1; + } + + // Save last endpoint URI + listener->get_local_address (_last_endpoint); + + add_endpoint (make_unconnected_bind_endpoint_pair (_last_endpoint), + static_cast (listener), NULL); + options.connected = true; + return 0; + } +#endif +#if defined ZMQ_HAVE_TIPC + if (protocol == protocol_name::tipc) { + tipc_listener_t *listener = + new (std::nothrow) tipc_listener_t (io_thread, this, options); + alloc_assert (listener); + int rc = listener->set_local_address (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (listener); + event_bind_failed (make_unconnected_bind_endpoint_pair (address), + zmq_errno ()); + return -1; + } + + // Save last endpoint URI + listener->get_local_address (_last_endpoint); + + // TODO shouldn't this use _last_endpoint as in the other cases? + add_endpoint (make_unconnected_bind_endpoint_pair (endpoint_uri_), + static_cast (listener), NULL); + options.connected = true; + return 0; + } +#endif +#if defined ZMQ_HAVE_VMCI + if (protocol == protocol_name::vmci) { + vmci_listener_t *listener = + new (std::nothrow) vmci_listener_t (io_thread, this, options); + alloc_assert (listener); + int rc = listener->set_local_address (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (listener); + event_bind_failed (make_unconnected_bind_endpoint_pair (address), + zmq_errno ()); + return -1; + } + + listener->get_local_address (_last_endpoint); + + add_endpoint (make_unconnected_bind_endpoint_pair (_last_endpoint), + static_cast (listener), NULL); + options.connected = true; + return 0; + } +#endif + + zmq_assert (false); + return -1; +} + +int zmq::socket_base_t::connect (const char *endpoint_uri_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + return connect_internal (endpoint_uri_); +} + +int zmq::socket_base_t::connect_internal (const char *endpoint_uri_) +{ + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + // Process pending commands, if any. + int rc = process_commands (0, false); + if (unlikely (rc != 0)) { + return -1; + } + + // Parse endpoint_uri_ string. + std::string protocol; + std::string address; + if (parse_uri (endpoint_uri_, protocol, address) + || check_protocol (protocol)) { + return -1; + } + + if (protocol == protocol_name::inproc) { + // TODO: inproc connect is specific with respect to creating pipes + // as there's no 'reconnect' functionality implemented. Once that + // is in place we should follow generic pipe creation algorithm. + + // Find the peer endpoint. + const endpoint_t peer = find_endpoint (endpoint_uri_); + + // The total HWM for an inproc connection should be the sum of + // the binder's HWM and the connector's HWM. + const int sndhwm = peer.socket == NULL + ? options.sndhwm + : options.sndhwm != 0 && peer.options.rcvhwm != 0 + ? options.sndhwm + peer.options.rcvhwm + : 0; + const int rcvhwm = peer.socket == NULL + ? options.rcvhwm + : options.rcvhwm != 0 && peer.options.sndhwm != 0 + ? options.rcvhwm + peer.options.sndhwm + : 0; + + // Create a bi-directional pipe to connect the peers. + object_t *parents[2] = {this, peer.socket == NULL ? this : peer.socket}; + pipe_t *new_pipes[2] = {NULL, NULL}; + + const bool conflate = get_effective_conflate_option (options); + + int hwms[2] = {conflate ? -1 : sndhwm, conflate ? -1 : rcvhwm}; + bool conflates[2] = {conflate, conflate}; + rc = pipepair (parents, new_pipes, hwms, conflates); + if (!conflate) { + new_pipes[0]->set_hwms_boost (peer.options.sndhwm, + peer.options.rcvhwm); + new_pipes[1]->set_hwms_boost (options.sndhwm, options.rcvhwm); + } + + errno_assert (rc == 0); + + if (!peer.socket) { + // The peer doesn't exist yet so we don't know whether + // to send the routing id message or not. To resolve this, + // we always send our routing id and drop it later if + // the peer doesn't expect it. + send_routing_id (new_pipes[0], options); + +#ifdef ZMQ_BUILD_DRAFT_API + // If set, send the hello msg of the local socket to the peer. + if (options.can_send_hello_msg && options.hello_msg.size () > 0) { + send_hello_msg (new_pipes[0], options); + } +#endif + + const endpoint_t endpoint = {this, options}; + pend_connection (std::string (endpoint_uri_), endpoint, new_pipes); + } else { + // If required, send the routing id of the local socket to the peer. + if (peer.options.recv_routing_id) { + send_routing_id (new_pipes[0], options); + } + + // If required, send the routing id of the peer to the local socket. + if (options.recv_routing_id) { + send_routing_id (new_pipes[1], peer.options); + } + +#ifdef ZMQ_BUILD_DRAFT_API + // If set, send the hello msg of the local socket to the peer. + if (options.can_send_hello_msg && options.hello_msg.size () > 0) { + send_hello_msg (new_pipes[0], options); + } + + // If set, send the hello msg of the peer to the local socket. + if (peer.options.can_send_hello_msg + && peer.options.hello_msg.size () > 0) { + send_hello_msg (new_pipes[1], peer.options); + } + + if (peer.options.can_recv_disconnect_msg + && peer.options.disconnect_msg.size () > 0) + new_pipes[0]->set_disconnect_msg (peer.options.disconnect_msg); +#endif + + // Attach remote end of the pipe to the peer socket. Note that peer's + // seqnum was incremented in find_endpoint function. We don't need it + // increased here. + send_bind (peer.socket, new_pipes[1], false); + } + + // Attach local end of the pipe to this socket object. + attach_pipe (new_pipes[0], false, true); + + // Save last endpoint URI + _last_endpoint.assign (endpoint_uri_); + + // remember inproc connections for disconnect + _inprocs.emplace (endpoint_uri_, new_pipes[0]); + + options.connected = true; + return 0; + } + const bool is_single_connect = + (options.type == ZMQ_DEALER || options.type == ZMQ_SUB + || options.type == ZMQ_PUB || options.type == ZMQ_REQ); + if (unlikely (is_single_connect)) { + if (0 != _endpoints.count (endpoint_uri_)) { + // There is no valid use for multiple connects for SUB-PUB nor + // DEALER-ROUTER nor REQ-REP. Multiple connects produces + // nonsensical results. + return 0; + } + } + + // Choose the I/O thread to run the session in. + io_thread_t *io_thread = choose_io_thread (options.affinity); + if (!io_thread) { + errno = EMTHREAD; + return -1; + } + + address_t *paddr = + new (std::nothrow) address_t (protocol, address, this->get_ctx ()); + alloc_assert (paddr); + + // Resolve address (if needed by the protocol) + if (protocol == protocol_name::tcp) { + // Do some basic sanity checks on tcp:// address syntax + // - hostname starts with digit or letter, with embedded '-' or '.' + // - IPv6 address may contain hex chars and colons. + // - IPv6 link local address may contain % followed by interface name / zone_id + // (Reference: https://tools.ietf.org/html/rfc4007) + // - IPv4 address may contain decimal digits and dots. + // - Address must end in ":port" where port is *, or numeric + // - Address may contain two parts separated by ':' + // Following code is quick and dirty check to catch obvious errors, + // without trying to be fully accurate. + const char *check = address.c_str (); + if (isalnum (*check) || isxdigit (*check) || *check == '[' + || *check == ':') { + check++; + while (isalnum (*check) || isxdigit (*check) || *check == '.' + || *check == '-' || *check == ':' || *check == '%' + || *check == ';' || *check == '[' || *check == ']' + || *check == '_' || *check == '*') { + check++; + } + } + // Assume the worst, now look for success + rc = -1; + // Did we reach the end of the address safely? + if (*check == 0) { + // Do we have a valid port string? (cannot be '*' in connect + check = strrchr (address.c_str (), ':'); + if (check) { + check++; + if (*check && (isdigit (*check))) + rc = 0; // Valid + } + } + if (rc == -1) { + errno = EINVAL; + LIBZMQ_DELETE (paddr); + return -1; + } + // Defer resolution until a socket is opened + paddr->resolved.tcp_addr = NULL; + } +#ifdef ZMQ_HAVE_WS +#ifdef ZMQ_HAVE_WSS + else if (protocol == protocol_name::ws || protocol == protocol_name::wss) { + if (protocol == protocol_name::wss) { + paddr->resolved.wss_addr = new (std::nothrow) wss_address_t (); + alloc_assert (paddr->resolved.wss_addr); + rc = paddr->resolved.wss_addr->resolve (address.c_str (), false, + options.ipv6); + } else +#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, + options.ipv6); + } + + if (rc != 0) { + LIBZMQ_DELETE (paddr); + return -1; + } + } +#endif + +#if defined ZMQ_HAVE_IPC + else if (protocol == protocol_name::ipc) { + paddr->resolved.ipc_addr = new (std::nothrow) ipc_address_t (); + alloc_assert (paddr->resolved.ipc_addr); + int rc = paddr->resolved.ipc_addr->resolve (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (paddr); + return -1; + } + } +#endif + + if (protocol == protocol_name::udp) { + if (options.type != ZMQ_RADIO) { + errno = ENOCOMPATPROTO; + LIBZMQ_DELETE (paddr); + return -1; + } + + paddr->resolved.udp_addr = new (std::nothrow) udp_address_t (); + alloc_assert (paddr->resolved.udp_addr); + rc = paddr->resolved.udp_addr->resolve (address.c_str (), false, + options.ipv6); + if (rc != 0) { + LIBZMQ_DELETE (paddr); + return -1; + } + } + + // TBD - Should we check address for ZMQ_HAVE_NORM??? + +#ifdef ZMQ_HAVE_OPENPGM + if (protocol == protocol_name::pgm || protocol == protocol_name::epgm) { + struct pgm_addrinfo_t *res = NULL; + uint16_t port_number = 0; + int rc = + pgm_socket_t::init_address (address.c_str (), &res, &port_number); + if (res != NULL) + pgm_freeaddrinfo (res); + if (rc != 0 || port_number == 0) { + return -1; + } + } +#endif +#if defined ZMQ_HAVE_TIPC + else if (protocol == protocol_name::tipc) { + paddr->resolved.tipc_addr = new (std::nothrow) tipc_address_t (); + alloc_assert (paddr->resolved.tipc_addr); + int rc = paddr->resolved.tipc_addr->resolve (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (paddr); + return -1; + } + const sockaddr_tipc *const saddr = + reinterpret_cast ( + paddr->resolved.tipc_addr->addr ()); + // Cannot connect to random Port Identity + if (saddr->addrtype == TIPC_ADDR_ID + && paddr->resolved.tipc_addr->is_random ()) { + LIBZMQ_DELETE (paddr); + errno = EINVAL; + return -1; + } + } +#endif +#if defined ZMQ_HAVE_VMCI + else if (protocol == protocol_name::vmci) { + paddr->resolved.vmci_addr = + new (std::nothrow) vmci_address_t (this->get_ctx ()); + alloc_assert (paddr->resolved.vmci_addr); + int rc = paddr->resolved.vmci_addr->resolve (address.c_str ()); + if (rc != 0) { + LIBZMQ_DELETE (paddr); + return -1; + } + } +#endif + + // Create session. + session_base_t *session = + session_base_t::create (io_thread, true, this, options, paddr); + errno_assert (session); + + // PGM does not support subscription forwarding; ask for all data to be + // sent to this pipe. (same for NORM, currently?) +#if defined ZMQ_HAVE_OPENPGM && defined ZMQ_HAVE_NORM + const bool subscribe_to_all = + protocol == protocol_name::pgm || protocol == protocol_name::epgm + || protocol == protocol_name::norm || protocol == protocol_name::udp; +#elif defined ZMQ_HAVE_OPENPGM + const bool subscribe_to_all = protocol == protocol_name::pgm + || protocol == protocol_name::epgm + || protocol == protocol_name::udp; +#elif defined ZMQ_HAVE_NORM + const bool subscribe_to_all = + protocol == protocol_name::norm || protocol == protocol_name::udp; +#else + const bool subscribe_to_all = protocol == protocol_name::udp; +#endif + pipe_t *newpipe = NULL; + + if (options.immediate != 1 || subscribe_to_all) { + // Create a bi-directional pipe. + object_t *parents[2] = {this, session}; + pipe_t *new_pipes[2] = {NULL, NULL}; + + const bool conflate = get_effective_conflate_option (options); + + int hwms[2] = {conflate ? -1 : options.sndhwm, + conflate ? -1 : options.rcvhwm}; + bool conflates[2] = {conflate, conflate}; + rc = pipepair (parents, new_pipes, hwms, conflates); + errno_assert (rc == 0); + + // Attach local end of the pipe to the socket object. + attach_pipe (new_pipes[0], subscribe_to_all, true); + newpipe = new_pipes[0]; + + // Attach remote end of the pipe to the session object later on. + session->attach_pipe (new_pipes[1]); + } + + // Save last endpoint URI + paddr->to_string (_last_endpoint); + + add_endpoint (make_unconnected_connect_endpoint_pair (endpoint_uri_), + static_cast (session), newpipe); + return 0; +} + +std::string +zmq::socket_base_t::resolve_tcp_addr (std::string endpoint_uri_pair_, + const char *tcp_address_) +{ + // The resolved last_endpoint is used as a key in the endpoints map. + // The address passed by the user might not match in the TCP case due to + // IPv4-in-IPv6 mapping (EG: tcp://[::ffff:127.0.0.1]:9999), so try to + // resolve before giving up. Given at this stage we don't know whether a + // socket is connected or bound, try with both. + if (_endpoints.find (endpoint_uri_pair_) == _endpoints.end ()) { + tcp_address_t *tcp_addr = new (std::nothrow) tcp_address_t (); + alloc_assert (tcp_addr); + int rc = tcp_addr->resolve (tcp_address_, false, options.ipv6); + + if (rc == 0) { + tcp_addr->to_string (endpoint_uri_pair_); + if (_endpoints.find (endpoint_uri_pair_) == _endpoints.end ()) { + rc = tcp_addr->resolve (tcp_address_, true, options.ipv6); + if (rc == 0) { + tcp_addr->to_string (endpoint_uri_pair_); + } + } + } + LIBZMQ_DELETE (tcp_addr); + } + return endpoint_uri_pair_; +} + +void zmq::socket_base_t::add_endpoint ( + const endpoint_uri_pair_t &endpoint_pair_, own_t *endpoint_, pipe_t *pipe_) +{ + // Activate the session. Make it a child of this socket. + launch_child (endpoint_); + _endpoints.ZMQ_MAP_INSERT_OR_EMPLACE (endpoint_pair_.identifier (), + endpoint_pipe_t (endpoint_, pipe_)); + + if (pipe_ != NULL) + pipe_->set_endpoint_pair (endpoint_pair_); +} + +int zmq::socket_base_t::term_endpoint (const char *endpoint_uri_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + // Check whether the context hasn't been shut down yet. + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + // Check whether endpoint address passed to the function is valid. + if (unlikely (!endpoint_uri_)) { + errno = EINVAL; + return -1; + } + + // Process pending commands, if any, since there could be pending unprocessed process_own()'s + // (from launch_child() for example) we're asked to terminate now. + const int rc = process_commands (0, false); + if (unlikely (rc != 0)) { + return -1; + } + + // Parse endpoint_uri_ string. + std::string uri_protocol; + std::string uri_path; + if (parse_uri (endpoint_uri_, uri_protocol, uri_path) + || check_protocol (uri_protocol)) { + return -1; + } + + const std::string endpoint_uri_str = std::string (endpoint_uri_); + + // Disconnect an inproc socket + if (uri_protocol == protocol_name::inproc) { + return unregister_endpoint (endpoint_uri_str, this) == 0 + ? 0 + : _inprocs.erase_pipes (endpoint_uri_str); + } + + const std::string resolved_endpoint_uri = + uri_protocol == protocol_name::tcp + ? resolve_tcp_addr (endpoint_uri_str, uri_path.c_str ()) + : endpoint_uri_str; + + // Find the endpoints range (if any) corresponding to the endpoint_uri_pair_ string. + const std::pair range = + _endpoints.equal_range (resolved_endpoint_uri); + if (range.first == range.second) { + errno = ENOENT; + return -1; + } + + for (endpoints_t::iterator it = range.first; it != range.second; ++it) { + // If we have an associated pipe, terminate it. + if (it->second.second != NULL) + it->second.second->terminate (false); + term_child (it->second.first); + } + _endpoints.erase (range.first, range.second); + + if (options.reconnect_stop & ZMQ_RECONNECT_STOP_AFTER_DISCONNECT) { + _disconnected = true; + } + + return 0; +} + +int zmq::socket_base_t::send (msg_t *msg_, int flags_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + // Check whether the context hasn't been shut down yet. + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + // Check whether message passed to the function is valid. + if (unlikely (!msg_ || !msg_->check ())) { + errno = EFAULT; + return -1; + } + + // Process pending commands, if any. + int rc = process_commands (0, true); + if (unlikely (rc != 0)) { + return -1; + } + + // Clear any user-visible flags that are set on the message. + msg_->reset_flags (msg_t::more); + + // At this point we impose the flags on the message. + if (flags_ & ZMQ_SNDMORE) + msg_->set_flags (msg_t::more); + + msg_->reset_metadata (); + + // Try to send the message using method in each socket class + rc = xsend (msg_); + if (rc == 0) { + return 0; + } + // Special case for ZMQ_PUSH: -2 means pipe is dead while a + // multi-part send is in progress and can't be recovered, so drop + // silently when in blocking mode to keep backward compatibility. + if (unlikely (rc == -2)) { + if (!((flags_ & ZMQ_DONTWAIT) || options.sndtimeo == 0)) { + rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + return 0; + } + } + if (unlikely (errno != EAGAIN)) { + 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) { + return -1; + } + + // Compute the time when the timeout should occur. + // If the timeout is infinite, don't care. + int timeout = options.sndtimeo; + const uint64_t end = timeout < 0 ? 0 : (_clock.now_ms () + timeout); + + // Oops, we couldn't send the message. Wait for the next + // command, process it and try to send the message again. + // If timeout is reached in the meantime, return EAGAIN. + while (true) { + if (unlikely (process_commands (timeout, false) != 0)) { + return -1; + } + rc = xsend (msg_); + if (rc == 0) + break; + if (unlikely (errno != EAGAIN)) { + return -1; + } + if (timeout > 0) { + timeout = static_cast (end - _clock.now_ms ()); + if (timeout <= 0) { + errno = EAGAIN; + return -1; + } + } + } + + return 0; +} + +int zmq::socket_base_t::recv (msg_t *msg_, int flags_) +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + // Check whether the context hasn't been shut down yet. + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + // Check whether message passed to the function is valid. + if (unlikely (!msg_ || !msg_->check ())) { + errno = EFAULT; + return -1; + } + + // Once every inbound_poll_rate messages check for signals and process + // incoming commands. This happens only if we are not polling altogether + // because there are messages available all the time. If poll occurs, + // ticks is set to zero and thus we avoid this code. + // + // Note that 'recv' uses different command throttling algorithm (the one + // described above) from the one used by 'send'. This is because counting + // ticks is more efficient than doing RDTSC all the time. + if (++_ticks == inbound_poll_rate) { + if (unlikely (process_commands (0, false) != 0)) { + return -1; + } + _ticks = 0; + } + + // Get the message. + int rc = xrecv (msg_); + if (unlikely (rc != 0 && errno != EAGAIN)) { + return -1; + } + + // If we have the message, return immediately. + if (rc == 0) { + extract_flags (msg_); + return 0; + } + + // If the message cannot be fetched immediately, there are two scenarios. + // For non-blocking recv, commands are processed in case there's an + // activate_reader command already waiting in a command pipe. + // If it's not, return EAGAIN. + if ((flags_ & ZMQ_DONTWAIT) || options.rcvtimeo == 0) { + if (unlikely (process_commands (0, false) != 0)) { + return -1; + } + _ticks = 0; + + rc = xrecv (msg_); + if (rc < 0) { + return rc; + } + extract_flags (msg_); + + return 0; + } + + // Compute the time when the timeout should occur. + // If the timeout is infinite, don't care. + int timeout = options.rcvtimeo; + const uint64_t end = timeout < 0 ? 0 : (_clock.now_ms () + timeout); + + // In blocking scenario, commands are processed over and over again until + // we are able to fetch a message. + bool block = (_ticks != 0); + while (true) { + if (unlikely (process_commands (block ? timeout : 0, false) != 0)) { + return -1; + } + rc = xrecv (msg_); + if (rc == 0) { + _ticks = 0; + break; + } + if (unlikely (errno != EAGAIN)) { + return -1; + } + block = true; + if (timeout > 0) { + timeout = static_cast (end - _clock.now_ms ()); + if (timeout <= 0) { + errno = EAGAIN; + return -1; + } + } + } + + extract_flags (msg_); + return 0; +} + +int zmq::socket_base_t::close () +{ + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + // Remove all existing signalers for thread safe sockets + if (_thread_safe) + (static_cast (_mailbox))->clear_signalers (); + + // Mark the socket as dead + _tag = 0xdeadbeef; + + + // Transfer the ownership of the socket from this application thread + // to the reaper thread which will take care of the rest of shutdown + // process. + send_reap (this); + + return 0; +} + +bool zmq::socket_base_t::has_in () +{ + return xhas_in (); +} + +bool zmq::socket_base_t::has_out () +{ + return xhas_out (); +} + +void zmq::socket_base_t::start_reaping (poller_t *poller_) +{ + // Plug the socket to the reaper thread. + _poller = poller_; + + fd_t fd; + + if (!_thread_safe) + fd = (static_cast (_mailbox))->get_fd (); + else { + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + _reaper_signaler = new (std::nothrow) signaler_t (); + zmq_assert (_reaper_signaler); + + // Add signaler to the safe mailbox + fd = _reaper_signaler->get_fd (); + (static_cast (_mailbox)) + ->add_signaler (_reaper_signaler); + + // Send a signal to make sure reaper handle existing commands + _reaper_signaler->send (); + } + + _handle = _poller->add_fd (fd, this); + _poller->set_pollin (_handle); + + // Initialise the termination and check whether it can be deallocated + // immediately. + terminate (); + check_destroy (); +} + +int zmq::socket_base_t::process_commands (int timeout_, bool throttle_) +{ + if (timeout_ == 0) { + // If we are asked not to wait, check whether we haven't processed + // commands recently, so that we can throttle the new commands. + + // Get the CPU's tick counter. If 0, the counter is not available. + const uint64_t tsc = zmq::clock_t::rdtsc (); + + // Optimised version of command processing - it doesn't have to check + // for incoming commands each time. It does so only if certain time + // elapsed since last command processing. Command delay varies + // depending on CPU speed: It's ~1ms on 3GHz CPU, ~2ms on 1.5GHz CPU + // etc. The optimisation makes sense only on platforms where getting + // a timestamp is a very cheap operation (tens of nanoseconds). + if (tsc && throttle_) { + // Check whether TSC haven't jumped backwards (in case of migration + // between CPU cores) and whether certain time have elapsed since + // last command processing. If it didn't do nothing. + if (tsc >= _last_tsc && tsc - _last_tsc <= max_command_delay) + return 0; + _last_tsc = tsc; + } + } + + // Check whether there are any commands pending for this thread. + command_t cmd; + int rc = _mailbox->recv (&cmd, timeout_); + + // Process all available commands. + while (rc == 0) { + cmd.destination->process_command (cmd); + rc = _mailbox->recv (&cmd, 0); + } + + if (errno == EINTR) + return -1; + + zmq_assert (errno == EAGAIN); + + if (_ctx_terminated) { + errno = ETERM; + return -1; + } + + return 0; +} + +void zmq::socket_base_t::process_stop () +{ + // Here, someone have called zmq_ctx_term while the socket was still alive. + // We'll remember the fact so that any blocking call is interrupted and any + // further attempt to use the socket will return ETERM. The user is still + // responsible for calling zmq_close on the socket though! + scoped_lock_t lock (_monitor_sync); + stop_monitor (); + + _ctx_terminated = true; +} + +void zmq::socket_base_t::process_bind (pipe_t *pipe_) +{ + attach_pipe (pipe_); +} + +void zmq::socket_base_t::process_term (int linger_) +{ + // Unregister all inproc endpoints associated with this socket. + // Doing this we make sure that no new pipes from other sockets (inproc) + // will be initiated. + unregister_endpoints (this); + + // Ask all attached pipes to terminate. + for (pipes_t::size_type i = 0, size = _pipes.size (); i != size; ++i) { + // Only inprocs might have a disconnect message set + _pipes[i]->send_disconnect_msg (); + _pipes[i]->terminate (false); + } + register_term_acks (static_cast (_pipes.size ())); + + // Continue the termination process immediately. + own_t::process_term (linger_); +} + +void zmq::socket_base_t::process_term_endpoint (std::string *endpoint_) +{ + term_endpoint (endpoint_->c_str ()); + delete endpoint_; +} + +void zmq::socket_base_t::process_pipe_stats_publish ( + uint64_t outbound_queue_count_, + uint64_t inbound_queue_count_, + endpoint_uri_pair_t *endpoint_pair_) +{ + uint64_t values[2] = {outbound_queue_count_, inbound_queue_count_}; + event (*endpoint_pair_, values, 2, ZMQ_EVENT_PIPES_STATS); + delete endpoint_pair_; +} + +/* + * There are 2 pipes per connection, and the inbound one _must_ be queried from + * the I/O thread. So ask the outbound pipe, in the application thread, to send + * a message (pipe_peer_stats) to its peer. The message will carry the outbound + * pipe stats and endpoint, and the reference to the socket object. + * The inbound pipe on the I/O thread will then add its own stats and endpoint, + * and write back a message to the socket object (pipe_stats_publish) which + * will raise an event with the data. + */ +int zmq::socket_base_t::query_pipes_stats () +{ + { + scoped_lock_t lock (_monitor_sync); + if (!(_monitor_events & ZMQ_EVENT_PIPES_STATS)) { + errno = EINVAL; + return -1; + } + } + if (_pipes.size () == 0) { + errno = EAGAIN; + return -1; + } + for (pipes_t::size_type i = 0, size = _pipes.size (); i != size; ++i) { + _pipes[i]->send_stats_to_peer (this); + } + + return 0; +} + +void zmq::socket_base_t::update_pipe_options (int option_) +{ + if (option_ == ZMQ_SNDHWM || option_ == ZMQ_RCVHWM) { + for (pipes_t::size_type i = 0, size = _pipes.size (); i != size; ++i) { + _pipes[i]->set_hwms (options.rcvhwm, options.sndhwm); + _pipes[i]->send_hwms_to_peer (options.sndhwm, options.rcvhwm); + } + } +} + +void zmq::socket_base_t::process_destroy () +{ + _destroyed = true; +} + +int zmq::socket_base_t::xsetsockopt (int, const void *, size_t) +{ + errno = EINVAL; + return -1; +} + +bool zmq::socket_base_t::xhas_out () +{ + return false; +} + +int zmq::socket_base_t::xsend (msg_t *) +{ + errno = ENOTSUP; + return -1; +} + +bool zmq::socket_base_t::xhas_in () +{ + return false; +} + +int zmq::socket_base_t::xjoin (const char *group_) +{ + LIBZMQ_UNUSED (group_); + errno = ENOTSUP; + return -1; +} + +int zmq::socket_base_t::xleave (const char *group_) +{ + LIBZMQ_UNUSED (group_); + errno = ENOTSUP; + return -1; +} + +int zmq::socket_base_t::xrecv (msg_t *) +{ + errno = ENOTSUP; + return -1; +} + +void zmq::socket_base_t::xread_activated (pipe_t *) +{ + zmq_assert (false); +} +void zmq::socket_base_t::xwrite_activated (pipe_t *) +{ + zmq_assert (false); +} + +void zmq::socket_base_t::xhiccuped (pipe_t *) +{ + zmq_assert (false); +} + +void zmq::socket_base_t::in_event () +{ + // This function is invoked only once the socket is running in the context + // of the reaper thread. Process any commands from other threads/sockets + // that may be available at the moment. Ultimately, the socket will + // be destroyed. + { + scoped_optional_lock_t sync_lock (_thread_safe ? &_sync : NULL); + + // If the socket is thread safe we need to unsignal the reaper signaler + if (_thread_safe) + _reaper_signaler->recv (); + + process_commands (0, false); + } + check_destroy (); +} + +void zmq::socket_base_t::out_event () +{ + zmq_assert (false); +} + +void zmq::socket_base_t::timer_event (int) +{ + zmq_assert (false); +} + +void zmq::socket_base_t::check_destroy () +{ + // If the object was already marked as destroyed, finish the deallocation. + if (_destroyed) { + // Remove the socket from the reaper's poller. + _poller->rm_fd (_handle); + + // Remove the socket from the context. + destroy_socket (this); + + // Notify the reaper about the fact. + send_reaped (); + + // Deallocate. + own_t::process_destroy (); + } +} + +void zmq::socket_base_t::read_activated (pipe_t *pipe_) +{ + xread_activated (pipe_); +} + +void zmq::socket_base_t::write_activated (pipe_t *pipe_) +{ + xwrite_activated (pipe_); +} + +void zmq::socket_base_t::hiccuped (pipe_t *pipe_) +{ + if (options.immediate == 1) + pipe_->terminate (false); + else + // Notify derived sockets of the hiccup + xhiccuped (pipe_); +} + +void zmq::socket_base_t::pipe_terminated (pipe_t *pipe_) +{ + // Notify the specific socket type about the pipe termination. + xpipe_terminated (pipe_); + + // Remove pipe from inproc pipes + _inprocs.erase_pipe (pipe_); + + // Remove the pipe from the list of attached pipes and confirm its + // termination if we are already shutting down. + _pipes.erase (pipe_); + + // Remove the pipe from _endpoints (set it to NULL). + const std::string &identifier = pipe_->get_endpoint_pair ().identifier (); + if (!identifier.empty ()) { + std::pair range; + range = _endpoints.equal_range (identifier); + + for (endpoints_t::iterator it = range.first; it != range.second; ++it) { + if (it->second.second == pipe_) { + it->second.second = NULL; + break; + } + } + } + + if (is_terminating ()) + unregister_term_ack (); +} + +void zmq::socket_base_t::extract_flags (const msg_t *msg_) +{ + // Test whether routing_id flag is valid for this socket type. + if (unlikely (msg_->flags () & msg_t::routing_id)) + zmq_assert (options.recv_routing_id); + + // Remove MORE flag. + _rcvmore = (msg_->flags () & msg_t::more) != 0; +} + +int zmq::socket_base_t::monitor (const char *endpoint_, + uint64_t events_, + int event_version_, + int type_) +{ + scoped_lock_t lock (_monitor_sync); + + if (unlikely (_ctx_terminated)) { + errno = ETERM; + return -1; + } + + // Event version 1 supports only first 16 events. + if (unlikely (event_version_ == 1 && events_ >> 16 != 0)) { + errno = EINVAL; + return -1; + } + + // Support deregistering monitoring endpoints as well + if (endpoint_ == NULL) { + stop_monitor (); + return 0; + } + // Parse endpoint_uri_ string. + std::string protocol; + std::string address; + if (parse_uri (endpoint_, protocol, address) || check_protocol (protocol)) + return -1; + + // Event notification only supported over inproc:// + if (protocol != protocol_name::inproc) { + errno = EPROTONOSUPPORT; + return -1; + } + + // already monitoring. Stop previous monitor before starting new one. + if (_monitor_socket != NULL) { + stop_monitor (true); + } + + // Check if the specified socket type is supported. It must be a + // one-way socket types that support the SNDMORE flag. + switch (type_) { + case ZMQ_PAIR: + break; + case ZMQ_PUB: + break; + case ZMQ_PUSH: + break; + default: + errno = EINVAL; + return -1; + } + + // Register events to monitor + _monitor_events = events_; + options.monitor_event_version = event_version_; + // Create a monitor socket of the specified type. + _monitor_socket = zmq_socket (get_ctx (), type_); + if (_monitor_socket == NULL) + return -1; + + // Never block context termination on pending event messages + int linger = 0; + int rc = + zmq_setsockopt (_monitor_socket, ZMQ_LINGER, &linger, sizeof (linger)); + if (rc == -1) + stop_monitor (false); + + // Spawn the monitor socket endpoint + rc = zmq_bind (_monitor_socket, endpoint_); + if (rc == -1) + stop_monitor (false); + return rc; +} + +void zmq::socket_base_t::event_connected ( + const endpoint_uri_pair_t &endpoint_uri_pair_, zmq::fd_t fd_) +{ + uint64_t values[1] = {static_cast (fd_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_CONNECTED); +} + +void zmq::socket_base_t::event_connect_delayed ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_CONNECT_DELAYED); +} + +void zmq::socket_base_t::event_connect_retried ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int interval_) +{ + uint64_t values[1] = {static_cast (interval_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_CONNECT_RETRIED); +} + +void zmq::socket_base_t::event_listening ( + const endpoint_uri_pair_t &endpoint_uri_pair_, zmq::fd_t fd_) +{ + uint64_t values[1] = {static_cast (fd_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_LISTENING); +} + +void zmq::socket_base_t::event_bind_failed ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_BIND_FAILED); +} + +void zmq::socket_base_t::event_accepted ( + const endpoint_uri_pair_t &endpoint_uri_pair_, zmq::fd_t fd_) +{ + uint64_t values[1] = {static_cast (fd_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_ACCEPTED); +} + +void zmq::socket_base_t::event_accept_failed ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_ACCEPT_FAILED); +} + +void zmq::socket_base_t::event_closed ( + const endpoint_uri_pair_t &endpoint_uri_pair_, zmq::fd_t fd_) +{ + uint64_t values[1] = {static_cast (fd_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_CLOSED); +} + +void zmq::socket_base_t::event_close_failed ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_CLOSE_FAILED); +} + +void zmq::socket_base_t::event_disconnected ( + const endpoint_uri_pair_t &endpoint_uri_pair_, zmq::fd_t fd_) +{ + uint64_t values[1] = {static_cast (fd_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_DISCONNECTED); +} + +void zmq::socket_base_t::event_handshake_failed_no_detail ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL); +} + +void zmq::socket_base_t::event_handshake_failed_protocol ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL); +} + +void zmq::socket_base_t::event_handshake_failed_auth ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_HANDSHAKE_FAILED_AUTH); +} + +void zmq::socket_base_t::event_handshake_succeeded ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_) +{ + uint64_t values[1] = {static_cast (err_)}; + event (endpoint_uri_pair_, values, 1, ZMQ_EVENT_HANDSHAKE_SUCCEEDED); +} + +void zmq::socket_base_t::event (const endpoint_uri_pair_t &endpoint_uri_pair_, + uint64_t values_[], + uint64_t values_count_, + uint64_t type_) +{ + scoped_lock_t lock (_monitor_sync); + if (_monitor_events & type_) { + monitor_event (type_, values_, values_count_, endpoint_uri_pair_); + } +} + +// Send a monitor event +void zmq::socket_base_t::monitor_event ( + uint64_t event_, + const uint64_t values_[], + uint64_t values_count_, + const endpoint_uri_pair_t &endpoint_uri_pair_) const +{ + // this is a private method which is only called from + // contexts where the _monitor_sync mutex has been locked before + + if (_monitor_socket) { + zmq_msg_t msg; + + switch (options.monitor_event_version) { + case 1: { + // The API should not allow to activate unsupported events + zmq_assert (event_ <= std::numeric_limits::max ()); + // v1 only allows one value + zmq_assert (values_count_ == 1); + zmq_assert (values_[0] + <= std::numeric_limits::max ()); + + // Send event and value in first frame + const uint16_t event = static_cast (event_); + const uint32_t value = static_cast (values_[0]); + zmq_msg_init_size (&msg, sizeof (event) + sizeof (value)); + uint8_t *data = static_cast (zmq_msg_data (&msg)); + // Avoid dereferencing uint32_t on unaligned address + memcpy (data + 0, &event, sizeof (event)); + memcpy (data + sizeof (event), &value, sizeof (value)); + zmq_msg_send (&msg, _monitor_socket, ZMQ_SNDMORE); + + const std::string &endpoint_uri = + endpoint_uri_pair_.identifier (); + + // Send address in second frame + zmq_msg_init_size (&msg, endpoint_uri.size ()); + memcpy (zmq_msg_data (&msg), endpoint_uri.c_str (), + endpoint_uri.size ()); + zmq_msg_send (&msg, _monitor_socket, 0); + } break; + case 2: { + // Send event in first frame (64bit unsigned) + zmq_msg_init_size (&msg, sizeof (event_)); + memcpy (zmq_msg_data (&msg), &event_, sizeof (event_)); + zmq_msg_send (&msg, _monitor_socket, ZMQ_SNDMORE); + + // Send number of values that will follow in second frame + zmq_msg_init_size (&msg, sizeof (values_count_)); + memcpy (zmq_msg_data (&msg), &values_count_, + sizeof (values_count_)); + zmq_msg_send (&msg, _monitor_socket, ZMQ_SNDMORE); + + // Send values in third-Nth frames (64bit unsigned) + for (uint64_t i = 0; i < values_count_; ++i) { + zmq_msg_init_size (&msg, sizeof (values_[i])); + memcpy (zmq_msg_data (&msg), &values_[i], + sizeof (values_[i])); + zmq_msg_send (&msg, _monitor_socket, ZMQ_SNDMORE); + } + + // Send local endpoint URI in second-to-last frame (string) + zmq_msg_init_size (&msg, endpoint_uri_pair_.local.size ()); + memcpy (zmq_msg_data (&msg), endpoint_uri_pair_.local.c_str (), + endpoint_uri_pair_.local.size ()); + zmq_msg_send (&msg, _monitor_socket, ZMQ_SNDMORE); + + // Send remote endpoint URI in last frame (string) + zmq_msg_init_size (&msg, endpoint_uri_pair_.remote.size ()); + memcpy (zmq_msg_data (&msg), endpoint_uri_pair_.remote.c_str (), + endpoint_uri_pair_.remote.size ()); + zmq_msg_send (&msg, _monitor_socket, 0); + } break; + } + } +} + +void zmq::socket_base_t::stop_monitor (bool send_monitor_stopped_event_) +{ + // this is a private method which is only called from + // contexts where the _monitor_sync mutex has been locked before + + if (_monitor_socket) { + if ((_monitor_events & ZMQ_EVENT_MONITOR_STOPPED) + && send_monitor_stopped_event_) { + uint64_t values[1] = {0}; + monitor_event (ZMQ_EVENT_MONITOR_STOPPED, values, 1, + endpoint_uri_pair_t ()); + } + zmq_close (_monitor_socket); + _monitor_socket = NULL; + _monitor_events = 0; + } +} + +bool zmq::socket_base_t::is_disconnected () const +{ + return _disconnected; +} + +zmq::routing_socket_base_t::routing_socket_base_t (class ctx_t *parent_, + uint32_t tid_, + int sid_) : + socket_base_t (parent_, tid_, sid_) +{ +} + +zmq::routing_socket_base_t::~routing_socket_base_t () +{ + zmq_assert (_out_pipes.empty ()); +} + +int zmq::routing_socket_base_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + switch (option_) { + case ZMQ_CONNECT_ROUTING_ID: + // TODO why isn't it possible to set an empty connect_routing_id + // (which is the default value) + if (optval_ && optvallen_) { + _connect_routing_id.assign (static_cast (optval_), + optvallen_); + return 0; + } + break; + } + errno = EINVAL; + return -1; +} + +void zmq::routing_socket_base_t::xwrite_activated (pipe_t *pipe_) +{ + const out_pipes_t::iterator end = _out_pipes.end (); + out_pipes_t::iterator it; + for (it = _out_pipes.begin (); it != end; ++it) + if (it->second.pipe == pipe_) + break; + + zmq_assert (it != end); + zmq_assert (!it->second.active); + it->second.active = true; +} + +std::string zmq::routing_socket_base_t::extract_connect_routing_id () +{ + std::string res = ZMQ_MOVE (_connect_routing_id); + _connect_routing_id.clear (); + return res; +} + +bool zmq::routing_socket_base_t::connect_routing_id_is_set () const +{ + return !_connect_routing_id.empty (); +} + +void zmq::routing_socket_base_t::add_out_pipe (blob_t routing_id_, + pipe_t *pipe_) +{ + // Add the record into output pipes lookup table + const out_pipe_t outpipe = {pipe_, true}; + const bool ok = + _out_pipes.ZMQ_MAP_INSERT_OR_EMPLACE (ZMQ_MOVE (routing_id_), outpipe) + .second; + zmq_assert (ok); +} + +bool zmq::routing_socket_base_t::has_out_pipe (const blob_t &routing_id_) const +{ + return 0 != _out_pipes.count (routing_id_); +} + +zmq::routing_socket_base_t::out_pipe_t * +zmq::routing_socket_base_t::lookup_out_pipe (const blob_t &routing_id_) +{ + // TODO we could probably avoid constructor a temporary blob_t to call this function + out_pipes_t::iterator it = _out_pipes.find (routing_id_); + return it == _out_pipes.end () ? NULL : &it->second; +} + +const zmq::routing_socket_base_t::out_pipe_t * +zmq::routing_socket_base_t::lookup_out_pipe (const blob_t &routing_id_) const +{ + // TODO we could probably avoid constructor a temporary blob_t to call this function + const out_pipes_t::const_iterator it = _out_pipes.find (routing_id_); + return it == _out_pipes.end () ? NULL : &it->second; +} + +void zmq::routing_socket_base_t::erase_out_pipe (const pipe_t *pipe_) +{ + const size_t erased = _out_pipes.erase (pipe_->get_routing_id ()); + zmq_assert (erased); +} + +zmq::routing_socket_base_t::out_pipe_t +zmq::routing_socket_base_t::try_erase_out_pipe (const blob_t &routing_id_) +{ + const out_pipes_t::iterator it = _out_pipes.find (routing_id_); + out_pipe_t res = {NULL, false}; + if (it != _out_pipes.end ()) { + res = it->second; + _out_pipes.erase (it); + } + return res; +} diff --git a/3rd/libzmq/src/socket_base.hpp b/3rd/libzmq/src/socket_base.hpp new file mode 100644 index 00000000..92deb9f7 --- /dev/null +++ b/3rd/libzmq/src/socket_base.hpp @@ -0,0 +1,405 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SOCKET_BASE_HPP_INCLUDED__ +#define __ZMQ_SOCKET_BASE_HPP_INCLUDED__ + +#include +#include +#include + +#include "own.hpp" +#include "array.hpp" +#include "blob.hpp" +#include "stdint.hpp" +#include "poller.hpp" +#include "i_poll_events.hpp" +#include "i_mailbox.hpp" +#include "clock.hpp" +#include "pipe.hpp" +#include "endpoint.hpp" + +extern "C" { +void zmq_free_event (void *data_, void *hint_); +} + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; + +class socket_base_t : public own_t, + public array_item_t<>, + public i_poll_events, + public i_pipe_events +{ + friend class reaper_t; + + public: + // Returns false if object is not a socket. + bool check_tag () const; + + // Returns whether the socket is thread-safe. + bool is_thread_safe () const; + + // Create a socket of a specified type. + static socket_base_t * + create (int type_, zmq::ctx_t *parent_, uint32_t tid_, int sid_); + + // Returns the mailbox associated with this socket. + i_mailbox *get_mailbox () const; + + // Interrupt blocking call if the socket is stuck in one. + // This function can be called from a different thread! + void stop (); + + // Interface for communication with the API layer. + int setsockopt (int option_, const void *optval_, size_t optvallen_); + int getsockopt (int option_, void *optval_, size_t *optvallen_); + int bind (const char *endpoint_uri_); + int connect (const char *endpoint_uri_); + int term_endpoint (const char *endpoint_uri_); + int send (zmq::msg_t *msg_, int flags_); + int recv (zmq::msg_t *msg_, int flags_); + void add_signaler (signaler_t *s_); + void remove_signaler (signaler_t *s_); + int close (); + + // These functions are used by the polling mechanism to determine + // which events are to be reported from this socket. + bool has_in (); + bool has_out (); + + // Joining and leaving groups + int join (const char *group_); + int leave (const char *group_); + + // Using this function reaper thread ask the socket to register with + // its poller. + void start_reaping (poller_t *poller_); + + // i_poll_events implementation. This interface is used when socket + // is handled by the poller in the reaper thread. + void in_event () ZMQ_FINAL; + void out_event () ZMQ_FINAL; + void timer_event (int id_) ZMQ_FINAL; + + // i_pipe_events interface implementation. + void read_activated (pipe_t *pipe_) ZMQ_FINAL; + void write_activated (pipe_t *pipe_) ZMQ_FINAL; + void hiccuped (pipe_t *pipe_) ZMQ_FINAL; + void pipe_terminated (pipe_t *pipe_) ZMQ_FINAL; + void lock (); + void unlock (); + + int monitor (const char *endpoint_, + uint64_t events_, + int event_version_, + int type_); + + void event_connected (const endpoint_uri_pair_t &endpoint_uri_pair_, + zmq::fd_t fd_); + void event_connect_delayed (const endpoint_uri_pair_t &endpoint_uri_pair_, + int err_); + void event_connect_retried (const endpoint_uri_pair_t &endpoint_uri_pair_, + int interval_); + void event_listening (const endpoint_uri_pair_t &endpoint_uri_pair_, + zmq::fd_t fd_); + void event_bind_failed (const endpoint_uri_pair_t &endpoint_uri_pair_, + int err_); + void event_accepted (const endpoint_uri_pair_t &endpoint_uri_pair_, + zmq::fd_t fd_); + void event_accept_failed (const endpoint_uri_pair_t &endpoint_uri_pair_, + int err_); + void event_closed (const endpoint_uri_pair_t &endpoint_uri_pair_, + zmq::fd_t fd_); + void event_close_failed (const endpoint_uri_pair_t &endpoint_uri_pair_, + int err_); + void event_disconnected (const endpoint_uri_pair_t &endpoint_uri_pair_, + zmq::fd_t fd_); + void event_handshake_failed_no_detail ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_); + void event_handshake_failed_protocol ( + const endpoint_uri_pair_t &endpoint_uri_pair_, int err_); + void + event_handshake_failed_auth (const endpoint_uri_pair_t &endpoint_uri_pair_, + int err_); + void + event_handshake_succeeded (const endpoint_uri_pair_t &endpoint_uri_pair_, + int err_); + + // Query the state of a specific peer. The default implementation + // always returns an ENOTSUP error. + virtual int get_peer_state (const void *routing_id_, + size_t routing_id_size_) const; + + // Request for pipes statistics - will generate a ZMQ_EVENT_PIPES_STATS + // after gathering the data asynchronously. Requires event monitoring to + // be enabled. + int query_pipes_stats (); + + bool is_disconnected () const; + + protected: + socket_base_t (zmq::ctx_t *parent_, + uint32_t tid_, + int sid_, + bool thread_safe_ = false); + ~socket_base_t () ZMQ_OVERRIDE; + + // Concrete algorithms for the x- methods are to be defined by + // individual socket types. + virtual void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_ = false, + bool locally_initiated_ = false) = 0; + + // The default implementation assumes there are no specific socket + // options for the particular socket type. If not so, ZMQ_FINAL this + // method. + virtual int + xsetsockopt (int option_, const void *optval_, size_t optvallen_); + + // The default implementation assumes that send is not supported. + virtual bool xhas_out (); + virtual int xsend (zmq::msg_t *msg_); + + // The default implementation assumes that recv in not supported. + virtual bool xhas_in (); + virtual int xrecv (zmq::msg_t *msg_); + + // i_pipe_events will be forwarded to these functions. + virtual void xread_activated (pipe_t *pipe_); + virtual void xwrite_activated (pipe_t *pipe_); + virtual void xhiccuped (pipe_t *pipe_); + virtual void xpipe_terminated (pipe_t *pipe_) = 0; + + // the default implementation assumes that joub and leave are not supported. + virtual int xjoin (const char *group_); + virtual int xleave (const char *group_); + + // Delay actual destruction of the socket. + void process_destroy () ZMQ_FINAL; + + int connect_internal (const char *endpoint_uri_); + + // Mutex for synchronize access to the socket in thread safe mode + mutex_t _sync; + + private: + // test if event should be sent and then dispatch it + void event (const endpoint_uri_pair_t &endpoint_uri_pair_, + uint64_t values_[], + uint64_t values_count_, + uint64_t type_); + + // Socket event data dispatch + void monitor_event (uint64_t event_, + const uint64_t values_[], + uint64_t values_count_, + const endpoint_uri_pair_t &endpoint_uri_pair_) const; + + // Monitor socket cleanup + void stop_monitor (bool send_monitor_stopped_event_ = true); + + // Creates new endpoint ID and adds the endpoint to the map. + void add_endpoint (const endpoint_uri_pair_t &endpoint_pair_, + own_t *endpoint_, + pipe_t *pipe_); + + // Map of open endpoints. + typedef std::pair endpoint_pipe_t; + typedef std::multimap endpoints_t; + endpoints_t _endpoints; + + // Map of open inproc endpoints. + class inprocs_t + { + public: + void emplace (const char *endpoint_uri_, pipe_t *pipe_); + int erase_pipes (const std::string &endpoint_uri_str_); + void erase_pipe (const pipe_t *pipe_); + + private: + typedef std::multimap map_t; + map_t _inprocs; + }; + inprocs_t _inprocs; + + // To be called after processing commands or invoking any command + // handlers explicitly. If required, it will deallocate the socket. + void check_destroy (); + + // Moves the flags from the message to local variables, + // to be later retrieved by getsockopt. + void extract_flags (const msg_t *msg_); + + // Used to check whether the object is a socket. + uint32_t _tag; + + // If true, associated context was already terminated. + bool _ctx_terminated; + + // If true, object should have been already destroyed. However, + // destruction is delayed while we unwind the stack to the point + // where it doesn't intersect the object being destroyed. + bool _destroyed; + + // Parse URI string. + static int + parse_uri (const char *uri_, std::string &protocol_, std::string &path_); + + // Check whether transport protocol, as specified in connect or + // bind, is available and compatible with the socket type. + int check_protocol (const std::string &protocol_) const; + + // Register the pipe with this socket. + void attach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_ = false, + bool locally_initiated_ = false); + + // Processes commands sent to this socket (if any). If timeout is -1, + // returns only after at least one command was processed. + // If throttle argument is true, commands are processed at most once + // in a predefined time period. + int process_commands (int timeout_, bool throttle_); + + // Handlers for incoming commands. + void process_stop () ZMQ_FINAL; + void process_bind (zmq::pipe_t *pipe_) ZMQ_FINAL; + void + process_pipe_stats_publish (uint64_t outbound_queue_count_, + uint64_t inbound_queue_count_, + endpoint_uri_pair_t *endpoint_pair_) ZMQ_FINAL; + void process_term (int linger_) ZMQ_FINAL; + void process_term_endpoint (std::string *endpoint_) ZMQ_FINAL; + + void update_pipe_options (int option_); + + std::string resolve_tcp_addr (std::string endpoint_uri_, + const char *tcp_address_); + + // Socket's mailbox object. + i_mailbox *_mailbox; + + // List of attached pipes. + typedef array_t pipes_t; + pipes_t _pipes; + + // Reaper's poller and handle of this socket within it. + poller_t *_poller; + poller_t::handle_t _handle; + + // Timestamp of when commands were processed the last time. + uint64_t _last_tsc; + + // Number of messages received since last command processing. + int _ticks; + + // True if the last message received had MORE flag set. + bool _rcvmore; + + // Improves efficiency of time measurement. + clock_t _clock; + + // Monitor socket; + void *_monitor_socket; + + // Bitmask of events being monitored + int64_t _monitor_events; + + // Last socket endpoint resolved URI + std::string _last_endpoint; + + // Indicate if the socket is thread safe + const bool _thread_safe; + + // Signaler to be used in the reaping stage + signaler_t *_reaper_signaler; + + // Mutex to synchronize access to the monitor Pair socket + mutex_t _monitor_sync; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (socket_base_t) + + // Add a flag for mark disconnect action + bool _disconnected; +}; + +class routing_socket_base_t : public socket_base_t +{ + protected: + routing_socket_base_t (class ctx_t *parent_, uint32_t tid_, int sid_); + ~routing_socket_base_t () ZMQ_OVERRIDE; + + // methods from socket_base_t + int xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) ZMQ_OVERRIDE; + void xwrite_activated (pipe_t *pipe_) ZMQ_FINAL; + + // own methods + std::string extract_connect_routing_id (); + bool connect_routing_id_is_set () const; + + struct out_pipe_t + { + pipe_t *pipe; + bool active; + }; + + void add_out_pipe (blob_t routing_id_, pipe_t *pipe_); + bool has_out_pipe (const blob_t &routing_id_) const; + out_pipe_t *lookup_out_pipe (const blob_t &routing_id_); + const out_pipe_t *lookup_out_pipe (const blob_t &routing_id_) const; + void erase_out_pipe (const pipe_t *pipe_); + out_pipe_t try_erase_out_pipe (const blob_t &routing_id_); + template bool any_of_out_pipes (Func func_) + { + bool res = false; + for (out_pipes_t::iterator it = _out_pipes.begin (), + end = _out_pipes.end (); + it != end && !res; ++it) { + res |= func_ (*it->second.pipe); + } + + return res; + } + + private: + // Outbound pipes indexed by the peer IDs. + typedef std::map out_pipes_t; + out_pipes_t _out_pipes; + + // Next assigned name on a zmq_connect() call used by ROUTER and STREAM socket types + std::string _connect_routing_id; +}; +} + +#endif diff --git a/3rd/libzmq/src/socket_poller.cpp b/3rd/libzmq/src/socket_poller.cpp new file mode 100644 index 00000000..eecbf16b --- /dev/null +++ b/3rd/libzmq/src/socket_poller.cpp @@ -0,0 +1,695 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "socket_poller.hpp" +#include "err.hpp" +#include "polling_util.hpp" +#include "macros.hpp" + +#include + +static bool is_thread_safe (const zmq::socket_base_t &socket_) +{ + // do not use getsockopt here, since that would fail during context termination + return socket_.is_thread_safe (); +} + +// compare elements to value +template +static It find_if2 (It b_, It e_, const T &value, Pred pred) +{ + for (; b_ != e_; ++b_) { + if (pred (*b_, value)) { + break; + } + } + return b_; +} + +zmq::socket_poller_t::socket_poller_t () : + _tag (0xCAFEBABE), + _signaler (NULL) +#if defined ZMQ_POLL_BASED_ON_POLL + , + _pollfds (NULL) +#elif defined ZMQ_POLL_BASED_ON_SELECT + , + _max_fd (0) +#endif +{ + rebuild (); +} + +zmq::socket_poller_t::~socket_poller_t () +{ + // Mark the socket_poller as dead + _tag = 0xdeadbeef; + + for (items_t::iterator it = _items.begin (), end = _items.end (); it != end; + ++it) { + // TODO shouldn't this zmq_assert (it->socket->check_tag ()) instead? + if (it->socket && it->socket->check_tag () + && is_thread_safe (*it->socket)) { + it->socket->remove_signaler (_signaler); + } + } + + if (_signaler != NULL) { + LIBZMQ_DELETE (_signaler); + } + +#if defined ZMQ_POLL_BASED_ON_POLL + if (_pollfds) { + free (_pollfds); + _pollfds = NULL; + } +#endif +} + +bool zmq::socket_poller_t::check_tag () const +{ + return _tag == 0xCAFEBABE; +} + +int zmq::socket_poller_t::signaler_fd (fd_t *fd_) const +{ + if (_signaler) { + *fd_ = _signaler->get_fd (); + return 0; + } + // Only thread-safe socket types are guaranteed to have a signaler. + errno = EINVAL; + return -1; +} + +int zmq::socket_poller_t::add (socket_base_t *socket_, + void *user_data_, + short events_) +{ + if (find_if2 (_items.begin (), _items.end (), socket_, &is_socket) + != _items.end ()) { + errno = EINVAL; + return -1; + } + + if (is_thread_safe (*socket_)) { + if (_signaler == NULL) { + _signaler = new (std::nothrow) signaler_t (); + if (!_signaler) { + errno = ENOMEM; + return -1; + } + if (!_signaler->valid ()) { + delete _signaler; + _signaler = NULL; + errno = EMFILE; + return -1; + } + } + + socket_->add_signaler (_signaler); + } + + const item_t item = { + socket_, + 0, + user_data_, + events_ +#if defined ZMQ_POLL_BASED_ON_POLL + , + -1 +#endif + }; + try { + _items.push_back (item); + } + catch (const std::bad_alloc &) { + errno = ENOMEM; + return -1; + } + _need_rebuild = true; + + return 0; +} + +int zmq::socket_poller_t::add_fd (fd_t fd_, void *user_data_, short events_) +{ + if (find_if2 (_items.begin (), _items.end (), fd_, &is_fd) + != _items.end ()) { + errno = EINVAL; + return -1; + } + + const item_t item = { + NULL, + fd_, + user_data_, + events_ +#if defined ZMQ_POLL_BASED_ON_POLL + , + -1 +#endif + }; + try { + _items.push_back (item); + } + catch (const std::bad_alloc &) { + errno = ENOMEM; + return -1; + } + _need_rebuild = true; + + return 0; +} + +int zmq::socket_poller_t::modify (const socket_base_t *socket_, short events_) +{ + const items_t::iterator it = + find_if2 (_items.begin (), _items.end (), socket_, &is_socket); + + if (it == _items.end ()) { + errno = EINVAL; + return -1; + } + + it->events = events_; + _need_rebuild = true; + + return 0; +} + + +int zmq::socket_poller_t::modify_fd (fd_t fd_, short events_) +{ + const items_t::iterator it = + find_if2 (_items.begin (), _items.end (), fd_, &is_fd); + + if (it == _items.end ()) { + errno = EINVAL; + return -1; + } + + it->events = events_; + _need_rebuild = true; + + return 0; +} + + +int zmq::socket_poller_t::remove (socket_base_t *socket_) +{ + const items_t::iterator it = + find_if2 (_items.begin (), _items.end (), socket_, &is_socket); + + if (it == _items.end ()) { + errno = EINVAL; + return -1; + } + + _items.erase (it); + _need_rebuild = true; + + if (is_thread_safe (*socket_)) { + socket_->remove_signaler (_signaler); + } + + return 0; +} + +int zmq::socket_poller_t::remove_fd (fd_t fd_) +{ + const items_t::iterator it = + find_if2 (_items.begin (), _items.end (), fd_, &is_fd); + + if (it == _items.end ()) { + errno = EINVAL; + return -1; + } + + _items.erase (it); + _need_rebuild = true; + + return 0; +} + +int zmq::socket_poller_t::rebuild () +{ + _use_signaler = false; + _pollset_size = 0; + _need_rebuild = false; + +#if defined ZMQ_POLL_BASED_ON_POLL + + if (_pollfds) { + free (_pollfds); + _pollfds = NULL; + } + + for (items_t::iterator it = _items.begin (), end = _items.end (); it != end; + ++it) { + if (it->events) { + if (it->socket && is_thread_safe (*it->socket)) { + if (!_use_signaler) { + _use_signaler = true; + _pollset_size++; + } + } else + _pollset_size++; + } + } + + if (_pollset_size == 0) + return 0; + + _pollfds = static_cast (malloc (_pollset_size * sizeof (pollfd))); + + if (!_pollfds) { + errno = ENOMEM; + _need_rebuild = true; + return -1; + } + + int item_nbr = 0; + + if (_use_signaler) { + item_nbr = 1; + _pollfds[0].fd = _signaler->get_fd (); + _pollfds[0].events = POLLIN; + } + + for (items_t::iterator it = _items.begin (), end = _items.end (); it != end; + ++it) { + if (it->events) { + if (it->socket) { + if (!is_thread_safe (*it->socket)) { + size_t fd_size = sizeof (zmq::fd_t); + const int rc = it->socket->getsockopt ( + ZMQ_FD, &_pollfds[item_nbr].fd, &fd_size); + zmq_assert (rc == 0); + + _pollfds[item_nbr].events = POLLIN; + item_nbr++; + } + } else { + _pollfds[item_nbr].fd = it->fd; + _pollfds[item_nbr].events = + (it->events & ZMQ_POLLIN ? POLLIN : 0) + | (it->events & ZMQ_POLLOUT ? POLLOUT : 0) + | (it->events & ZMQ_POLLPRI ? POLLPRI : 0); + it->pollfd_index = item_nbr; + item_nbr++; + } + } + } + +#elif defined ZMQ_POLL_BASED_ON_SELECT + + // Ensure we do not attempt to select () on more than FD_SETSIZE + // file descriptors. + zmq_assert (_items.size () <= FD_SETSIZE); + + _pollset_in.resize (_items.size ()); + _pollset_out.resize (_items.size ()); + _pollset_err.resize (_items.size ()); + + FD_ZERO (_pollset_in.get ()); + FD_ZERO (_pollset_out.get ()); + FD_ZERO (_pollset_err.get ()); + + for (items_t::iterator it = _items.begin (), end = _items.end (); it != end; + ++it) { + if (it->socket && is_thread_safe (*it->socket) && it->events) { + _use_signaler = true; + FD_SET (_signaler->get_fd (), _pollset_in.get ()); + _pollset_size = 1; + break; + } + } + + _max_fd = 0; + + // Build the fd_sets for passing to select (). + for (items_t::iterator it = _items.begin (), end = _items.end (); it != end; + ++it) { + if (it->events) { + // If the poll item is a 0MQ socket we are interested in input on the + // notification file descriptor retrieved by the ZMQ_FD socket option. + if (it->socket) { + if (!is_thread_safe (*it->socket)) { + zmq::fd_t notify_fd; + size_t fd_size = sizeof (zmq::fd_t); + int rc = + it->socket->getsockopt (ZMQ_FD, ¬ify_fd, &fd_size); + zmq_assert (rc == 0); + + FD_SET (notify_fd, _pollset_in.get ()); + if (_max_fd < notify_fd) + _max_fd = notify_fd; + + _pollset_size++; + } + } + // Else, the poll item is a raw file descriptor. Convert the poll item + // events to the appropriate fd_sets. + else { + if (it->events & ZMQ_POLLIN) + FD_SET (it->fd, _pollset_in.get ()); + if (it->events & ZMQ_POLLOUT) + FD_SET (it->fd, _pollset_out.get ()); + if (it->events & ZMQ_POLLERR) + FD_SET (it->fd, _pollset_err.get ()); + if (_max_fd < it->fd) + _max_fd = it->fd; + + _pollset_size++; + } + } + } + +#endif + + return 0; +} + +void zmq::socket_poller_t::zero_trail_events ( + zmq::socket_poller_t::event_t *events_, int n_events_, int found_) +{ + for (int i = found_; i < n_events_; ++i) { + events_[i].socket = NULL; + events_[i].fd = zmq::retired_fd; + events_[i].user_data = NULL; + events_[i].events = 0; + } +} + +#if defined ZMQ_POLL_BASED_ON_POLL +int zmq::socket_poller_t::check_events (zmq::socket_poller_t::event_t *events_, + int n_events_) +#elif defined ZMQ_POLL_BASED_ON_SELECT +int zmq::socket_poller_t::check_events (zmq::socket_poller_t::event_t *events_, + int n_events_, + fd_set &inset_, + fd_set &outset_, + fd_set &errset_) +#endif +{ + int found = 0; + for (items_t::iterator it = _items.begin (), end = _items.end (); + it != end && found < n_events_; ++it) { + // The poll item is a 0MQ socket. Retrieve pending events + // using the ZMQ_EVENTS socket option. + if (it->socket) { + size_t events_size = sizeof (uint32_t); + uint32_t events; + if (it->socket->getsockopt (ZMQ_EVENTS, &events, &events_size) + == -1) { + return -1; + } + + if (it->events & events) { + events_[found].socket = it->socket; + events_[found].fd = zmq::retired_fd; + events_[found].user_data = it->user_data; + events_[found].events = it->events & events; + ++found; + } + } + // Else, the poll item is a raw file descriptor, simply convert + // the events to zmq_pollitem_t-style format. + else if (it->events) { +#if defined ZMQ_POLL_BASED_ON_POLL + zmq_assert (it->pollfd_index >= 0); + const short revents = _pollfds[it->pollfd_index].revents; + short events = 0; + + if (revents & POLLIN) + events |= ZMQ_POLLIN; + if (revents & POLLOUT) + events |= ZMQ_POLLOUT; + if (revents & POLLPRI) + events |= ZMQ_POLLPRI; + if (revents & ~(POLLIN | POLLOUT | POLLPRI)) + events |= ZMQ_POLLERR; + +#elif defined ZMQ_POLL_BASED_ON_SELECT + + short events = 0; + + if (FD_ISSET (it->fd, &inset_)) + events |= ZMQ_POLLIN; + if (FD_ISSET (it->fd, &outset_)) + events |= ZMQ_POLLOUT; + if (FD_ISSET (it->fd, &errset_)) + events |= ZMQ_POLLERR; +#endif //POLL_SELECT + + if (events) { + events_[found].socket = NULL; + events_[found].fd = it->fd; + events_[found].user_data = it->user_data; + events_[found].events = events; + ++found; + } + } + } + + return found; +} + +//Return 0 if timeout is expired otherwise 1 +int zmq::socket_poller_t::adjust_timeout (zmq::clock_t &clock_, + long timeout_, + uint64_t &now_, + uint64_t &end_, + bool &first_pass_) +{ + // If socket_poller_t::timeout is zero, exit immediately whether there + // are events or not. + if (timeout_ == 0) + return 0; + + // At this point we are meant to wait for events but there are none. + // If timeout is infinite we can just loop until we get some events. + if (timeout_ < 0) { + if (first_pass_) + first_pass_ = false; + return 1; + } + + // The timeout is finite and there are no events. In the first pass + // we get a timestamp of when the polling have begun. (We assume that + // first pass have taken negligible time). We also compute the time + // when the polling should time out. + now_ = clock_.now_ms (); + if (first_pass_) { + end_ = now_ + timeout_; + first_pass_ = false; + return 1; + } + + // Find out whether timeout have expired. + if (now_ >= end_) + return 0; + + return 1; +} + +int zmq::socket_poller_t::wait (zmq::socket_poller_t::event_t *events_, + int n_events_, + long timeout_) +{ + if (_items.empty () && timeout_ < 0) { + errno = EFAULT; + return -1; + } + + if (_need_rebuild) { + const int rc = rebuild (); + if (rc == -1) + return -1; + } + + if (unlikely (_pollset_size == 0)) { + if (timeout_ < 0) { + // Fail instead of trying to sleep forever + errno = EFAULT; + return -1; + } + // We'll report an error (timed out) as if the list was non-empty and + // no event occurred within the specified timeout. Otherwise the caller + // needs to check the return value AND the event to avoid using the + // nullified event data. + errno = EAGAIN; + if (timeout_ == 0) + return -1; +#if defined ZMQ_HAVE_WINDOWS + Sleep (timeout_ > 0 ? timeout_ : INFINITE); + return -1; +#elif defined ZMQ_HAVE_ANDROID + usleep (timeout_ * 1000); + return -1; +#elif defined ZMQ_HAVE_OSX + usleep (timeout_ * 1000); + errno = EAGAIN; + return -1; +#elif defined ZMQ_HAVE_VXWORKS + struct timespec ns_; + ns_.tv_sec = timeout_ / 1000; + ns_.tv_nsec = timeout_ % 1000 * 1000000; + nanosleep (&ns_, 0); + return -1; +#else + usleep (timeout_ * 1000); + return -1; +#endif + } + +#if defined ZMQ_POLL_BASED_ON_POLL + zmq::clock_t clock; + uint64_t now = 0; + uint64_t end = 0; + + bool first_pass = true; + + while (true) { + // Compute the timeout for the subsequent poll. + int timeout; + if (first_pass) + timeout = 0; + else if (timeout_ < 0) + timeout = -1; + else + timeout = + static_cast (std::min (end - now, INT_MAX)); + + // Wait for events. + const int rc = poll (_pollfds, _pollset_size, timeout); + if (rc == -1 && errno == EINTR) { + return -1; + } + errno_assert (rc >= 0); + + // Receive the signal from pollfd + if (_use_signaler && _pollfds[0].revents & POLLIN) + _signaler->recv (); + + // Check for the events. + const int found = check_events (events_, n_events_); + if (found) { + if (found > 0) + zero_trail_events (events_, n_events_, found); + return found; + } + + // Adjust timeout or break + if (adjust_timeout (clock, timeout_, now, end, first_pass) == 0) + break; + } + errno = EAGAIN; + return -1; + +#elif defined ZMQ_POLL_BASED_ON_SELECT + + zmq::clock_t clock; + uint64_t now = 0; + uint64_t end = 0; + + bool first_pass = true; + + optimized_fd_set_t inset (_pollset_size); + optimized_fd_set_t outset (_pollset_size); + optimized_fd_set_t errset (_pollset_size); + + while (true) { + // Compute the timeout for the subsequent poll. + timeval timeout; + timeval *ptimeout; + if (first_pass) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + ptimeout = &timeout; + } else if (timeout_ < 0) + ptimeout = NULL; + else { + timeout.tv_sec = static_cast ((end - now) / 1000); + timeout.tv_usec = static_cast ((end - now) % 1000 * 1000); + ptimeout = &timeout; + } + + // Wait for events. Ignore interrupts if there's infinite timeout. + memcpy (inset.get (), _pollset_in.get (), + valid_pollset_bytes (*_pollset_in.get ())); + memcpy (outset.get (), _pollset_out.get (), + valid_pollset_bytes (*_pollset_out.get ())); + memcpy (errset.get (), _pollset_err.get (), + valid_pollset_bytes (*_pollset_err.get ())); + const int rc = select (static_cast (_max_fd + 1), inset.get (), + outset.get (), errset.get (), ptimeout); +#if defined ZMQ_HAVE_WINDOWS + if (unlikely (rc == SOCKET_ERROR)) { + errno = wsa_error_to_errno (WSAGetLastError ()); + wsa_assert (errno == ENOTSOCK); + return -1; + } +#else + if (unlikely (rc == -1)) { + errno_assert (errno == EINTR || errno == EBADF); + return -1; + } +#endif + + if (_use_signaler && FD_ISSET (_signaler->get_fd (), inset.get ())) + _signaler->recv (); + + // Check for the events. + const int found = check_events (events_, n_events_, *inset.get (), + *outset.get (), *errset.get ()); + if (found) { + if (found > 0) + zero_trail_events (events_, n_events_, found); + return found; + } + + // Adjust timeout or break + if (adjust_timeout (clock, timeout_, now, end, first_pass) == 0) + break; + } + + errno = EAGAIN; + return -1; + +#else + + // Exotic platforms that support neither poll() nor select(). + errno = ENOTSUP; + return -1; + +#endif +} diff --git a/3rd/libzmq/src/socket_poller.hpp b/3rd/libzmq/src/socket_poller.hpp new file mode 100644 index 00000000..6f4923e7 --- /dev/null +++ b/3rd/libzmq/src/socket_poller.hpp @@ -0,0 +1,154 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SOCKET_POLLER_HPP_INCLUDED__ +#define __ZMQ_SOCKET_POLLER_HPP_INCLUDED__ + +#include "poller.hpp" + +#if defined ZMQ_POLL_BASED_ON_POLL && !defined ZMQ_HAVE_WINDOWS +#include +#endif + +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#elif defined ZMQ_HAVE_VXWORKS +#include +#include +#include +#else +#include +#endif + +#include + +#include "socket_base.hpp" +#include "signaler.hpp" +#include "polling_util.hpp" + +namespace zmq +{ +class socket_poller_t +{ + public: + socket_poller_t (); + ~socket_poller_t (); + + typedef zmq_poller_event_t event_t; + + int add (socket_base_t *socket_, void *user_data_, short events_); + int modify (const socket_base_t *socket_, short events_); + int remove (socket_base_t *socket_); + + int add_fd (fd_t fd_, void *user_data_, short events_); + int modify_fd (fd_t fd_, short events_); + int remove_fd (fd_t fd_); + // Returns the signaler's fd if there is one, otherwise errors. + int signaler_fd (fd_t *fd_) const; + + int wait (event_t *events_, int n_events_, long timeout_); + + int size () const { return static_cast (_items.size ()); }; + + // Return false if object is not a socket. + bool check_tag () const; + + private: + typedef struct item_t + { + socket_base_t *socket; + fd_t fd; + void *user_data; + short events; +#if defined ZMQ_POLL_BASED_ON_POLL + int pollfd_index; +#endif + } item_t; + + static void zero_trail_events (zmq::socket_poller_t::event_t *events_, + int n_events_, + int found_); +#if defined ZMQ_POLL_BASED_ON_POLL + int check_events (zmq::socket_poller_t::event_t *events_, int n_events_); +#elif defined ZMQ_POLL_BASED_ON_SELECT + int check_events (zmq::socket_poller_t::event_t *events_, + int n_events_, + fd_set &inset_, + fd_set &outset_, + fd_set &errset_); +#endif + static int adjust_timeout (zmq::clock_t &clock_, + long timeout_, + uint64_t &now_, + uint64_t &end_, + bool &first_pass_); + static bool is_socket (const item_t &item, const socket_base_t *socket_) + { + return item.socket == socket_; + } + static bool is_fd (const item_t &item, fd_t fd_) + { + return !item.socket && item.fd == fd_; + } + + int rebuild (); + + // Used to check whether the object is a socket_poller. + uint32_t _tag; + + // Signaler used for thread safe sockets polling + signaler_t *_signaler; + + // List of sockets + typedef std::vector items_t; + items_t _items; + + // Does the pollset needs rebuilding? + bool _need_rebuild; + + // Should the signaler be used for the thread safe polling? + bool _use_signaler; + + // Size of the pollset + int _pollset_size; + +#if defined ZMQ_POLL_BASED_ON_POLL + pollfd *_pollfds; +#elif defined ZMQ_POLL_BASED_ON_SELECT + resizable_optimized_fd_set_t _pollset_in; + resizable_optimized_fd_set_t _pollset_out; + resizable_optimized_fd_set_t _pollset_err; + zmq::fd_t _max_fd; +#endif + + ZMQ_NON_COPYABLE_NOR_MOVABLE (socket_poller_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/socks.cpp b/3rd/libzmq/src/socks.cpp new file mode 100644 index 00000000..9da12be6 --- /dev/null +++ b/3rd/libzmq/src/socks.cpp @@ -0,0 +1,381 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include "err.hpp" +#include "socks.hpp" +#include "tcp.hpp" +#include "blob.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#endif + +zmq::socks_greeting_t::socks_greeting_t (uint8_t method_) : num_methods (1) +{ + methods[0] = method_; +} + +zmq::socks_greeting_t::socks_greeting_t (const uint8_t *methods_, + uint8_t num_methods_) : + num_methods (num_methods_) +{ + for (uint8_t i = 0; i < num_methods_; i++) + methods[i] = methods_[i]; +} + +zmq::socks_greeting_encoder_t::socks_greeting_encoder_t () : + _bytes_encoded (0), + _bytes_written (0) +{ +} + +void zmq::socks_greeting_encoder_t::encode (const socks_greeting_t &greeting_) +{ + uint8_t *ptr = _buf; + + *ptr++ = 0x05; + *ptr++ = static_cast (greeting_.num_methods); + for (uint8_t i = 0; i < greeting_.num_methods; i++) + *ptr++ = greeting_.methods[i]; + + _bytes_encoded = 2 + greeting_.num_methods; + _bytes_written = 0; +} + +int zmq::socks_greeting_encoder_t::output (fd_t fd_) +{ + const int rc = + tcp_write (fd_, _buf + _bytes_written, _bytes_encoded - _bytes_written); + if (rc > 0) + _bytes_written += static_cast (rc); + return rc; +} + +bool zmq::socks_greeting_encoder_t::has_pending_data () const +{ + return _bytes_written < _bytes_encoded; +} + +void zmq::socks_greeting_encoder_t::reset () +{ + _bytes_encoded = _bytes_written = 0; +} + +zmq::socks_choice_t::socks_choice_t (unsigned char method_) : method (method_) +{ +} + +zmq::socks_choice_decoder_t::socks_choice_decoder_t () : _bytes_read (0) +{ +} + +int zmq::socks_choice_decoder_t::input (fd_t fd_) +{ + zmq_assert (_bytes_read < 2); + const int rc = tcp_read (fd_, _buf + _bytes_read, 2 - _bytes_read); + if (rc > 0) { + _bytes_read += static_cast (rc); + if (_buf[0] != 0x05) + return -1; + } + return rc; +} + +bool zmq::socks_choice_decoder_t::message_ready () const +{ + return _bytes_read == 2; +} + +zmq::socks_choice_t zmq::socks_choice_decoder_t::decode () +{ + zmq_assert (message_ready ()); + return socks_choice_t (_buf[1]); +} + +void zmq::socks_choice_decoder_t::reset () +{ + _bytes_read = 0; +} + + +zmq::socks_basic_auth_request_t::socks_basic_auth_request_t ( + const std::string &username_, const std::string &password_) : + username (username_), + password (password_) +{ + zmq_assert (username_.size () <= UINT8_MAX); + zmq_assert (password_.size () <= UINT8_MAX); +} + + +zmq::socks_basic_auth_request_encoder_t::socks_basic_auth_request_encoder_t () : + _bytes_encoded (0), + _bytes_written (0) +{ +} + +void zmq::socks_basic_auth_request_encoder_t::encode ( + const socks_basic_auth_request_t &req_) +{ + unsigned char *ptr = _buf; + *ptr++ = 0x01; + *ptr++ = static_cast (req_.username.size ()); + memcpy (ptr, req_.username.c_str (), req_.username.size ()); + ptr += req_.username.size (); + *ptr++ = static_cast (req_.password.size ()); + memcpy (ptr, req_.password.c_str (), req_.password.size ()); + ptr += req_.password.size (); + + _bytes_encoded = ptr - _buf; + _bytes_written = 0; +} + +int zmq::socks_basic_auth_request_encoder_t::output (fd_t fd_) +{ + const int rc = + tcp_write (fd_, _buf + _bytes_written, _bytes_encoded - _bytes_written); + if (rc > 0) + _bytes_written += static_cast (rc); + return rc; +} + +bool zmq::socks_basic_auth_request_encoder_t::has_pending_data () const +{ + return _bytes_written < _bytes_encoded; +} + +void zmq::socks_basic_auth_request_encoder_t::reset () +{ + _bytes_encoded = _bytes_written = 0; +} + + +zmq::socks_auth_response_t::socks_auth_response_t (uint8_t response_code_) : + response_code (response_code_) +{ +} + +zmq::socks_auth_response_decoder_t::socks_auth_response_decoder_t () : + _bytes_read (0) +{ +} + +int zmq::socks_auth_response_decoder_t::input (fd_t fd_) +{ + zmq_assert (_bytes_read < 2); + const int rc = tcp_read (fd_, _buf + _bytes_read, 2 - _bytes_read); + if (rc > 0) { + _bytes_read += static_cast (rc); + if (_buf[0] != 0x01) + return -1; + } + return rc; +} + +bool zmq::socks_auth_response_decoder_t::message_ready () const +{ + return _bytes_read == 2; +} + +zmq::socks_auth_response_t zmq::socks_auth_response_decoder_t::decode () +{ + zmq_assert (message_ready ()); + return socks_auth_response_t (_buf[1]); +} + +void zmq::socks_auth_response_decoder_t::reset () +{ + _bytes_read = 0; +} + + +zmq::socks_request_t::socks_request_t (uint8_t command_, + std::string hostname_, + uint16_t port_) : + command (command_), + hostname (ZMQ_MOVE (hostname_)), + port (port_) +{ + zmq_assert (hostname.size () <= UINT8_MAX); +} + +zmq::socks_request_encoder_t::socks_request_encoder_t () : + _bytes_encoded (0), + _bytes_written (0) +{ +} + +void zmq::socks_request_encoder_t::encode (const socks_request_t &req_) +{ + zmq_assert (req_.hostname.size () <= UINT8_MAX); + + unsigned char *ptr = _buf; + *ptr++ = 0x05; + *ptr++ = req_.command; + *ptr++ = 0x00; + +#if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64 + __addrinfo64 hints, *res = NULL; +#else + addrinfo hints, *res = NULL; +#endif + + memset (&hints, 0, sizeof hints); + + // Suppress potential DNS lookups. + hints.ai_flags = AI_NUMERICHOST; + + const int rc = getaddrinfo (req_.hostname.c_str (), NULL, &hints, &res); + if (rc == 0 && res->ai_family == AF_INET) { + const struct sockaddr_in *sockaddr_in = + reinterpret_cast (res->ai_addr); + *ptr++ = 0x01; + memcpy (ptr, &sockaddr_in->sin_addr, 4); + ptr += 4; + } else if (rc == 0 && res->ai_family == AF_INET6) { + const struct sockaddr_in6 *sockaddr_in6 = + reinterpret_cast (res->ai_addr); + *ptr++ = 0x04; + memcpy (ptr, &sockaddr_in6->sin6_addr, 16); + ptr += 16; + } else { + *ptr++ = 0x03; + *ptr++ = static_cast (req_.hostname.size ()); + memcpy (ptr, req_.hostname.c_str (), req_.hostname.size ()); + ptr += req_.hostname.size (); + } + + if (rc == 0) + freeaddrinfo (res); + + *ptr++ = req_.port / 256; + *ptr++ = req_.port % 256; + + _bytes_encoded = ptr - _buf; + _bytes_written = 0; +} + +int zmq::socks_request_encoder_t::output (fd_t fd_) +{ + const int rc = + tcp_write (fd_, _buf + _bytes_written, _bytes_encoded - _bytes_written); + if (rc > 0) + _bytes_written += static_cast (rc); + return rc; +} + +bool zmq::socks_request_encoder_t::has_pending_data () const +{ + return _bytes_written < _bytes_encoded; +} + +void zmq::socks_request_encoder_t::reset () +{ + _bytes_encoded = _bytes_written = 0; +} + +zmq::socks_response_t::socks_response_t (uint8_t response_code_, + const std::string &address_, + uint16_t port_) : + response_code (response_code_), + address (address_), + port (port_) +{ +} + +zmq::socks_response_decoder_t::socks_response_decoder_t () : _bytes_read (0) +{ +} + +int zmq::socks_response_decoder_t::input (fd_t fd_) +{ + size_t n = 0; + + if (_bytes_read < 5) + n = 5 - _bytes_read; + else { + const uint8_t atyp = _buf[3]; + zmq_assert (atyp == 0x01 || atyp == 0x03 || atyp == 0x04); + if (atyp == 0x01) + n = 3 + 2; + else if (atyp == 0x03) + n = _buf[4] + 2; + else if (atyp == 0x04) + n = 15 + 2; + } + const int rc = tcp_read (fd_, _buf + _bytes_read, n); + if (rc > 0) { + _bytes_read += static_cast (rc); + if (_buf[0] != 0x05) + return -1; + if (_bytes_read >= 2) + if (_buf[1] > 0x08) + return -1; + if (_bytes_read >= 3) + if (_buf[2] != 0x00) + return -1; + if (_bytes_read >= 4) { + const uint8_t atyp = _buf[3]; + if (atyp != 0x01 && atyp != 0x03 && atyp != 0x04) + return -1; + } + } + return rc; +} + +bool zmq::socks_response_decoder_t::message_ready () const +{ + if (_bytes_read < 4) + return false; + + const uint8_t atyp = _buf[3]; + zmq_assert (atyp == 0x01 || atyp == 0x03 || atyp == 0x04); + if (atyp == 0x01) + return _bytes_read == 10; + if (atyp == 0x03) + return _bytes_read > 4 && _bytes_read == 4 + 1 + _buf[4] + 2u; + + return _bytes_read == 22; +} + +zmq::socks_response_t zmq::socks_response_decoder_t::decode () +{ + zmq_assert (message_ready ()); + return socks_response_t (_buf[1], "", 0); +} + +void zmq::socks_response_decoder_t::reset () +{ + _bytes_read = 0; +} diff --git a/3rd/libzmq/src/socks.hpp b/3rd/libzmq/src/socks.hpp new file mode 100644 index 00000000..bc3d5e2f --- /dev/null +++ b/3rd/libzmq/src/socks.hpp @@ -0,0 +1,178 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SOCKS_HPP_INCLUDED__ +#define __ZMQ_SOCKS_HPP_INCLUDED__ + +#include +#include "fd.hpp" +#include "stdint.hpp" + +namespace zmq +{ +struct socks_greeting_t +{ + socks_greeting_t (uint8_t method_); + socks_greeting_t (const uint8_t *methods_, uint8_t num_methods_); + + uint8_t methods[UINT8_MAX]; + const size_t num_methods; +}; + +class socks_greeting_encoder_t +{ + public: + socks_greeting_encoder_t (); + void encode (const socks_greeting_t &greeting_); + int output (fd_t fd_); + bool has_pending_data () const; + void reset (); + + private: + size_t _bytes_encoded; + size_t _bytes_written; + uint8_t _buf[2 + UINT8_MAX]; +}; + +struct socks_choice_t +{ + socks_choice_t (uint8_t method_); + + uint8_t method; +}; + +class socks_choice_decoder_t +{ + public: + socks_choice_decoder_t (); + int input (fd_t fd_); + bool message_ready () const; + socks_choice_t decode (); + void reset (); + + private: + unsigned char _buf[2]; + size_t _bytes_read; +}; + + +struct socks_basic_auth_request_t +{ + socks_basic_auth_request_t (const std::string &username_, + const std::string &password_); + + const std::string username; + const std::string password; +}; + +class socks_basic_auth_request_encoder_t +{ + public: + socks_basic_auth_request_encoder_t (); + void encode (const socks_basic_auth_request_t &req_); + int output (fd_t fd_); + bool has_pending_data () const; + void reset (); + + private: + size_t _bytes_encoded; + size_t _bytes_written; + uint8_t _buf[1 + 1 + UINT8_MAX + 1 + UINT8_MAX]; +}; + +struct socks_auth_response_t +{ + socks_auth_response_t (uint8_t response_code_); + uint8_t response_code; +}; + +class socks_auth_response_decoder_t +{ + public: + socks_auth_response_decoder_t (); + int input (fd_t fd_); + bool message_ready () const; + socks_auth_response_t decode (); + void reset (); + + private: + int8_t _buf[2]; + size_t _bytes_read; +}; + +struct socks_request_t +{ + socks_request_t (uint8_t command_, std::string hostname_, uint16_t port_); + + const uint8_t command; + const std::string hostname; + const uint16_t port; +}; + +class socks_request_encoder_t +{ + public: + socks_request_encoder_t (); + void encode (const socks_request_t &req_); + int output (fd_t fd_); + bool has_pending_data () const; + void reset (); + + private: + size_t _bytes_encoded; + size_t _bytes_written; + uint8_t _buf[4 + UINT8_MAX + 1 + 2]; +}; + +struct socks_response_t +{ + socks_response_t (uint8_t response_code_, + const std::string &address_, + uint16_t port_); + uint8_t response_code; + std::string address; + uint16_t port; +}; + +class socks_response_decoder_t +{ + public: + socks_response_decoder_t (); + int input (fd_t fd_); + bool message_ready () const; + socks_response_t decode (); + void reset (); + + private: + int8_t _buf[4 + UINT8_MAX + 1 + 2]; + size_t _bytes_read; +}; +} + +#endif diff --git a/3rd/libzmq/src/socks_connecter.cpp b/3rd/libzmq/src/socks_connecter.cpp new file mode 100644 index 00000000..b2931605 --- /dev/null +++ b/3rd/libzmq/src/socks_connecter.cpp @@ -0,0 +1,424 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "macros.hpp" +#include "socks_connecter.hpp" +#include "random.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "address.hpp" +#include "tcp_address.hpp" +#include "session_base.hpp" +#include "socks.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#if defined ZMQ_HAVE_VXWORKS +#include +#endif +#endif + +zmq::socks_connecter_t::socks_connecter_t (class io_thread_t *io_thread_, + class session_base_t *session_, + const options_t &options_, + address_t *addr_, + address_t *proxy_addr_, + bool delayed_start_) : + stream_connecter_base_t ( + io_thread_, session_, options_, addr_, delayed_start_), + _proxy_addr (proxy_addr_), + _auth_method (socks_no_auth_required), + _status (unplugged) +{ + zmq_assert (_addr->protocol == protocol_name::tcp); + _proxy_addr->to_string (_endpoint); +} + +zmq::socks_connecter_t::~socks_connecter_t () +{ + LIBZMQ_DELETE (_proxy_addr); +} + +void zmq::socks_connecter_t::set_auth_method_none () +{ + _auth_method = socks_no_auth_required; + _auth_username.clear (); + _auth_password.clear (); +} + +void zmq::socks_connecter_t::set_auth_method_basic ( + const std::string &username_, const std::string &password_) +{ + _auth_method = socks_basic_auth; + _auth_username = username_; + _auth_password = password_; +} + +void zmq::socks_connecter_t::in_event () +{ + int expected_status = -1; + zmq_assert (_status != unplugged); + + if (_status == waiting_for_choice) { + int rc = _choice_decoder.input (_s); + if (rc == 0 || rc == -1) + error (); + else if (_choice_decoder.message_ready ()) { + const socks_choice_t choice = _choice_decoder.decode (); + rc = process_server_response (choice); + if (rc == -1) + error (); + else { + if (choice.method == socks_basic_auth) + expected_status = sending_basic_auth_request; + else + expected_status = sending_request; + } + } + } else if (_status == waiting_for_auth_response) { + int rc = _auth_response_decoder.input (_s); + if (rc == 0 || rc == -1) + error (); + else if (_auth_response_decoder.message_ready ()) { + const socks_auth_response_t auth_response = + _auth_response_decoder.decode (); + rc = process_server_response (auth_response); + if (rc == -1) + error (); + else { + expected_status = sending_request; + } + } + } else if (_status == waiting_for_response) { + int rc = _response_decoder.input (_s); + if (rc == 0 || rc == -1) + error (); + else if (_response_decoder.message_ready ()) { + const socks_response_t response = _response_decoder.decode (); + rc = process_server_response (response); + if (rc == -1) + error (); + else { + rm_handle (); + create_engine ( + _s, get_socket_name (_s, socket_end_local)); + _s = -1; + _status = unplugged; + } + } + } else + error (); + + if (expected_status == sending_basic_auth_request) { + _basic_auth_request_encoder.encode ( + socks_basic_auth_request_t (_auth_username, _auth_password)); + reset_pollin (_handle); + set_pollout (_handle); + _status = sending_basic_auth_request; + } else if (expected_status == sending_request) { + std::string hostname; + uint16_t port = 0; + if (parse_address (_addr->address, hostname, port) == -1) + error (); + else { + _request_encoder.encode (socks_request_t (1, hostname, port)); + reset_pollin (_handle); + set_pollout (_handle); + _status = sending_request; + } + } +} + +void zmq::socks_connecter_t::out_event () +{ + zmq_assert ( + _status == waiting_for_proxy_connection || _status == sending_greeting + || _status == sending_basic_auth_request || _status == sending_request); + + if (_status == waiting_for_proxy_connection) { + const int rc = static_cast (check_proxy_connection ()); + if (rc == -1) + error (); + else { + _greeting_encoder.encode (socks_greeting_t (_auth_method)); + _status = sending_greeting; + } + } else if (_status == sending_greeting) { + zmq_assert (_greeting_encoder.has_pending_data ()); + const int rc = _greeting_encoder.output (_s); + if (rc == -1 || rc == 0) + error (); + else if (!_greeting_encoder.has_pending_data ()) { + reset_pollout (_handle); + set_pollin (_handle); + _status = waiting_for_choice; + } + } else if (_status == sending_basic_auth_request) { + zmq_assert (_basic_auth_request_encoder.has_pending_data ()); + const int rc = _basic_auth_request_encoder.output (_s); + if (rc == -1 || rc == 0) + error (); + else if (!_basic_auth_request_encoder.has_pending_data ()) { + reset_pollout (_handle); + set_pollin (_handle); + _status = waiting_for_auth_response; + } + } else { + zmq_assert (_request_encoder.has_pending_data ()); + const int rc = _request_encoder.output (_s); + if (rc == -1 || rc == 0) + error (); + else if (!_request_encoder.has_pending_data ()) { + reset_pollout (_handle); + set_pollin (_handle); + _status = waiting_for_response; + } + } +} + +void zmq::socks_connecter_t::start_connecting () +{ + zmq_assert (_status == unplugged); + + // Open the connecting socket. + const int rc = connect_to_proxy (); + + // Connect may succeed in synchronous manner. + if (rc == 0) { + _handle = add_fd (_s); + set_pollout (_handle); + _status = sending_greeting; + } + // Connection establishment may be delayed. Poll for its completion. + else if (errno == EINPROGRESS) { + _handle = add_fd (_s); + set_pollout (_handle); + _status = waiting_for_proxy_connection; + _socket->event_connect_delayed ( + make_unconnected_connect_endpoint_pair (_endpoint), zmq_errno ()); + } + // Handle any other error condition by eventual reconnect. + else { + if (_s != retired_fd) + close (); + add_reconnect_timer (); + } +} + +int zmq::socks_connecter_t::process_server_response ( + const socks_choice_t &response_) +{ + return response_.method == socks_no_auth_required + || response_.method == socks_basic_auth + ? 0 + : -1; +} + +int zmq::socks_connecter_t::process_server_response ( + const socks_response_t &response_) +{ + return response_.response_code == 0 ? 0 : -1; +} + +int zmq::socks_connecter_t::process_server_response ( + const socks_auth_response_t &response_) +{ + return response_.response_code == 0 ? 0 : -1; +} + +void zmq::socks_connecter_t::error () +{ + rm_fd (_handle); + close (); + _greeting_encoder.reset (); + _choice_decoder.reset (); + _basic_auth_request_encoder.reset (); + _auth_response_decoder.reset (); + _request_encoder.reset (); + _response_decoder.reset (); + _status = unplugged; + add_reconnect_timer (); +} + +int zmq::socks_connecter_t::connect_to_proxy () +{ + zmq_assert (_s == retired_fd); + + // Resolve the address + if (_proxy_addr->resolved.tcp_addr != NULL) { + LIBZMQ_DELETE (_proxy_addr->resolved.tcp_addr); + } + + _proxy_addr->resolved.tcp_addr = new (std::nothrow) tcp_address_t (); + alloc_assert (_proxy_addr->resolved.tcp_addr); + // Automatic fallback to ipv4 is disabled here since this was the existing + // behaviour, however I don't see a real reason for this. Maybe this can + // be changed to true (and then the parameter can be removed entirely). + _s = tcp_open_socket (_proxy_addr->address.c_str (), options, false, false, + _proxy_addr->resolved.tcp_addr); + if (_s == retired_fd) { + // TODO we should emit some event in this case! + LIBZMQ_DELETE (_proxy_addr->resolved.tcp_addr); + return -1; + } + zmq_assert (_proxy_addr->resolved.tcp_addr != NULL); + + // Set the socket to non-blocking mode so that we get async connect(). + unblock_socket (_s); + + const tcp_address_t *const tcp_addr = _proxy_addr->resolved.tcp_addr; + + int rc; + + // Set a source address for conversations + if (tcp_addr->has_src_addr ()) { +#if defined ZMQ_HAVE_VXWORKS + rc = ::bind (_s, (sockaddr *) tcp_addr->src_addr (), + tcp_addr->src_addrlen ()); +#else + rc = ::bind (_s, tcp_addr->src_addr (), tcp_addr->src_addrlen ()); +#endif + if (rc == -1) { + close (); + return -1; + } + } + + // Connect to the remote peer. +#if defined ZMQ_HAVE_VXWORKS + rc = ::connect (_s, (sockaddr *) tcp_addr->addr (), tcp_addr->addrlen ()); +#else + rc = ::connect (_s, tcp_addr->addr (), tcp_addr->addrlen ()); +#endif + // Connect was successful immediately. + if (rc == 0) + return 0; + + // Translate error codes indicating asynchronous connect has been + // launched to a uniform EINPROGRESS. +#ifdef ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + if (last_error == WSAEINPROGRESS || last_error == WSAEWOULDBLOCK) + errno = EINPROGRESS; + else { + errno = wsa_error_to_errno (last_error); + close (); + } +#else + if (errno == EINTR) + errno = EINPROGRESS; +#endif + return -1; +} + +zmq::fd_t zmq::socks_connecter_t::check_proxy_connection () const +{ + // Async connect has finished. Check whether an error occurred + int err = 0; +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int len = sizeof err; +#else + socklen_t len = sizeof err; +#endif + + int rc = getsockopt (_s, SOL_SOCKET, SO_ERROR, + reinterpret_cast (&err), &len); + + // Assert if the error was caused by 0MQ bug. + // Networking problems are OK. No need to assert. +#ifdef ZMQ_HAVE_WINDOWS + zmq_assert (rc == 0); + if (err != 0) { + wsa_assert (err == WSAECONNREFUSED || err == WSAETIMEDOUT + || err == WSAECONNABORTED || err == WSAEHOSTUNREACH + || err == WSAENETUNREACH || err == WSAENETDOWN + || err == WSAEACCES || err == WSAEINVAL + || err == WSAEADDRINUSE); + return -1; + } +#else + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + if (rc == -1) + err = errno; + if (err != 0) { + errno = err; + errno_assert (errno == ECONNREFUSED || errno == ECONNRESET + || errno == ETIMEDOUT || errno == EHOSTUNREACH + || errno == ENETUNREACH || errno == ENETDOWN + || errno == EINVAL); + return -1; + } +#endif + + rc = tune_tcp_socket (_s); + rc = rc + | tune_tcp_keepalives ( + _s, options.tcp_keepalive, options.tcp_keepalive_cnt, + options.tcp_keepalive_idle, options.tcp_keepalive_intvl); + if (rc != 0) + return -1; + + return 0; +} + +int zmq::socks_connecter_t::parse_address (const std::string &address_, + std::string &hostname_, + uint16_t &port_) +{ + // Find the ':' at end that separates address from the port number. + const size_t idx = address_.rfind (':'); + if (idx == std::string::npos) { + errno = EINVAL; + return -1; + } + + // Extract hostname + if (idx < 2 || address_[0] != '[' || address_[idx - 1] != ']') + hostname_ = address_.substr (0, idx); + else + hostname_ = address_.substr (1, idx - 2); + + // Separate the hostname/port. + const std::string port_str = address_.substr (idx + 1); + // Parse the port number (0 is not a valid port). + port_ = static_cast (atoi (port_str.c_str ())); + if (port_ == 0) { + errno = EINVAL; + return -1; + } + return 0; +} diff --git a/3rd/libzmq/src/socks_connecter.hpp b/3rd/libzmq/src/socks_connecter.hpp new file mode 100644 index 00000000..de79415c --- /dev/null +++ b/3rd/libzmq/src/socks_connecter.hpp @@ -0,0 +1,135 @@ +/* + 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 . +*/ + +#ifndef __SOCKS_CONNECTER_HPP_INCLUDED__ +#define __SOCKS_CONNECTER_HPP_INCLUDED__ + +#include "fd.hpp" +#include "stream_connecter_base.hpp" +#include "stdint.hpp" +#include "socks.hpp" + +namespace zmq +{ +class io_thread_t; +class session_base_t; +struct address_t; + +class socks_connecter_t ZMQ_FINAL : public stream_connecter_base_t +{ + public: + // If 'delayed_start' is true connecter first waits for a while, + // then starts connection process. + socks_connecter_t (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const options_t &options_, + address_t *addr_, + address_t *proxy_addr_, + bool delayed_start_); + ~socks_connecter_t (); + + void set_auth_method_basic (const std::string &username, + const std::string &password); + void set_auth_method_none (); + + + private: + enum + { + unplugged, + waiting_for_reconnect_time, + waiting_for_proxy_connection, + sending_greeting, + waiting_for_choice, + sending_basic_auth_request, + waiting_for_auth_response, + sending_request, + waiting_for_response + }; + + // Method ID + enum + { + socks_no_auth_required = 0x00, + socks_basic_auth = 0x02, + socks_no_acceptable_method = 0xff + }; + + // Handlers for I/O events. + void in_event (); + void out_event (); + + // Internal function to start the actual connection establishment. + void start_connecting (); + + static int process_server_response (const socks_choice_t &response_); + static int process_server_response (const socks_response_t &response_); + static int process_server_response (const socks_auth_response_t &response_); + + static int parse_address (const std::string &address_, + std::string &hostname_, + uint16_t &port_); + + int connect_to_proxy (); + + void error (); + + // Open TCP connecting socket. Returns -1 in case of error, + // 0 if connect was successful immediately. Returns -1 with + // EAGAIN errno if async connect was launched. + int open (); + + // Get the file descriptor of newly created connection. Returns + // retired_fd if the connection was unsuccessful. + zmq::fd_t check_proxy_connection () const; + + socks_greeting_encoder_t _greeting_encoder; + socks_choice_decoder_t _choice_decoder; + socks_basic_auth_request_encoder_t _basic_auth_request_encoder; + socks_auth_response_decoder_t _auth_response_decoder; + socks_request_encoder_t _request_encoder; + socks_response_decoder_t _response_decoder; + + // SOCKS address; owned by this connecter. + address_t *_proxy_addr; + + // User defined authentication method + int _auth_method; + + // Credentials for basic authentication + std::string _auth_username; + std::string _auth_password; + + int _status; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (socks_connecter_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/stdint.hpp b/3rd/libzmq/src/stdint.hpp new file mode 100644 index 00000000..dc4d5df1 --- /dev/null +++ b/3rd/libzmq/src/stdint.hpp @@ -0,0 +1,80 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_STDINT_HPP_INCLUDED__ +#define __ZMQ_STDINT_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_OPENVMS + +#include + +#elif defined _MSC_VER && _MSC_VER < 1600 + +#ifndef int8_t +typedef __int8 int8_t; +#endif +#ifndef int16_t +typedef __int16 int16_t; +#endif +#ifndef int32_t +typedef __int32 int32_t; +#endif +#ifndef int64_t +typedef __int64 int64_t; +#endif +#ifndef uint8_t +typedef unsigned __int8 uint8_t; +#endif +#ifndef uint16_t +typedef unsigned __int16 uint16_t; +#endif +#ifndef uint32_t +typedef unsigned __int32 uint32_t; +#endif +#ifndef uint64_t +typedef unsigned __int64 uint64_t; +#endif +#ifndef UINT16_MAX +#define UINT16_MAX _UI16_MAX +#endif +#ifndef UINT32_MAX +#define UINT32_MAX _UI32_MAX +#endif + +#else + +#include + +#endif + +#ifndef UINT8_MAX +#define UINT8_MAX 0xFF +#endif + +#endif diff --git a/3rd/libzmq/src/stream.cpp b/3rd/libzmq/src/stream.cpp new file mode 100644 index 00000000..3713c7e7 --- /dev/null +++ b/3rd/libzmq/src/stream.cpp @@ -0,0 +1,291 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "stream.hpp" +#include "pipe.hpp" +#include "wire.hpp" +#include "random.hpp" +#include "likely.hpp" +#include "err.hpp" + +zmq::stream_t::stream_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + routing_socket_base_t (parent_, tid_, sid_), + _prefetched (false), + _routing_id_sent (false), + _current_out (NULL), + _more_out (false), + _next_integral_routing_id (generate_random ()) +{ + options.type = ZMQ_STREAM; + options.raw_socket = true; + + _prefetched_routing_id.init (); + _prefetched_msg.init (); +} + +zmq::stream_t::~stream_t () +{ + _prefetched_routing_id.close (); + _prefetched_msg.close (); +} + +void zmq::stream_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + + zmq_assert (pipe_); + + identify_peer (pipe_, locally_initiated_); + _fq.attach (pipe_); +} + +void zmq::stream_t::xpipe_terminated (pipe_t *pipe_) +{ + erase_out_pipe (pipe_); + _fq.pipe_terminated (pipe_); + // TODO router_t calls pipe_->rollback() here; should this be done here as + // well? then xpipe_terminated could be pulled up to routing_socket_base_t + if (pipe_ == _current_out) + _current_out = NULL; +} + +void zmq::stream_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +int zmq::stream_t::xsend (msg_t *msg_) +{ + // If this is the first part of the message it's the ID of the + // peer to send the message to. + if (!_more_out) { + zmq_assert (!_current_out); + + // If we have malformed message (prefix with no subsequent message) + // then just silently ignore it. + // TODO: The connections should be killed instead. + if (msg_->flags () & msg_t::more) { + // Find the pipe associated with the routing id stored in the prefix. + // If there's no such pipe return an error + + out_pipe_t *out_pipe = lookup_out_pipe ( + blob_t (static_cast (msg_->data ()), + msg_->size (), reference_tag_t ())); + + if (out_pipe) { + _current_out = out_pipe->pipe; + if (!_current_out->check_write ()) { + out_pipe->active = false; + _current_out = NULL; + errno = EAGAIN; + return -1; + } + } else { + errno = EHOSTUNREACH; + return -1; + } + } + + // Expect one more message frame. + _more_out = true; + + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + return 0; + } + + // Ignore the MORE flag + msg_->reset_flags (msg_t::more); + + // This is the last part of the message. + _more_out = false; + + // Push the message into the pipe. If there's no out pipe, just drop it. + if (_current_out) { + // Close the remote connection if user has asked to do so + // by sending zero length message. + // Pending messages in the pipe will be dropped (on receiving term- ack) + if (msg_->size () == 0) { + _current_out->terminate (false); + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + _current_out = NULL; + return 0; + } + const bool ok = _current_out->write (msg_); + if (likely (ok)) + _current_out->flush (); + _current_out = NULL; + } else { + const int rc = msg_->close (); + errno_assert (rc == 0); + } + + // Detach the message from the data buffer. + const int rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +int zmq::stream_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + switch (option_) { + case ZMQ_STREAM_NOTIFY: + return do_setsockopt_int_as_bool_strict (optval_, optvallen_, + &options.raw_notify); + + default: + return routing_socket_base_t::xsetsockopt (option_, optval_, + optvallen_); + } +} + +int zmq::stream_t::xrecv (msg_t *msg_) +{ + if (_prefetched) { + if (!_routing_id_sent) { + const int rc = msg_->move (_prefetched_routing_id); + errno_assert (rc == 0); + _routing_id_sent = true; + } else { + const int rc = msg_->move (_prefetched_msg); + errno_assert (rc == 0); + _prefetched = false; + } + return 0; + } + + pipe_t *pipe = NULL; + int rc = _fq.recvpipe (&_prefetched_msg, &pipe); + if (rc != 0) + return -1; + + zmq_assert (pipe != NULL); + zmq_assert ((_prefetched_msg.flags () & msg_t::more) == 0); + + // We have received a frame with TCP data. + // Rather than sending this frame, we keep it in prefetched + // buffer and send a frame with peer's ID. + const blob_t &routing_id = pipe->get_routing_id (); + rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init_size (routing_id.size ()); + errno_assert (rc == 0); + + // forward metadata (if any) + metadata_t *metadata = _prefetched_msg.metadata (); + if (metadata) + msg_->set_metadata (metadata); + + memcpy (msg_->data (), routing_id.data (), routing_id.size ()); + msg_->set_flags (msg_t::more); + + _prefetched = true; + _routing_id_sent = true; + + return 0; +} + +bool zmq::stream_t::xhas_in () +{ + // We may already have a message pre-fetched. + if (_prefetched) + return true; + + // Try to read the next message. + // The message, if read, is kept in the pre-fetch buffer. + pipe_t *pipe = NULL; + int rc = _fq.recvpipe (&_prefetched_msg, &pipe); + if (rc != 0) + return false; + + zmq_assert (pipe != NULL); + zmq_assert ((_prefetched_msg.flags () & msg_t::more) == 0); + + const blob_t &routing_id = pipe->get_routing_id (); + rc = _prefetched_routing_id.init_size (routing_id.size ()); + errno_assert (rc == 0); + + // forward metadata (if any) + metadata_t *metadata = _prefetched_msg.metadata (); + if (metadata) + _prefetched_routing_id.set_metadata (metadata); + + memcpy (_prefetched_routing_id.data (), routing_id.data (), + routing_id.size ()); + _prefetched_routing_id.set_flags (msg_t::more); + + _prefetched = true; + _routing_id_sent = false; + + return true; +} + +bool zmq::stream_t::xhas_out () +{ + // In theory, STREAM socket is always ready for writing. Whether actual + // attempt to write succeeds depends on which pipe the message is going + // to be routed to. + return true; +} + +void zmq::stream_t::identify_peer (pipe_t *pipe_, bool locally_initiated_) +{ + // Always assign routing id for raw-socket + unsigned char buffer[5]; + buffer[0] = 0; + blob_t routing_id; + if (locally_initiated_ && connect_routing_id_is_set ()) { + const std::string connect_routing_id = extract_connect_routing_id (); + routing_id.set ( + reinterpret_cast (connect_routing_id.c_str ()), + connect_routing_id.length ()); + // Not allowed to duplicate an existing rid + zmq_assert (!has_out_pipe (routing_id)); + } else { + put_uint32 (buffer + 1, _next_integral_routing_id++); + routing_id.set (buffer, sizeof buffer); + memcpy (options.routing_id, routing_id.data (), routing_id.size ()); + options.routing_id_size = + static_cast (routing_id.size ()); + } + pipe_->set_router_socket_routing_id (routing_id); + add_out_pipe (ZMQ_MOVE (routing_id), pipe_); +} diff --git a/3rd/libzmq/src/stream.hpp b/3rd/libzmq/src/stream.hpp new file mode 100644 index 00000000..b5ea24cd --- /dev/null +++ b/3rd/libzmq/src/stream.hpp @@ -0,0 +1,94 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_STREAM_HPP_INCLUDED__ +#define __ZMQ_STREAM_HPP_INCLUDED__ + +#include + +#include "router.hpp" + +namespace zmq +{ +class ctx_t; +class pipe_t; + +class stream_t ZMQ_FINAL : public routing_socket_base_t +{ + public: + stream_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~stream_t (); + + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_); + int xsend (zmq::msg_t *msg_); + int xrecv (zmq::msg_t *msg_); + bool xhas_in (); + bool xhas_out (); + void xread_activated (zmq::pipe_t *pipe_); + void xpipe_terminated (zmq::pipe_t *pipe_); + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + + private: + // Generate peer's id and update lookup map + void identify_peer (pipe_t *pipe_, bool locally_initiated_); + + // Fair queueing object for inbound pipes. + fq_t _fq; + + // True iff there is a message held in the pre-fetch buffer. + bool _prefetched; + + // If true, the receiver got the message part with + // the peer's identity. + bool _routing_id_sent; + + // Holds the prefetched identity. + msg_t _prefetched_routing_id; + + // Holds the prefetched message. + msg_t _prefetched_msg; + + // The pipe we are currently writing to. + zmq::pipe_t *_current_out; + + // If true, more outgoing message parts are expected. + bool _more_out; + + // Routing IDs are generated. It's a simple increment and wrap-over + // algorithm. This value is the next ID to use (if not used already). + uint32_t _next_integral_routing_id; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (stream_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/stream_connecter_base.cpp b/3rd/libzmq/src/stream_connecter_base.cpp new file mode 100644 index 00000000..138d7d61 --- /dev/null +++ b/3rd/libzmq/src/stream_connecter_base.cpp @@ -0,0 +1,199 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "stream_connecter_base.hpp" +#include "session_base.hpp" +#include "address.hpp" +#include "random.hpp" +#include "zmtp_engine.hpp" +#include "raw_engine.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#else +#include +#endif + +#include + +zmq::stream_connecter_base_t::stream_connecter_base_t ( + zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const zmq::options_t &options_, + zmq::address_t *addr_, + bool delayed_start_) : + own_t (io_thread_, options_), + io_object_t (io_thread_), + _addr (addr_), + _s (retired_fd), + _handle (static_cast (NULL)), + _socket (session_->get_socket ()), + _delayed_start (delayed_start_), + _reconnect_timer_started (false), + _current_reconnect_ivl (options.reconnect_ivl), + _session (session_) +{ + zmq_assert (_addr); + _addr->to_string (_endpoint); + // TODO the return value is unused! what if it fails? if this is impossible + // or does not matter, change such that endpoint in initialized using an + // initializer, and make endpoint const +} + +zmq::stream_connecter_base_t::~stream_connecter_base_t () +{ + zmq_assert (!_reconnect_timer_started); + zmq_assert (!_handle); + zmq_assert (_s == retired_fd); +} + +void zmq::stream_connecter_base_t::process_plug () +{ + if (_delayed_start) + add_reconnect_timer (); + else + start_connecting (); +} + +void zmq::stream_connecter_base_t::process_term (int linger_) +{ + if (_reconnect_timer_started) { + cancel_timer (reconnect_timer_id); + _reconnect_timer_started = false; + } + + if (_handle) { + rm_handle (); + } + + if (_s != retired_fd) + close (); + + own_t::process_term (linger_); +} + +void zmq::stream_connecter_base_t::add_reconnect_timer () +{ + if (options.reconnect_ivl > 0) { + const int interval = get_new_reconnect_ivl (); + add_timer (interval, reconnect_timer_id); + _socket->event_connect_retried ( + make_unconnected_connect_endpoint_pair (_endpoint), interval); + _reconnect_timer_started = true; + } +} + +int zmq::stream_connecter_base_t::get_new_reconnect_ivl () +{ + // TODO should the random jitter be really based on the configured initial + // reconnect interval options.reconnect_ivl, or better on the + // _current_reconnect_ivl? + + // The new interval is the current interval + random value. + const int random_jitter = generate_random () % options.reconnect_ivl; + const int interval = + _current_reconnect_ivl < std::numeric_limits::max () - random_jitter + ? _current_reconnect_ivl + random_jitter + : std::numeric_limits::max (); + + // Only change the new current reconnect interval if the maximum reconnect + // interval was set and if it's larger than the reconnect interval. + if (options.reconnect_ivl_max > 0 + && options.reconnect_ivl_max > options.reconnect_ivl) { + // Calculate the next interval + _current_reconnect_ivl = + _current_reconnect_ivl < std::numeric_limits::max () / 2 + ? std::min (_current_reconnect_ivl * 2, options.reconnect_ivl_max) + : options.reconnect_ivl_max; + } + + return interval; +} + +void zmq::stream_connecter_base_t::rm_handle () +{ + rm_fd (_handle); + _handle = static_cast (NULL); +} + +void zmq::stream_connecter_base_t::close () +{ + // TODO before, this was an assertion for _s != retired_fd, but this does not match usage of close + if (_s != retired_fd) { +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (_s); + wsa_assert (rc != SOCKET_ERROR); +#else + const int rc = ::close (_s); + errno_assert (rc == 0); +#endif + _socket->event_closed ( + make_unconnected_connect_endpoint_pair (_endpoint), _s); + _s = retired_fd; + } +} + +void zmq::stream_connecter_base_t::in_event () +{ + // We are not polling for incoming data, so we are actually called + // because of error here. However, we can get error on out event as well + // on some platforms, so we'll simply handle both events in the same way. + out_event (); +} + +void zmq::stream_connecter_base_t::create_engine ( + fd_t fd_, const std::string &local_address_) +{ + const endpoint_uri_pair_t endpoint_pair (local_address_, _endpoint, + endpoint_type_connect); + + // Create the engine object for this connection. + i_engine *engine; + if (options.raw_socket) + engine = new (std::nothrow) raw_engine_t (fd_, options, endpoint_pair); + else + engine = new (std::nothrow) zmtp_engine_t (fd_, options, endpoint_pair); + alloc_assert (engine); + + // Attach the engine to the corresponding session object. + send_attach (_session, engine); + + // Shut the connecter down. + terminate (); + + _socket->event_connected (endpoint_pair, fd_); +} + +void zmq::stream_connecter_base_t::timer_event (int id_) +{ + zmq_assert (id_ == reconnect_timer_id); + _reconnect_timer_started = false; + start_connecting (); +} diff --git a/3rd/libzmq/src/stream_connecter_base.hpp b/3rd/libzmq/src/stream_connecter_base.hpp new file mode 100644 index 00000000..008c9014 --- /dev/null +++ b/3rd/libzmq/src/stream_connecter_base.hpp @@ -0,0 +1,125 @@ +/* + 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 . +*/ + +#ifndef __STREAM_CONNECTER_BASE_HPP_INCLUDED__ +#define __STREAM_CONNECTER_BASE_HPP_INCLUDED__ + +#include "fd.hpp" +#include "own.hpp" +#include "io_object.hpp" + +namespace zmq +{ +class io_thread_t; +class session_base_t; +struct address_t; + +class stream_connecter_base_t : public own_t, public io_object_t +{ + public: + // If 'delayed_start' is true connecter first waits for a while, + // then starts connection process. + stream_connecter_base_t (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_); + + ~stream_connecter_base_t () ZMQ_OVERRIDE; + + protected: + // Handlers for incoming commands. + void process_plug () ZMQ_FINAL; + void process_term (int linger_) ZMQ_OVERRIDE; + + // Handlers for I/O events. + void in_event () ZMQ_OVERRIDE; + void timer_event (int id_) ZMQ_OVERRIDE; + + // Internal function to create the engine after connection was established. + virtual void create_engine (fd_t fd, const std::string &local_address_); + + // Internal function to add a reconnect timer + void add_reconnect_timer (); + + // Removes the handle from the poller. + void rm_handle (); + + // Close the connecting socket. + void close (); + + // Address to connect to. Owned by session_base_t. + // It is non-const since some parts may change during opening. + address_t *const _addr; + + // Underlying socket. + fd_t _s; + + // Handle corresponding to the listening socket, if file descriptor is + // registered with the poller, or NULL. + handle_t _handle; + + // String representation of endpoint to connect to + std::string _endpoint; + + // Socket + zmq::socket_base_t *const _socket; + + private: + // ID of the timer used to delay the reconnection. + enum + { + reconnect_timer_id = 1 + }; + + // Internal function to return a reconnect backoff delay. + // Will modify the current_reconnect_ivl used for next call + // Returns the currently used interval + int get_new_reconnect_ivl (); + + virtual void start_connecting () = 0; + + // If true, connecter is waiting a while before trying to connect. + const bool _delayed_start; + + // True iff a timer has been started. + bool _reconnect_timer_started; + + // Current reconnect ivl, updated for backoff strategy + int _current_reconnect_ivl; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (stream_connecter_base_t) + + protected: + // Reference to the session we belong to. + zmq::session_base_t *const _session; +}; +} + +#endif diff --git a/3rd/libzmq/src/stream_engine_base.cpp b/3rd/libzmq/src/stream_engine_base.cpp new file mode 100644 index 00000000..bb64e0db --- /dev/null +++ b/3rd/libzmq/src/stream_engine_base.cpp @@ -0,0 +1,787 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" + +#include +#include + +#ifndef ZMQ_HAVE_WINDOWS +#include +#endif + +#include +#include + +#include "stream_engine_base.hpp" +#include "io_thread.hpp" +#include "session_base.hpp" +#include "v1_encoder.hpp" +#include "v1_decoder.hpp" +#include "v2_encoder.hpp" +#include "v2_decoder.hpp" +#include "null_mechanism.hpp" +#include "plain_client.hpp" +#include "plain_server.hpp" +#include "gssapi_client.hpp" +#include "gssapi_server.hpp" +#include "curve_client.hpp" +#include "curve_server.hpp" +#include "raw_decoder.hpp" +#include "raw_encoder.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "likely.hpp" +#include "wire.hpp" + +static std::string get_peer_address (zmq::fd_t s_) +{ + std::string peer_address; + + const int family = zmq::get_peer_ip_address (s_, peer_address); + if (family == 0) + peer_address.clear (); +#if defined ZMQ_HAVE_SO_PEERCRED + else if (family == PF_UNIX) { + struct ucred cred; + socklen_t size = sizeof (cred); + if (!getsockopt (s_, SOL_SOCKET, SO_PEERCRED, &cred, &size)) { + std::ostringstream buf; + buf << ":" << cred.uid << ":" << cred.gid << ":" << cred.pid; + peer_address += buf.str (); + } + } +#elif defined ZMQ_HAVE_LOCAL_PEERCRED + else if (family == PF_UNIX) { + struct xucred cred; + socklen_t size = sizeof (cred); + if (!getsockopt (s_, 0, LOCAL_PEERCRED, &cred, &size) + && cred.cr_version == XUCRED_VERSION) { + std::ostringstream buf; + buf << ":" << cred.cr_uid << ":"; + if (cred.cr_ngroups > 0) + buf << cred.cr_groups[0]; + buf << ":"; + peer_address += buf.str (); + } + } +#endif + + return peer_address; +} + +zmq::stream_engine_base_t::stream_engine_base_t ( + fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_, + bool has_handshake_stage_) : + _options (options_), + _inpos (NULL), + _insize (0), + _decoder (NULL), + _outpos (NULL), + _outsize (0), + _encoder (NULL), + _mechanism (NULL), + _next_msg (NULL), + _process_msg (NULL), + _metadata (NULL), + _input_stopped (false), + _output_stopped (false), + _endpoint_uri_pair (endpoint_uri_pair_), + _has_handshake_timer (false), + _has_ttl_timer (false), + _has_timeout_timer (false), + _has_heartbeat_timer (false), + _peer_address (get_peer_address (fd_)), + _s (fd_), + _handle (static_cast (NULL)), + _plugged (false), + _handshaking (true), + _io_error (false), + _session (NULL), + _socket (NULL), + _has_handshake_stage (has_handshake_stage_) +{ + const int rc = _tx_msg.init (); + errno_assert (rc == 0); + + // Put the socket into non-blocking mode. + unblock_socket (_s); +} + +zmq::stream_engine_base_t::~stream_engine_base_t () +{ + zmq_assert (!_plugged); + + if (_s != retired_fd) { +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (_s); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = close (_s); +#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) + // FreeBSD may return ECONNRESET on close() under load but this is not + // an error. + if (rc == -1 && errno == ECONNRESET) + rc = 0; +#endif + errno_assert (rc == 0); +#endif + _s = retired_fd; + } + + const int rc = _tx_msg.close (); + errno_assert (rc == 0); + + // Drop reference to metadata and destroy it if we are + // the only user. + if (_metadata != NULL) { + if (_metadata->drop_ref ()) { + LIBZMQ_DELETE (_metadata); + } + } + + LIBZMQ_DELETE (_encoder); + LIBZMQ_DELETE (_decoder); + LIBZMQ_DELETE (_mechanism); +} + +void zmq::stream_engine_base_t::plug (io_thread_t *io_thread_, + session_base_t *session_) +{ + zmq_assert (!_plugged); + _plugged = true; + + // Connect to session object. + zmq_assert (!_session); + zmq_assert (session_); + _session = session_; + _socket = _session->get_socket (); + + // Connect to I/O threads poller object. + io_object_t::plug (io_thread_); + _handle = add_fd (_s); + _io_error = false; + + plug_internal (); +} + +void zmq::stream_engine_base_t::unplug () +{ + zmq_assert (_plugged); + _plugged = false; + + // Cancel all timers. + if (_has_handshake_timer) { + cancel_timer (handshake_timer_id); + _has_handshake_timer = false; + } + + if (_has_ttl_timer) { + cancel_timer (heartbeat_ttl_timer_id); + _has_ttl_timer = false; + } + + if (_has_timeout_timer) { + cancel_timer (heartbeat_timeout_timer_id); + _has_timeout_timer = false; + } + + if (_has_heartbeat_timer) { + cancel_timer (heartbeat_ivl_timer_id); + _has_heartbeat_timer = false; + } + // Cancel all fd subscriptions. + if (!_io_error) + rm_fd (_handle); + + // Disconnect from I/O threads poller object. + io_object_t::unplug (); + + _session = NULL; +} + +void zmq::stream_engine_base_t::terminate () +{ + unplug (); + delete this; +} + +void zmq::stream_engine_base_t::in_event () +{ + // ignore errors + const bool res = in_event_internal (); + LIBZMQ_UNUSED (res); +} + +bool zmq::stream_engine_base_t::in_event_internal () +{ + zmq_assert (!_io_error); + + // If still handshaking, receive and process the greeting message. + if (unlikely (_handshaking)) { + if (handshake ()) { + // Handshaking was successful. + // Switch into the normal message flow. + _handshaking = false; + + if (_mechanism == NULL && _has_handshake_stage) + _session->engine_ready (); + } else + return false; + } + + + zmq_assert (_decoder); + + // If there has been an I/O error, stop polling. + if (_input_stopped) { + rm_fd (_handle); + _io_error = true; + return true; // TODO or return false in this case too? + } + + // If there's no data to process in the buffer... + if (!_insize) { + // Retrieve the buffer and read as much data as possible. + // Note that buffer can be arbitrarily large. However, we assume + // the underlying TCP layer has fixed buffer size and thus the + // number of bytes read will be always limited. + size_t bufsize = 0; + _decoder->get_buffer (&_inpos, &bufsize); + + const int rc = read (_inpos, bufsize); + + if (rc == -1) { + if (errno != EAGAIN) { + error (connection_error); + return false; + } + return true; + } + + // Adjust input size + _insize = static_cast (rc); + // Adjust buffer size to received bytes + _decoder->resize_buffer (_insize); + } + + int rc = 0; + size_t processed = 0; + + while (_insize > 0) { + rc = _decoder->decode (_inpos, _insize, processed); + zmq_assert (processed <= _insize); + _inpos += processed; + _insize -= processed; + if (rc == 0 || rc == -1) + break; + rc = (this->*_process_msg) (_decoder->msg ()); + if (rc == -1) + break; + } + + // Tear down the connection if we have failed to decode input data + // or the session has rejected the message. + if (rc == -1) { + if (errno != EAGAIN) { + error (protocol_error); + return false; + } + _input_stopped = true; + reset_pollin (_handle); + } + + _session->flush (); + return true; +} + +void zmq::stream_engine_base_t::out_event () +{ + zmq_assert (!_io_error); + + // If write buffer is empty, try to read new data from the encoder. + if (!_outsize) { + // Even when we stop polling as soon as there is no + // data to send, the poller may invoke out_event one + // more time due to 'speculative write' optimisation. + if (unlikely (_encoder == NULL)) { + zmq_assert (_handshaking); + return; + } + + _outpos = NULL; + _outsize = _encoder->encode (&_outpos, 0); + + while (_outsize < static_cast (_options.out_batch_size)) { + if ((this->*_next_msg) (&_tx_msg) == -1) { + // ws_engine can cause an engine error and delete it, so + // bail out immediately to avoid use-after-free + if (errno == ECONNRESET) + return; + else + break; + } + _encoder->load_msg (&_tx_msg); + unsigned char *bufptr = _outpos + _outsize; + const size_t n = + _encoder->encode (&bufptr, _options.out_batch_size - _outsize); + zmq_assert (n > 0); + if (_outpos == NULL) + _outpos = bufptr; + _outsize += n; + } + + // If there is no data to send, stop polling for output. + if (_outsize == 0) { + _output_stopped = true; + reset_pollout (); + return; + } + } + + // If there are any data to write in write buffer, write as much as + // possible to the socket. Note that amount of data to write can be + // 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 = write (_outpos, _outsize); + + // IO error has occurred. We stop waiting for output events. + // The engine is not terminated until we detect input error; + // this is necessary to prevent losing incoming messages. + if (nbytes == -1) { + reset_pollout (); + return; + } + + _outpos += nbytes; + _outsize -= nbytes; + + // If we are still handshaking and there are no data + // to send, stop polling for output. + if (unlikely (_handshaking)) + if (_outsize == 0) + reset_pollout (); +} + +void zmq::stream_engine_base_t::restart_output () +{ + if (unlikely (_io_error)) + return; + + if (likely (_output_stopped)) { + set_pollout (); + _output_stopped = false; + } + + // Speculative write: The assumption is that at the moment new message + // was sent by the user the socket is probably available for writing. + // Thus we try to write the data to socket avoiding polling for POLLOUT. + // Consequently, the latency should be better in request/reply scenarios. + out_event (); +} + +bool zmq::stream_engine_base_t::restart_input () +{ + zmq_assert (_input_stopped); + zmq_assert (_session != NULL); + zmq_assert (_decoder != NULL); + + int rc = (this->*_process_msg) (_decoder->msg ()); + if (rc == -1) { + if (errno == EAGAIN) + _session->flush (); + else { + error (protocol_error); + return false; + } + return true; + } + + while (_insize > 0) { + size_t processed = 0; + rc = _decoder->decode (_inpos, _insize, processed); + zmq_assert (processed <= _insize); + _inpos += processed; + _insize -= processed; + if (rc == 0 || rc == -1) + break; + rc = (this->*_process_msg) (_decoder->msg ()); + if (rc == -1) + break; + } + + if (rc == -1 && errno == EAGAIN) + _session->flush (); + else if (_io_error) { + error (connection_error); + return false; + } else if (rc == -1) { + error (protocol_error); + return false; + } + + else { + _input_stopped = false; + set_pollin (); + _session->flush (); + + // Speculative read. + if (!in_event_internal ()) + return false; + } + + return true; +} + +int zmq::stream_engine_base_t::next_handshake_command (msg_t *msg_) +{ + zmq_assert (_mechanism != NULL); + + if (_mechanism->status () == mechanism_t::ready) { + mechanism_ready (); + return pull_and_encode (msg_); + } + if (_mechanism->status () == mechanism_t::error) { + errno = EPROTO; + return -1; + } + const int rc = _mechanism->next_handshake_command (msg_); + + if (rc == 0) + msg_->set_flags (msg_t::command); + + return rc; +} + +int zmq::stream_engine_base_t::process_handshake_command (msg_t *msg_) +{ + zmq_assert (_mechanism != NULL); + const int rc = _mechanism->process_handshake_command (msg_); + if (rc == 0) { + if (_mechanism->status () == mechanism_t::ready) + mechanism_ready (); + else if (_mechanism->status () == mechanism_t::error) { + errno = EPROTO; + return -1; + } + if (_output_stopped) + restart_output (); + } + + return rc; +} + +void zmq::stream_engine_base_t::zap_msg_available () +{ + zmq_assert (_mechanism != NULL); + + const int rc = _mechanism->zap_msg_available (); + if (rc == -1) { + error (protocol_error); + return; + } + if (_input_stopped) + if (!restart_input ()) + return; + if (_output_stopped) + restart_output (); +} + +const zmq::endpoint_uri_pair_t &zmq::stream_engine_base_t::get_endpoint () const +{ + return _endpoint_uri_pair; +} + +void zmq::stream_engine_base_t::mechanism_ready () +{ + if (_options.heartbeat_interval > 0 && !_has_heartbeat_timer) { + add_timer (_options.heartbeat_interval, heartbeat_ivl_timer_id); + _has_heartbeat_timer = true; + } + + if (_has_handshake_stage) + _session->engine_ready (); + + bool flush_session = false; + + if (_options.recv_routing_id) { + msg_t routing_id; + _mechanism->peer_routing_id (&routing_id); + const int rc = _session->push_msg (&routing_id); + if (rc == -1 && errno == EAGAIN) { + // If the write is failing at this stage with + // an EAGAIN the pipe must be being shut down, + // so we can just bail out of the routing id set. + return; + } + errno_assert (rc == 0); + flush_session = true; + } + + if (_options.router_notify & ZMQ_NOTIFY_CONNECT) { + msg_t connect_notification; + connect_notification.init (); + const int rc = _session->push_msg (&connect_notification); + if (rc == -1 && errno == EAGAIN) { + // If the write is failing at this stage with + // an EAGAIN the pipe must be being shut down, + // so we can just bail out of the notification. + return; + } + errno_assert (rc == 0); + flush_session = true; + } + + if (flush_session) + _session->flush (); + + _next_msg = &stream_engine_base_t::pull_and_encode; + _process_msg = &stream_engine_base_t::write_credential; + + // Compile metadata. + properties_t properties; + init_properties (properties); + + // Add ZAP properties. + const properties_t &zap_properties = _mechanism->get_zap_properties (); + properties.insert (zap_properties.begin (), zap_properties.end ()); + + // Add ZMTP properties. + const properties_t &zmtp_properties = _mechanism->get_zmtp_properties (); + properties.insert (zmtp_properties.begin (), zmtp_properties.end ()); + + zmq_assert (_metadata == NULL); + if (!properties.empty ()) { + _metadata = new (std::nothrow) metadata_t (properties); + alloc_assert (_metadata); + } + + if (_has_handshake_timer) { + cancel_timer (handshake_timer_id); + _has_handshake_timer = false; + } + + _socket->event_handshake_succeeded (_endpoint_uri_pair, 0); +} + +int zmq::stream_engine_base_t::write_credential (msg_t *msg_) +{ + zmq_assert (_mechanism != NULL); + zmq_assert (_session != NULL); + + const blob_t &credential = _mechanism->get_user_id (); + if (credential.size () > 0) { + msg_t msg; + int rc = msg.init_size (credential.size ()); + zmq_assert (rc == 0); + memcpy (msg.data (), credential.data (), credential.size ()); + msg.set_flags (msg_t::credential); + rc = _session->push_msg (&msg); + if (rc == -1) { + rc = msg.close (); + errno_assert (rc == 0); + return -1; + } + } + _process_msg = &stream_engine_base_t::decode_and_push; + return decode_and_push (msg_); +} + +int zmq::stream_engine_base_t::pull_and_encode (msg_t *msg_) +{ + zmq_assert (_mechanism != NULL); + + if (_session->pull_msg (msg_) == -1) + return -1; + if (_mechanism->encode (msg_) == -1) + return -1; + return 0; +} + +int zmq::stream_engine_base_t::decode_and_push (msg_t *msg_) +{ + zmq_assert (_mechanism != NULL); + + if (_mechanism->decode (msg_) == -1) + return -1; + + if (_has_timeout_timer) { + _has_timeout_timer = false; + cancel_timer (heartbeat_timeout_timer_id); + } + + if (_has_ttl_timer) { + _has_ttl_timer = false; + cancel_timer (heartbeat_ttl_timer_id); + } + + if (msg_->flags () & msg_t::command) { + process_command_message (msg_); + } + + if (_metadata) + msg_->set_metadata (_metadata); + if (_session->push_msg (msg_) == -1) { + if (errno == EAGAIN) + _process_msg = &stream_engine_base_t::push_one_then_decode_and_push; + return -1; + } + return 0; +} + +int zmq::stream_engine_base_t::push_one_then_decode_and_push (msg_t *msg_) +{ + const int rc = _session->push_msg (msg_); + if (rc == 0) + _process_msg = &stream_engine_base_t::decode_and_push; + return rc; +} + +int zmq::stream_engine_base_t::pull_msg_from_session (msg_t *msg_) +{ + return _session->pull_msg (msg_); +} + +int zmq::stream_engine_base_t::push_msg_to_session (msg_t *msg_) +{ + return _session->push_msg (msg_); +} + +void zmq::stream_engine_base_t::error (error_reason_t reason_) +{ + zmq_assert (_session); + + if ((_options.router_notify & ZMQ_NOTIFY_DISCONNECT) && !_handshaking) { + // For router sockets with disconnect notification, rollback + // any incomplete message in the pipe, and push the disconnect + // notification message. + _session->rollback (); + + msg_t disconnect_notification; + disconnect_notification.init (); + _session->push_msg (&disconnect_notification); + } + + // protocol errors have been signaled already at the point where they occurred + if (reason_ != protocol_error + && (_mechanism == NULL + || _mechanism->status () == mechanism_t::handshaking)) { + const int err = errno; + _socket->event_handshake_failed_no_detail (_endpoint_uri_pair, err); + // special case: connecting to non-ZMTP process which immediately drops connection, + // or which never responds with greeting, should be treated as a protocol error + // (i.e. stop reconnect) + if (((reason_ == connection_error) || (reason_ == timeout_error)) + && (_options.reconnect_stop + & ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED)) { + reason_ = protocol_error; + } + } + + _socket->event_disconnected (_endpoint_uri_pair, _s); + _session->flush (); + _session->engine_error ( + !_handshaking + && (_mechanism == NULL + || _mechanism->status () != mechanism_t::handshaking), + reason_); + unplug (); + delete this; +} + +void zmq::stream_engine_base_t::set_handshake_timer () +{ + zmq_assert (!_has_handshake_timer); + + if (_options.handshake_ivl > 0) { + add_timer (_options.handshake_ivl, handshake_timer_id); + _has_handshake_timer = true; + } +} + +bool zmq::stream_engine_base_t::init_properties (properties_t &properties_) +{ + if (_peer_address.empty ()) + return false; + properties_.ZMQ_MAP_INSERT_OR_EMPLACE ( + std::string (ZMQ_MSG_PROPERTY_PEER_ADDRESS), _peer_address); + + // Private property to support deprecated SRCFD + std::ostringstream stream; + stream << static_cast (_s); + std::string fd_string = stream.str (); + properties_.ZMQ_MAP_INSERT_OR_EMPLACE (std::string ("__fd"), + ZMQ_MOVE (fd_string)); + return true; +} + +void zmq::stream_engine_base_t::timer_event (int id_) +{ + if (id_ == handshake_timer_id) { + _has_handshake_timer = false; + // handshake timer expired before handshake completed, so engine fail + error (timeout_error); + } else if (id_ == heartbeat_ivl_timer_id) { + _next_msg = &stream_engine_base_t::produce_ping_message; + out_event (); + add_timer (_options.heartbeat_interval, heartbeat_ivl_timer_id); + } else if (id_ == heartbeat_ttl_timer_id) { + _has_ttl_timer = false; + error (timeout_error); + } else if (id_ == heartbeat_timeout_timer_id) { + _has_timeout_timer = false; + error (timeout_error); + } else + // There are no other valid timer ids! + assert (false); +} + +int zmq::stream_engine_base_t::read (void *data_, size_t size_) +{ + const 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_); +} diff --git a/3rd/libzmq/src/stream_engine_base.hpp b/3rd/libzmq/src/stream_engine_base.hpp new file mode 100644 index 00000000..58bf9579 --- /dev/null +++ b/3rd/libzmq/src/stream_engine_base.hpp @@ -0,0 +1,221 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_STREAM_ENGINE_BASE_HPP_INCLUDED__ +#define __ZMQ_STREAM_ENGINE_BASE_HPP_INCLUDED__ + +#include + +#include "fd.hpp" +#include "i_engine.hpp" +#include "io_object.hpp" +#include "i_encoder.hpp" +#include "i_decoder.hpp" +#include "options.hpp" +#include "socket_base.hpp" +#include "metadata.hpp" +#include "msg.hpp" +#include "tcp.hpp" + +namespace zmq +{ +class io_thread_t; +class session_base_t; +class mechanism_t; + +// This engine handles any socket with SOCK_STREAM semantics, +// e.g. TCP socket or an UNIX domain socket. + +class stream_engine_base_t : public io_object_t, public i_engine +{ + public: + stream_engine_base_t (fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_, + bool has_handshake_stage_); + ~stream_engine_base_t () ZMQ_OVERRIDE; + + // i_engine interface implementation. + bool has_handshake_stage () ZMQ_FINAL { return _has_handshake_stage; }; + void plug (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_) ZMQ_FINAL; + void terminate () ZMQ_FINAL; + bool restart_input () ZMQ_FINAL; + void restart_output () ZMQ_FINAL; + void zap_msg_available () ZMQ_FINAL; + const endpoint_uri_pair_t &get_endpoint () const ZMQ_FINAL; + + // i_poll_events interface implementation. + void in_event () ZMQ_FINAL; + void out_event (); + void timer_event (int id_) ZMQ_FINAL; + + protected: + typedef metadata_t::dict_t properties_t; + bool init_properties (properties_t &properties_); + + // Function to handle network disconnections. + virtual void error (error_reason_t reason_); + + int next_handshake_command (msg_t *msg_); + int process_handshake_command (msg_t *msg_); + + int pull_msg_from_session (msg_t *msg_); + int push_msg_to_session (msg_t *msg_); + + int pull_and_encode (msg_t *msg_); + virtual int decode_and_push (msg_t *msg_); + int push_one_then_decode_and_push (msg_t *msg_); + + void set_handshake_timer (); + + virtual bool handshake () { return true; }; + virtual void plug_internal (){}; + + virtual int process_command_message (msg_t *msg_) + { + LIBZMQ_UNUSED (msg_); + return -1; + }; + virtual int produce_ping_message (msg_t *msg_) + { + LIBZMQ_UNUSED (msg_); + return -1; + }; + virtual int process_heartbeat_message (msg_t *msg_) + { + LIBZMQ_UNUSED (msg_); + return -1; + }; + virtual int produce_pong_message (msg_t *msg_) + { + LIBZMQ_UNUSED (msg_); + return -1; + }; + + virtual int read (void *data, size_t size_); + virtual int write (const void *data_, size_t size_); + + void reset_pollout () { io_object_t::reset_pollout (_handle); } + void set_pollout () { io_object_t::set_pollout (_handle); } + void set_pollin () { io_object_t::set_pollin (_handle); } + session_base_t *session () { return _session; } + socket_base_t *socket () { return _socket; } + + const options_t _options; + + unsigned char *_inpos; + size_t _insize; + i_decoder *_decoder; + + unsigned char *_outpos; + size_t _outsize; + i_encoder *_encoder; + + mechanism_t *_mechanism; + + int (stream_engine_base_t::*_next_msg) (msg_t *msg_); + int (stream_engine_base_t::*_process_msg) (msg_t *msg_); + + // Metadata to be attached to received messages. May be NULL. + metadata_t *_metadata; + + // True iff the engine couldn't consume the last decoded message. + bool _input_stopped; + + // True iff the engine doesn't have any message to encode. + bool _output_stopped; + + // Representation of the connected endpoints. + const endpoint_uri_pair_t _endpoint_uri_pair; + + // ID of the handshake timer + enum + { + handshake_timer_id = 0x40 + }; + + // True is linger timer is running. + bool _has_handshake_timer; + + // Heartbeat stuff + enum + { + heartbeat_ivl_timer_id = 0x80, + heartbeat_timeout_timer_id = 0x81, + heartbeat_ttl_timer_id = 0x82 + }; + bool _has_ttl_timer; + bool _has_timeout_timer; + bool _has_heartbeat_timer; + + + const std::string _peer_address; + + private: + bool in_event_internal (); + + // Unplug the engine from the session. + void unplug (); + + int write_credential (msg_t *msg_); + + void mechanism_ready (); + + // Underlying socket. + fd_t _s; + + handle_t _handle; + + bool _plugged; + + // When true, we are still trying to determine whether + // the peer is using versioned protocol, and if so, which + // version. When false, normal message flow has started. + bool _handshaking; + + msg_t _tx_msg; + + bool _io_error; + + // The session this engine is attached to. + zmq::session_base_t *_session; + + // Socket + zmq::socket_base_t *_socket; + + // Indicate if engine has an handshake stage, if it does, engine must call session.engine_ready + // when handshake is completed. + bool _has_handshake_stage; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (stream_engine_base_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/stream_listener_base.cpp b/3rd/libzmq/src/stream_listener_base.cpp new file mode 100644 index 00000000..ecd21be0 --- /dev/null +++ b/3rd/libzmq/src/stream_listener_base.cpp @@ -0,0 +1,127 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "stream_listener_base.hpp" +#include "session_base.hpp" +#include "socket_base.hpp" +#include "zmtp_engine.hpp" +#include "raw_engine.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#else +#include +#endif + +zmq::stream_listener_base_t::stream_listener_base_t ( + zmq::io_thread_t *io_thread_, + zmq::socket_base_t *socket_, + const zmq::options_t &options_) : + own_t (io_thread_, options_), + io_object_t (io_thread_), + _s (retired_fd), + _handle (static_cast (NULL)), + _socket (socket_) +{ +} + +zmq::stream_listener_base_t::~stream_listener_base_t () +{ + zmq_assert (_s == retired_fd); + zmq_assert (!_handle); +} + +int zmq::stream_listener_base_t::get_local_address (std::string &addr_) const +{ + addr_ = get_socket_name (_s, socket_end_local); + return addr_.empty () ? -1 : 0; +} + +void zmq::stream_listener_base_t::process_plug () +{ + // Start polling for incoming connections. + _handle = add_fd (_s); + set_pollin (_handle); +} + +void zmq::stream_listener_base_t::process_term (int linger_) +{ + rm_fd (_handle); + _handle = static_cast (NULL); + close (); + own_t::process_term (linger_); +} + +int zmq::stream_listener_base_t::close () +{ + // TODO this is identical to stream_connector_base_t::close + + zmq_assert (_s != retired_fd); +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (_s); + wsa_assert (rc != SOCKET_ERROR); +#else + const int rc = ::close (_s); + errno_assert (rc == 0); +#endif + _socket->event_closed (make_unconnected_bind_endpoint_pair (_endpoint), _s); + _s = retired_fd; + + return 0; +} + +void zmq::stream_listener_base_t::create_engine (fd_t fd_) +{ + const endpoint_uri_pair_t endpoint_pair ( + get_socket_name (fd_, socket_end_local), + get_socket_name (fd_, socket_end_remote), endpoint_type_bind); + + i_engine *engine; + if (options.raw_socket) + engine = new (std::nothrow) raw_engine_t (fd_, options, endpoint_pair); + else + engine = new (std::nothrow) zmtp_engine_t (fd_, options, endpoint_pair); + alloc_assert (engine); + + // Choose I/O thread to run connecter in. Given that we are already + // running in an I/O thread, there must be at least one available. + io_thread_t *io_thread = choose_io_thread (options.affinity); + zmq_assert (io_thread); + + // Create and launch a session object. + session_base_t *session = + session_base_t::create (io_thread, false, _socket, options, NULL); + errno_assert (session); + session->inc_seqnum (); + launch_child (session); + send_attach (session, engine, false); + + _socket->event_accepted (endpoint_pair, fd_); +} diff --git a/3rd/libzmq/src/stream_listener_base.hpp b/3rd/libzmq/src/stream_listener_base.hpp new file mode 100644 index 00000000..71582541 --- /dev/null +++ b/3rd/libzmq/src/stream_listener_base.hpp @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_STREAM_LISTENER_BASE_HPP_INCLUDED__ +#define __ZMQ_STREAM_LISTENER_BASE_HPP_INCLUDED__ + +#include + +#include "fd.hpp" +#include "own.hpp" +#include "stdint.hpp" +#include "io_object.hpp" +#include "address.hpp" + +namespace zmq +{ +class io_thread_t; +class socket_base_t; + +class stream_listener_base_t : public own_t, public io_object_t +{ + public: + stream_listener_base_t (zmq::io_thread_t *io_thread_, + zmq::socket_base_t *socket_, + const options_t &options_); + ~stream_listener_base_t () ZMQ_OVERRIDE; + + // Get the bound address for use with wildcards + int get_local_address (std::string &addr_) const; + + protected: + virtual std::string get_socket_name (fd_t fd_, + socket_end_t socket_end_) const = 0; + + private: + // Handlers for incoming commands. + void process_plug () ZMQ_FINAL; + void process_term (int linger_) ZMQ_FINAL; + + protected: + // Close the listening socket. + virtual int close (); + + virtual void create_engine (fd_t fd); + + // Underlying socket. + fd_t _s; + + // Handle corresponding to the listening socket. + handle_t _handle; + + // Socket the listener belongs to. + zmq::socket_base_t *_socket; + + // String representation of endpoint to bind to + std::string _endpoint; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (stream_listener_base_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/sub.cpp b/3rd/libzmq/src/sub.cpp new file mode 100644 index 00000000..7240054a --- /dev/null +++ b/3rd/libzmq/src/sub.cpp @@ -0,0 +1,84 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "sub.hpp" +#include "msg.hpp" + +zmq::sub_t::sub_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + xsub_t (parent_, tid_, sid_) +{ + options.type = ZMQ_SUB; + + // Switch filtering messages on (as opposed to XSUB which where the + // filtering is off). + options.filter = true; +} + +zmq::sub_t::~sub_t () +{ +} + +int zmq::sub_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + if (option_ != ZMQ_SUBSCRIBE && option_ != ZMQ_UNSUBSCRIBE) { + errno = EINVAL; + return -1; + } + + // Create the subscription message. + msg_t msg; + int rc; + const unsigned char *data = static_cast (optval_); + if (option_ == ZMQ_SUBSCRIBE) { + rc = msg.init_subscribe (optvallen_, data); + } else { + rc = msg.init_cancel (optvallen_, data); + } + errno_assert (rc == 0); + + // Pass it further on in the stack. + rc = xsub_t::xsend (&msg); + return close_and_return (&msg, rc); +} + +int zmq::sub_t::xsend (msg_t *) +{ + // Override the XSUB's send. + errno = ENOTSUP; + return -1; +} + +bool zmq::sub_t::xhas_out () +{ + // Override the XSUB's send. + return false; +} diff --git a/3rd/libzmq/src/sub.hpp b/3rd/libzmq/src/sub.hpp new file mode 100644 index 00000000..ee519c89 --- /dev/null +++ b/3rd/libzmq/src/sub.hpp @@ -0,0 +1,57 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_SUB_HPP_INCLUDED__ +#define __ZMQ_SUB_HPP_INCLUDED__ + +#include "xsub.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class io_thread_t; +class socket_base_t; + +class sub_t ZMQ_FINAL : public xsub_t +{ + public: + sub_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~sub_t (); + + protected: + int xsetsockopt (int option_, const void *optval_, size_t optvallen_); + int xsend (zmq::msg_t *msg_); + bool xhas_out (); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (sub_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/tcp.cpp b/3rd/libzmq/src/tcp.cpp new file mode 100644 index 00000000..c37bed02 --- /dev/null +++ b/3rd/libzmq/src/tcp.cpp @@ -0,0 +1,412 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "err.hpp" +#include "options.hpp" + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#endif + +#if defined ZMQ_HAVE_OPENVMS +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +int zmq::tune_tcp_socket (fd_t s_) +{ + // Disable Nagle's algorithm. We are doing data batching on 0MQ level, + // so using Nagle wouldn't improve throughput in anyway, but it would + // hurt latency. + int nodelay = 1; + const int rc = + setsockopt (s_, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast (&nodelay), sizeof (int)); + assert_success_or_recoverable (s_, rc); + if (rc != 0) + return rc; + +#ifdef ZMQ_HAVE_OPENVMS + // Disable delayed acknowledgements as they hurt latency significantly. + int nodelack = 1; + rc = setsockopt (s_, IPPROTO_TCP, TCP_NODELACK, (char *) &nodelack, + sizeof (int)); + assert_success_or_recoverable (s_, rc); +#endif + return rc; +} + +int zmq::set_tcp_send_buffer (fd_t sockfd_, int bufsize_) +{ + const int rc = + setsockopt (sockfd_, SOL_SOCKET, SO_SNDBUF, + reinterpret_cast (&bufsize_), sizeof bufsize_); + assert_success_or_recoverable (sockfd_, rc); + return rc; +} + +int zmq::set_tcp_receive_buffer (fd_t sockfd_, int bufsize_) +{ + const int rc = + setsockopt (sockfd_, SOL_SOCKET, SO_RCVBUF, + reinterpret_cast (&bufsize_), sizeof bufsize_); + assert_success_or_recoverable (sockfd_, rc); + return rc; +} + +int zmq::tune_tcp_keepalives (fd_t s_, + int keepalive_, + int keepalive_cnt_, + int keepalive_idle_, + int keepalive_intvl_) +{ + // These options are used only under certain #ifdefs below. + LIBZMQ_UNUSED (keepalive_); + LIBZMQ_UNUSED (keepalive_cnt_); + LIBZMQ_UNUSED (keepalive_idle_); + LIBZMQ_UNUSED (keepalive_intvl_); + + // If none of the #ifdefs apply, then s_ is unused. + LIBZMQ_UNUSED (s_); + + // Tuning TCP keep-alives if platform allows it + // All values = -1 means skip and leave it for OS +#ifdef ZMQ_HAVE_WINDOWS + if (keepalive_ != -1) { + tcp_keepalive keepalive_opts; + keepalive_opts.onoff = keepalive_; + keepalive_opts.keepalivetime = + keepalive_idle_ != -1 ? keepalive_idle_ * 1000 : 7200000; + keepalive_opts.keepaliveinterval = + keepalive_intvl_ != -1 ? keepalive_intvl_ * 1000 : 1000; + DWORD num_bytes_returned; + const int rc = WSAIoctl (s_, SIO_KEEPALIVE_VALS, &keepalive_opts, + sizeof (keepalive_opts), NULL, 0, + &num_bytes_returned, NULL, NULL); + assert_success_or_recoverable (s_, rc); + if (rc == SOCKET_ERROR) + return rc; + } +#else +#ifdef ZMQ_HAVE_SO_KEEPALIVE + if (keepalive_ != -1) { + int rc = + setsockopt (s_, SOL_SOCKET, SO_KEEPALIVE, + reinterpret_cast (&keepalive_), sizeof (int)); + assert_success_or_recoverable (s_, rc); + if (rc != 0) + return rc; + +#ifdef ZMQ_HAVE_TCP_KEEPCNT + if (keepalive_cnt_ != -1) { + int rc = setsockopt (s_, IPPROTO_TCP, TCP_KEEPCNT, &keepalive_cnt_, + sizeof (int)); + assert_success_or_recoverable (s_, rc); + if (rc != 0) + return rc; + } +#endif // ZMQ_HAVE_TCP_KEEPCNT + +#ifdef ZMQ_HAVE_TCP_KEEPIDLE + if (keepalive_idle_ != -1) { + int rc = setsockopt (s_, IPPROTO_TCP, TCP_KEEPIDLE, + &keepalive_idle_, sizeof (int)); + assert_success_or_recoverable (s_, rc); + if (rc != 0) + return rc; + } +#else // ZMQ_HAVE_TCP_KEEPIDLE +#ifdef ZMQ_HAVE_TCP_KEEPALIVE + if (keepalive_idle_ != -1) { + int rc = setsockopt (s_, IPPROTO_TCP, TCP_KEEPALIVE, + &keepalive_idle_, sizeof (int)); + assert_success_or_recoverable (s_, rc); + if (rc != 0) + return rc; + } +#endif // ZMQ_HAVE_TCP_KEEPALIVE +#endif // ZMQ_HAVE_TCP_KEEPIDLE + +#ifdef ZMQ_HAVE_TCP_KEEPINTVL + if (keepalive_intvl_ != -1) { + int rc = setsockopt (s_, IPPROTO_TCP, TCP_KEEPINTVL, + &keepalive_intvl_, sizeof (int)); + assert_success_or_recoverable (s_, rc); + if (rc != 0) + return rc; + } +#endif // ZMQ_HAVE_TCP_KEEPINTVL + } +#endif // ZMQ_HAVE_SO_KEEPALIVE +#endif // ZMQ_HAVE_WINDOWS + + return 0; +} + +int zmq::tune_tcp_maxrt (fd_t sockfd_, int timeout_) +{ + if (timeout_ <= 0) + return 0; + + LIBZMQ_UNUSED (sockfd_); + +#if defined(ZMQ_HAVE_WINDOWS) && defined(TCP_MAXRT) + // msdn says it's supported in >= Vista, >= Windows Server 2003 + timeout_ /= 1000; // in seconds + const int rc = + setsockopt (sockfd_, IPPROTO_TCP, TCP_MAXRT, + reinterpret_cast (&timeout_), sizeof (timeout_)); + assert_success_or_recoverable (sockfd_, rc); + return rc; +// FIXME: should be ZMQ_HAVE_TCP_USER_TIMEOUT +#elif defined(TCP_USER_TIMEOUT) + int rc = setsockopt (sockfd_, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout_, + sizeof (timeout_)); + assert_success_or_recoverable (sockfd_, rc); + return rc; +#else + return 0; +#endif +} + +int zmq::tcp_write (fd_t s_, const void *data_, size_t size_) +{ +#ifdef ZMQ_HAVE_WINDOWS + + const int nbytes = send (s_, (char *) data_, static_cast (size_), 0); + + // If not a single byte can be written to the socket in non-blocking mode + // we'll get an error (this may happen during the speculative write). + const int last_error = WSAGetLastError (); + if (nbytes == SOCKET_ERROR && last_error == WSAEWOULDBLOCK) + return 0; + + // Signalise peer failure. + if (nbytes == SOCKET_ERROR + && (last_error == WSAENETDOWN || last_error == WSAENETRESET + || last_error == WSAEHOSTUNREACH || last_error == WSAECONNABORTED + || last_error == WSAETIMEDOUT || last_error == WSAECONNRESET)) + return -1; + + // Circumvent a Windows bug: + // See https://support.microsoft.com/en-us/kb/201213 + // See https://zeromq.jira.com/browse/LIBZMQ-195 + if (nbytes == SOCKET_ERROR && last_error == WSAENOBUFS) + return 0; + + wsa_assert (nbytes != SOCKET_ERROR); + return nbytes; + +#else + ssize_t nbytes = send (s_, static_cast (data_), size_, 0); + + // Several errors are OK. When speculative write is being done we may not + // be able to write a single byte from the socket. Also, SIGSTOP issued + // by a debugging tool can result in EINTR error. + if (nbytes == -1 + && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) + return 0; + + // Signalise peer failure. + if (nbytes == -1) { +#if !defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE + errno_assert (errno != EACCES && errno != EBADF && errno != EDESTADDRREQ + && errno != EFAULT && errno != EISCONN + && errno != EMSGSIZE && errno != ENOMEM + && errno != ENOTSOCK && errno != EOPNOTSUPP); +#else + errno_assert (errno != EACCES && errno != EDESTADDRREQ + && errno != EFAULT && errno != EISCONN + && errno != EMSGSIZE && errno != ENOMEM + && errno != ENOTSOCK && errno != EOPNOTSUPP); +#endif + return -1; + } + + return static_cast (nbytes); + +#endif +} + +int zmq::tcp_read (fd_t s_, void *data_, size_t size_) +{ +#ifdef ZMQ_HAVE_WINDOWS + + const int rc = + recv (s_, static_cast (data_), static_cast (size_), 0); + + // If not a single byte can be read from the socket in non-blocking mode + // we'll get an error (this may happen during the speculative read). + if (rc == SOCKET_ERROR) { + const int last_error = WSAGetLastError (); + if (last_error == WSAEWOULDBLOCK) { + errno = EAGAIN; + } else { + wsa_assert ( + last_error == WSAENETDOWN || last_error == WSAENETRESET + || last_error == WSAECONNABORTED || last_error == WSAETIMEDOUT + || last_error == WSAECONNRESET || last_error == WSAECONNREFUSED + || last_error == WSAENOTCONN || last_error == WSAENOBUFS); + errno = wsa_error_to_errno (last_error); + } + } + + return rc == SOCKET_ERROR ? -1 : rc; + +#else + + const ssize_t rc = recv (s_, static_cast (data_), size_, 0); + + // Several errors are OK. When speculative read is being done we may not + // be able to read a single byte from the socket. Also, SIGSTOP issued + // by a debugging tool can result in EINTR error. + if (rc == -1) { +#if !defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE + errno_assert (errno != EBADF && errno != EFAULT && errno != ENOMEM + && errno != ENOTSOCK); +#else + errno_assert (errno != EFAULT && errno != ENOMEM && errno != ENOTSOCK); +#endif + if (errno == EWOULDBLOCK || errno == EINTR) + errno = EAGAIN; + } + + return static_cast (rc); + +#endif +} + +void zmq::tcp_tune_loopback_fast_path (const fd_t socket_) +{ +#if defined ZMQ_HAVE_WINDOWS && defined SIO_LOOPBACK_FAST_PATH + int sio_loopback_fastpath = 1; + DWORD number_of_bytes_returned = 0; + + const int rc = WSAIoctl ( + socket_, SIO_LOOPBACK_FAST_PATH, &sio_loopback_fastpath, + sizeof sio_loopback_fastpath, NULL, 0, &number_of_bytes_returned, 0, 0); + + if (SOCKET_ERROR == rc) { + const DWORD last_error = ::WSAGetLastError (); + + if (WSAEOPNOTSUPP == last_error) { + // This system is not Windows 8 or Server 2012, and the call is not supported. + } else { + wsa_assert (false); + } + } +#else + LIBZMQ_UNUSED (socket_); +#endif +} + +zmq::fd_t zmq::tcp_open_socket (const char *address_, + const zmq::options_t &options_, + bool local_, + bool fallback_to_ipv4_, + zmq::tcp_address_t *out_tcp_addr_) +{ + // Convert the textual address into address structure. + int rc = out_tcp_addr_->resolve (address_, local_, options_.ipv6); + if (rc != 0) + return retired_fd; + + // Create the socket. + fd_t s = open_socket (out_tcp_addr_->family (), SOCK_STREAM, IPPROTO_TCP); + + // IPv6 address family not supported, try automatic downgrade to IPv4. + if (s == retired_fd && fallback_to_ipv4_ + && out_tcp_addr_->family () == AF_INET6 && errno == EAFNOSUPPORT + && options_.ipv6) { + rc = out_tcp_addr_->resolve (address_, local_, false); + if (rc != 0) { + return retired_fd; + } + s = open_socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + } + + if (s == retired_fd) { + return retired_fd; + } + + // On some systems, IPv4 mapping in IPv6 sockets is disabled by default. + // Switch it on in such cases. + if (out_tcp_addr_->family () == AF_INET6) + enable_ipv4_mapping (s); + + // Set the IP Type-Of-Service priority for this socket + if (options_.tos != 0) + set_ip_type_of_service (s, options_.tos); + + // Set the protocol-defined priority for this socket + if (options_.priority != 0) + set_socket_priority (s, options_.priority); + + // Set the socket to loopback fastpath if configured. + if (options_.loopback_fastpath) + tcp_tune_loopback_fast_path (s); + + // Bind the socket to a device if applicable + if (!options_.bound_device.empty ()) + if (bind_to_device (s, options_.bound_device) == -1) + goto setsockopt_error; + + // Set the socket buffer limits for the underlying socket. + if (options_.sndbuf >= 0) + set_tcp_send_buffer (s, options_.sndbuf); + if (options_.rcvbuf >= 0) + set_tcp_receive_buffer (s, options_.rcvbuf); + + return s; + +setsockopt_error: +#ifdef ZMQ_HAVE_WINDOWS + rc = closesocket (s); + wsa_assert (rc != SOCKET_ERROR); +#else + rc = ::close (s); + errno_assert (rc == 0); +#endif + return retired_fd; +} diff --git a/3rd/libzmq/src/tcp.hpp b/3rd/libzmq/src/tcp.hpp new file mode 100644 index 00000000..fc355a63 --- /dev/null +++ b/3rd/libzmq/src/tcp.hpp @@ -0,0 +1,83 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_TCP_HPP_INCLUDED__ +#define __ZMQ_TCP_HPP_INCLUDED__ + +#include "fd.hpp" + +namespace zmq +{ +class tcp_address_t; +struct options_t; + +// Tunes the supplied TCP socket for the best latency. +int tune_tcp_socket (fd_t s_); + +// Sets the socket send buffer size. +int set_tcp_send_buffer (fd_t sockfd_, int bufsize_); + +// Sets the socket receive buffer size. +int set_tcp_receive_buffer (fd_t sockfd_, int bufsize_); + +// Tunes TCP keep-alives +int tune_tcp_keepalives (fd_t s_, + int keepalive_, + int keepalive_cnt_, + int keepalive_idle_, + int keepalive_intvl_); + +// Tunes TCP max retransmit timeout +int tune_tcp_maxrt (fd_t sockfd_, int timeout_); + +// Writes data to the socket. Returns the number of bytes actually +// written (even zero is to be considered to be a success). In case +// of error or orderly shutdown by the other peer -1 is returned. +int tcp_write (fd_t s_, const void *data_, size_t size_); + +// Reads data from the socket (up to 'size' bytes). +// Returns the number of bytes actually read or -1 on error. +// Zero indicates the peer has closed the connection. +int tcp_read (fd_t s_, void *data_, size_t size_); + +void tcp_tune_loopback_fast_path (fd_t socket_); + +// Resolves the given address_ string, opens a socket and sets socket options +// according to the passed options_. On success, returns the socket +// descriptor and assigns the resolved address to out_tcp_addr_. In case of +// an error, retired_fd is returned, and the value of out_tcp_addr_ is undefined. +// errno is set to an error code describing the cause of the error. +fd_t tcp_open_socket (const char *address_, + const options_t &options_, + bool local_, + bool fallback_to_ipv4_, + tcp_address_t *out_tcp_addr_); +} + +#endif diff --git a/3rd/libzmq/src/tcp_address.cpp b/3rd/libzmq/src/tcp_address.cpp new file mode 100644 index 00000000..bdda66a2 --- /dev/null +++ b/3rd/libzmq/src/tcp_address.cpp @@ -0,0 +1,307 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include "macros.hpp" +#include "tcp_address.hpp" +#include "stdint.hpp" +#include "err.hpp" +#include "ip.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +zmq::tcp_address_t::tcp_address_t () : _has_src_addr (false) +{ + memset (&_address, 0, sizeof (_address)); + memset (&_source_address, 0, sizeof (_source_address)); +} + +zmq::tcp_address_t::tcp_address_t (const sockaddr *sa_, socklen_t sa_len_) : + _has_src_addr (false) +{ + zmq_assert (sa_ && sa_len_ > 0); + + memset (&_address, 0, sizeof (_address)); + memset (&_source_address, 0, sizeof (_source_address)); + if (sa_->sa_family == AF_INET + && sa_len_ >= static_cast (sizeof (_address.ipv4))) + memcpy (&_address.ipv4, sa_, sizeof (_address.ipv4)); + else if (sa_->sa_family == AF_INET6 + && sa_len_ >= static_cast (sizeof (_address.ipv6))) + memcpy (&_address.ipv6, sa_, sizeof (_address.ipv6)); +} + +int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv6_) +{ + // Test the ';' to know if we have a source address in name_ + const char *src_delimiter = strrchr (name_, ';'); + if (src_delimiter) { + const std::string src_name (name_, src_delimiter - name_); + + ip_resolver_options_t src_resolver_opts; + + src_resolver_opts + .bindable (true) + // Restrict hostname/service to literals to avoid any DNS + // lookups or service-name irregularity due to + // indeterminate socktype. + .allow_dns (false) + .allow_nic_name (true) + .ipv6 (ipv6_) + .expect_port (true); + + ip_resolver_t src_resolver (src_resolver_opts); + + const int rc = + src_resolver.resolve (&_source_address, src_name.c_str ()); + if (rc != 0) + return -1; + name_ = src_delimiter + 1; + _has_src_addr = true; + } + + ip_resolver_options_t resolver_opts; + + resolver_opts.bindable (local_) + .allow_dns (!local_) + .allow_nic_name (local_) + .ipv6 (ipv6_) + .expect_port (true); + + ip_resolver_t resolver (resolver_opts); + + return resolver.resolve (&_address, name_); +} + +template +static std::string make_address_string (const char *hbuf_, + uint16_t port_, + const char (&ipv6_prefix_)[N1], + const char (&ipv6_suffix_)[N2]) +{ + const size_t max_port_str_length = 5; + char buf[NI_MAXHOST + sizeof ipv6_prefix_ + sizeof ipv6_suffix_ + + max_port_str_length]; + char *pos = buf; + memcpy (pos, ipv6_prefix_, sizeof ipv6_prefix_ - 1); + pos += sizeof ipv6_prefix_ - 1; + const size_t hbuf_len = strlen (hbuf_); + memcpy (pos, hbuf_, hbuf_len); + pos += hbuf_len; + memcpy (pos, ipv6_suffix_, sizeof ipv6_suffix_ - 1); + pos += sizeof ipv6_suffix_ - 1; + pos += sprintf (pos, "%d", ntohs (port_)); + return std::string (buf, pos - buf); +} + +int zmq::tcp_address_t::to_string (std::string &addr_) const +{ + if (_address.family () != AF_INET && _address.family () != AF_INET6) { + addr_.clear (); + return -1; + } + + // Not using service resolving because of + // https://github.com/zeromq/libzmq/commit/1824574f9b5a8ce786853320e3ea09fe1f822bc4 + char hbuf[NI_MAXHOST]; + const int rc = getnameinfo (addr (), addrlen (), hbuf, sizeof (hbuf), NULL, + 0, NI_NUMERICHOST); + if (rc != 0) { + addr_.clear (); + return rc; + } + + const char ipv4_prefix[] = "tcp://"; + const char ipv4_suffix[] = ":"; + const char ipv6_prefix[] = "tcp://["; + const char ipv6_suffix[] = "]:"; + if (_address.family () == AF_INET6) { + addr_ = make_address_string (hbuf, _address.ipv6.sin6_port, ipv6_prefix, + ipv6_suffix); + } else { + addr_ = make_address_string (hbuf, _address.ipv4.sin_port, ipv4_prefix, + ipv4_suffix); + } + return 0; +} + +const sockaddr *zmq::tcp_address_t::addr () const +{ + return _address.as_sockaddr (); +} + +socklen_t zmq::tcp_address_t::addrlen () const +{ + return _address.sockaddr_len (); +} + +const sockaddr *zmq::tcp_address_t::src_addr () const +{ + return _source_address.as_sockaddr (); +} + +socklen_t zmq::tcp_address_t::src_addrlen () const +{ + return _source_address.sockaddr_len (); +} + +bool zmq::tcp_address_t::has_src_addr () const +{ + return _has_src_addr; +} + +#if defined ZMQ_HAVE_WINDOWS +unsigned short zmq::tcp_address_t::family () const +#else +sa_family_t zmq::tcp_address_t::family () const +#endif +{ + return _address.family (); +} + +zmq::tcp_address_mask_t::tcp_address_mask_t () : _address_mask (-1) +{ + memset (&_network_address, 0, sizeof (_network_address)); +} + +int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv6_) +{ + // Find '/' at the end that separates address from the cidr mask number. + // Allow empty mask clause and treat it like '/32' for ipv4 or '/128' for ipv6. + std::string addr_str, mask_str; + const char *delimiter = strrchr (name_, '/'); + if (delimiter != NULL) { + addr_str.assign (name_, delimiter - name_); + mask_str.assign (delimiter + 1); + if (mask_str.empty ()) { + errno = EINVAL; + return -1; + } + } else + addr_str.assign (name_); + + // Parse address part using standard routines. + ip_resolver_options_t resolver_opts; + + resolver_opts.bindable (false) + .allow_dns (false) + .allow_nic_name (false) + .ipv6 (ipv6_) + .expect_port (false); + + ip_resolver_t resolver (resolver_opts); + + const int rc = resolver.resolve (&_network_address, addr_str.c_str ()); + if (rc != 0) + return rc; + + // Parse the cidr mask number. + const int full_mask_ipv4 = + sizeof (_network_address.ipv4.sin_addr) * CHAR_BIT; + const int full_mask_ipv6 = + sizeof (_network_address.ipv6.sin6_addr) * CHAR_BIT; + if (mask_str.empty ()) { + _address_mask = _network_address.family () == AF_INET6 ? full_mask_ipv6 + : full_mask_ipv4; + } else if (mask_str == "0") + _address_mask = 0; + else { + const long mask = strtol (mask_str.c_str (), NULL, 10); + if ((mask < 1) + || (_network_address.family () == AF_INET6 && mask > full_mask_ipv6) + || (_network_address.family () != AF_INET6 + && mask > full_mask_ipv4)) { + errno = EINVAL; + return -1; + } + _address_mask = static_cast (mask); + } + + return 0; +} + +bool zmq::tcp_address_mask_t::match_address (const struct sockaddr *ss_, + const socklen_t ss_len_) const +{ + zmq_assert (_address_mask != -1 && ss_ != NULL + && ss_len_ + >= static_cast (sizeof (struct sockaddr))); + + if (ss_->sa_family != _network_address.generic.sa_family) + return false; + + if (_address_mask > 0) { + int mask; + const uint8_t *our_bytes, *their_bytes; + if (ss_->sa_family == AF_INET6) { + zmq_assert (ss_len_ == sizeof (struct sockaddr_in6)); + their_bytes = reinterpret_cast ( + &((reinterpret_cast (ss_)) + ->sin6_addr)); + our_bytes = reinterpret_cast ( + &_network_address.ipv6.sin6_addr); + mask = sizeof (struct in6_addr) * 8; + } else { + zmq_assert (ss_len_ == sizeof (struct sockaddr_in)); + their_bytes = reinterpret_cast (&( + (reinterpret_cast (ss_))->sin_addr)); + our_bytes = reinterpret_cast ( + &_network_address.ipv4.sin_addr); + mask = sizeof (struct in_addr) * 8; + } + if (_address_mask < mask) + mask = _address_mask; + + const size_t full_bytes = mask / 8; + if (memcmp (our_bytes, their_bytes, full_bytes) != 0) + return false; + + const uint8_t last_byte_bits = 0xffU << (8 - mask % 8); + if (last_byte_bits) { + if ((their_bytes[full_bytes] & last_byte_bits) + != (our_bytes[full_bytes] & last_byte_bits)) + return false; + } + } + + return true; +} diff --git a/3rd/libzmq/src/tcp_address.hpp b/3rd/libzmq/src/tcp_address.hpp new file mode 100644 index 00000000..4d3edf49 --- /dev/null +++ b/3rd/libzmq/src/tcp_address.hpp @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_TCP_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_TCP_ADDRESS_HPP_INCLUDED__ + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#endif + +#include "ip_resolver.hpp" + +namespace zmq +{ +class tcp_address_t +{ + public: + tcp_address_t (); + tcp_address_t (const sockaddr *sa_, socklen_t sa_len_); + + // This function translates textual TCP address into an address + // structure. If 'local' is true, names are resolved as local interface + // names. If it is false, names are resolved as remote hostnames. + // If 'ipv6' is true, the name may resolve to IPv6 address. + int resolve (const char *name_, bool local_, bool ipv6_); + + // The opposite to resolve() + int to_string (std::string &addr_) const; + +#if defined ZMQ_HAVE_WINDOWS + unsigned short family () const; +#else + sa_family_t family () const; +#endif + const sockaddr *addr () const; + socklen_t addrlen () const; + + const sockaddr *src_addr () const; + socklen_t src_addrlen () const; + bool has_src_addr () const; + + private: + ip_addr_t _address; + ip_addr_t _source_address; + bool _has_src_addr; +}; + +class tcp_address_mask_t +{ + public: + tcp_address_mask_t (); + + // This function enhances tcp_address_t::resolve() with ability to parse + // additional cidr-like(/xx) mask value at the end of the name string. + // Works only with remote hostnames. + int resolve (const char *name_, bool ipv6_); + + bool match_address (const struct sockaddr *ss_, socklen_t ss_len_) const; + + private: + ip_addr_t _network_address; + int _address_mask; +}; +} + +#endif diff --git a/3rd/libzmq/src/tcp_connecter.cpp b/3rd/libzmq/src/tcp_connecter.cpp new file mode 100644 index 00000000..a0138eb1 --- /dev/null +++ b/3rd/libzmq/src/tcp_connecter.cpp @@ -0,0 +1,313 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "macros.hpp" +#include "tcp_connecter.hpp" +#include "io_thread.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "address.hpp" +#include "tcp_address.hpp" +#include "session_base.hpp" + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#ifdef ZMQ_HAVE_OPENVMS +#include +#endif +#endif + +#ifdef __APPLE__ +#include +#endif + +zmq::tcp_connecter_t::tcp_connecter_t (class io_thread_t *io_thread_, + class session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_) : + stream_connecter_base_t ( + io_thread_, session_, options_, addr_, delayed_start_), + _connect_timer_started (false) +{ + zmq_assert (_addr->protocol == protocol_name::tcp); +} + +zmq::tcp_connecter_t::~tcp_connecter_t () +{ + zmq_assert (!_connect_timer_started); +} + +void zmq::tcp_connecter_t::process_term (int linger_) +{ + if (_connect_timer_started) { + cancel_timer (connect_timer_id); + _connect_timer_started = false; + } + + stream_connecter_base_t::process_term (linger_); +} + +void zmq::tcp_connecter_t::out_event () +{ + if (_connect_timer_started) { + cancel_timer (connect_timer_id); + _connect_timer_started = false; + } + + // TODO this is still very similar to (t)ipc_connecter_t, maybe the + // differences can be factored out + + rm_handle (); + + const fd_t fd = connect (); + + if (fd == retired_fd + && ((options.reconnect_stop & ZMQ_RECONNECT_STOP_CONN_REFUSED) + && errno == ECONNREFUSED)) { + send_conn_failed (_session); + close (); + terminate (); + return; + } + + // Handle the error condition by attempt to reconnect. + if (fd == retired_fd || !tune_socket (fd)) { + close (); + add_reconnect_timer (); + return; + } + + create_engine (fd, get_socket_name (fd, socket_end_local)); +} + +void zmq::tcp_connecter_t::timer_event (int id_) +{ + if (id_ == connect_timer_id) { + _connect_timer_started = false; + rm_handle (); + close (); + add_reconnect_timer (); + } else + stream_connecter_base_t::timer_event (id_); +} + +void zmq::tcp_connecter_t::start_connecting () +{ + // Open the connecting socket. + const int rc = open (); + + // Connect may succeed in synchronous manner. + if (rc == 0) { + _handle = add_fd (_s); + out_event (); + } + + // Connection establishment may be delayed. Poll for its completion. + else if (rc == -1 && errno == EINPROGRESS) { + _handle = add_fd (_s); + set_pollout (_handle); + _socket->event_connect_delayed ( + make_unconnected_connect_endpoint_pair (_endpoint), zmq_errno ()); + + // add userspace connect timeout + add_connect_timer (); + } + + // Handle any other error condition by eventual reconnect. + else { + if (_s != retired_fd) + close (); + add_reconnect_timer (); + } +} + +void zmq::tcp_connecter_t::add_connect_timer () +{ + if (options.connect_timeout > 0) { + add_timer (options.connect_timeout, connect_timer_id); + _connect_timer_started = true; + } +} + +int zmq::tcp_connecter_t::open () +{ + zmq_assert (_s == retired_fd); + + // Resolve the address + if (_addr->resolved.tcp_addr != NULL) { + LIBZMQ_DELETE (_addr->resolved.tcp_addr); + } + + _addr->resolved.tcp_addr = new (std::nothrow) tcp_address_t (); + alloc_assert (_addr->resolved.tcp_addr); + _s = tcp_open_socket (_addr->address.c_str (), options, false, true, + _addr->resolved.tcp_addr); + if (_s == retired_fd) { + // TODO we should emit some event in this case! + + LIBZMQ_DELETE (_addr->resolved.tcp_addr); + return -1; + } + zmq_assert (_addr->resolved.tcp_addr != NULL); + + // Set the socket to non-blocking mode so that we get async connect(). + unblock_socket (_s); + + const tcp_address_t *const tcp_addr = _addr->resolved.tcp_addr; + + int rc; + + // Set a source address for conversations + if (tcp_addr->has_src_addr ()) { + // Allow reusing of the address, to connect to different servers + // using the same source port on the client. + int flag = 1; +#ifdef ZMQ_HAVE_WINDOWS + rc = setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast (&flag), sizeof (int)); + wsa_assert (rc != SOCKET_ERROR); +#elif defined ZMQ_HAVE_VXWORKS + rc = setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, + sizeof (int)); + errno_assert (rc == 0); +#else + rc = setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); + errno_assert (rc == 0); +#endif + +#if defined ZMQ_HAVE_VXWORKS + rc = ::bind (_s, (sockaddr *) tcp_addr->src_addr (), + tcp_addr->src_addrlen ()); +#else + rc = ::bind (_s, tcp_addr->src_addr (), tcp_addr->src_addrlen ()); +#endif + if (rc == -1) + return -1; + } + + // Connect to the remote peer. +#if defined ZMQ_HAVE_VXWORKS + rc = ::connect (_s, (sockaddr *) tcp_addr->addr (), tcp_addr->addrlen ()); +#else + rc = ::connect (_s, tcp_addr->addr (), tcp_addr->addrlen ()); +#endif + // Connect was successful immediately. + if (rc == 0) { + return 0; + } + + // Translate error codes indicating asynchronous connect has been + // launched to a uniform EINPROGRESS. +#ifdef ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + if (last_error == WSAEINPROGRESS || last_error == WSAEWOULDBLOCK) + errno = EINPROGRESS; + else + errno = wsa_error_to_errno (last_error); +#else + if (errno == EINTR) + errno = EINPROGRESS; +#endif + return -1; +} + +zmq::fd_t zmq::tcp_connecter_t::connect () +{ + // Async connect has finished. Check whether an error occurred + int err = 0; +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int len = sizeof err; +#else + socklen_t len = sizeof err; +#endif + + const int rc = getsockopt (_s, SOL_SOCKET, SO_ERROR, + reinterpret_cast (&err), &len); + + // Assert if the error was caused by 0MQ bug. + // Networking problems are OK. No need to assert. +#ifdef ZMQ_HAVE_WINDOWS + zmq_assert (rc == 0); + if (err != 0) { + if (err == WSAEBADF || err == WSAENOPROTOOPT || err == WSAENOTSOCK + || err == WSAENOBUFS) { + wsa_assert_no (err); + } + errno = wsa_error_to_errno (err); + return retired_fd; + } +#else + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + if (rc == -1) + err = errno; + if (err != 0) { + errno = err; +#if !defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE + errno_assert (errno != EBADF && errno != ENOPROTOOPT + && errno != ENOTSOCK && errno != ENOBUFS); +#else + errno_assert (errno != ENOPROTOOPT && errno != ENOTSOCK + && errno != ENOBUFS); +#endif + return retired_fd; + } +#endif + + // Return the newly connected socket. + const fd_t result = _s; + _s = retired_fd; + return result; +} + +bool zmq::tcp_connecter_t::tune_socket (const fd_t fd_) +{ + const int rc = tune_tcp_socket (fd_) + | tune_tcp_keepalives ( + fd_, options.tcp_keepalive, options.tcp_keepalive_cnt, + options.tcp_keepalive_idle, options.tcp_keepalive_intvl) + | tune_tcp_maxrt (fd_, options.tcp_maxrt); + return rc == 0; +} diff --git a/3rd/libzmq/src/tcp_connecter.hpp b/3rd/libzmq/src/tcp_connecter.hpp new file mode 100644 index 00000000..49477039 --- /dev/null +++ b/3rd/libzmq/src/tcp_connecter.hpp @@ -0,0 +1,90 @@ +/* + 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 . +*/ + +#ifndef __TCP_CONNECTER_HPP_INCLUDED__ +#define __TCP_CONNECTER_HPP_INCLUDED__ + +#include "fd.hpp" +#include "stdint.hpp" +#include "stream_connecter_base.hpp" + +namespace zmq +{ +class tcp_connecter_t ZMQ_FINAL : public stream_connecter_base_t +{ + public: + // If 'delayed_start' is true connecter first waits for a while, + // then starts connection process. + tcp_connecter_t (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_); + ~tcp_connecter_t (); + + private: + // ID of the timer used to check the connect timeout, must be different from stream_connecter_base_t::reconnect_timer_id. + enum + { + connect_timer_id = 2 + }; + + // Handlers for incoming commands. + void process_term (int linger_); + + // Handlers for I/O events. + void out_event (); + void timer_event (int id_); + + // Internal function to start the actual connection establishment. + void start_connecting (); + + // Internal function to add a connect timer + void add_connect_timer (); + + // Open TCP connecting socket. Returns -1 in case of error, + // 0 if connect was successful immediately. Returns -1 with + // EAGAIN errno if async connect was launched. + int open (); + + // Get the file descriptor of newly created connection. Returns + // retired_fd if the connection was unsuccessful. + fd_t connect (); + + // Tunes a connected socket. + bool tune_socket (fd_t fd_); + + // True iff a timer has been started. + bool _connect_timer_started; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (tcp_connecter_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/tcp_listener.cpp b/3rd/libzmq/src/tcp_listener.cpp new file mode 100644 index 00000000..69581b74 --- /dev/null +++ b/3rd/libzmq/src/tcp_listener.cpp @@ -0,0 +1,277 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include +#include + +#include "tcp_listener.hpp" +#include "io_thread.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "socket_base.hpp" +#include "address.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#endif + +#ifdef ZMQ_HAVE_OPENVMS +#include +#endif + +zmq::tcp_listener_t::tcp_listener_t (io_thread_t *io_thread_, + socket_base_t *socket_, + const options_t &options_) : + stream_listener_base_t (io_thread_, socket_, options_) +{ +} + +void zmq::tcp_listener_t::in_event () +{ + const fd_t fd = accept (); + + // If connection was reset by the peer in the meantime, just ignore it. + // TODO: Handle specific errors like ENFILE/EMFILE etc. + if (fd == retired_fd) { + _socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); + return; + } + + int rc = tune_tcp_socket (fd); + rc = rc + | tune_tcp_keepalives ( + fd, options.tcp_keepalive, options.tcp_keepalive_cnt, + options.tcp_keepalive_idle, options.tcp_keepalive_intvl); + rc = rc | tune_tcp_maxrt (fd, options.tcp_maxrt); + if (rc != 0) { + _socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); + return; + } + + // Create the engine object for this connection. + create_engine (fd); +} + +std::string +zmq::tcp_listener_t::get_socket_name (zmq::fd_t fd_, + socket_end_t socket_end_) const +{ + return zmq::get_socket_name (fd_, socket_end_); +} + +int zmq::tcp_listener_t::create_socket (const char *addr_) +{ + _s = tcp_open_socket (addr_, options, true, true, &_address); + if (_s == retired_fd) { + return -1; + } + + // TODO why is this only done for the listener? + make_socket_noninheritable (_s); + + // Allow reusing of the address. + int flag = 1; + int rc; +#ifdef ZMQ_HAVE_WINDOWS + // TODO this was changed for Windows from SO_REUSEADDRE to + // SE_EXCLUSIVEADDRUSE by 0ab65324195ad70205514d465b03d851a6de051c, + // so the comment above is no longer correct; also, now the settings are + // different between listener and connecter with a src address. + // is this intentional? + rc = setsockopt (_s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast (&flag), sizeof (int)); + wsa_assert (rc != SOCKET_ERROR); +#elif defined ZMQ_HAVE_VXWORKS + rc = + setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof (int)); + errno_assert (rc == 0); +#else + rc = setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); + errno_assert (rc == 0); +#endif + + // Bind the socket to the network interface and port. +#if defined ZMQ_HAVE_VXWORKS + rc = bind (_s, (sockaddr *) _address.addr (), _address.addrlen ()); +#else + rc = bind (_s, _address.addr (), _address.addrlen ()); +#endif +#ifdef ZMQ_HAVE_WINDOWS + if (rc == SOCKET_ERROR) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error; + } +#else + if (rc != 0) + goto error; +#endif + + // Listen for incoming connections. + rc = listen (_s, options.backlog); +#ifdef ZMQ_HAVE_WINDOWS + if (rc == SOCKET_ERROR) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error; + } +#else + if (rc != 0) + goto error; +#endif + + return 0; + +error: + const int err = errno; + close (); + errno = err; + return -1; +} + +int zmq::tcp_listener_t::set_local_address (const char *addr_) +{ + if (options.use_fd != -1) { + // in this case, the addr_ passed is not used and ignored, since the + // socket was already created by the application + _s = options.use_fd; + } else { + if (create_socket (addr_) == -1) + return -1; + } + + _endpoint = get_socket_name (_s, socket_end_local); + + _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint), + _s); + return 0; +} + +zmq::fd_t zmq::tcp_listener_t::accept () +{ + // The situation where connection cannot be accepted due to insufficient + // resources is considered valid and treated by ignoring the connection. + // Accept one connection and deal with different failure modes. + zmq_assert (_s != retired_fd); + + struct sockaddr_storage ss; + memset (&ss, 0, sizeof (ss)); +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int ss_len = sizeof (ss); +#else + socklen_t ss_len = sizeof (ss); +#endif +#if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4 + fd_t sock = ::accept4 (_s, reinterpret_cast (&ss), + &ss_len, SOCK_CLOEXEC); +#else + const fd_t sock = + ::accept (_s, reinterpret_cast (&ss), &ss_len); +#endif + + if (sock == retired_fd) { +#if defined ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET + || last_error == WSAEMFILE || last_error == WSAENOBUFS); +#elif defined ZMQ_HAVE_ANDROID + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR + || errno == ECONNABORTED || errno == EPROTO + || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE + || errno == ENFILE || errno == EINVAL); +#else + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR + || errno == ECONNABORTED || errno == EPROTO + || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE + || errno == ENFILE); +#endif + return retired_fd; + } + + make_socket_noninheritable (sock); + + if (!options.tcp_accept_filters.empty ()) { + bool matched = false; + for (options_t::tcp_accept_filters_t::size_type + i = 0, + size = options.tcp_accept_filters.size (); + i != size; ++i) { + if (options.tcp_accept_filters[i].match_address ( + reinterpret_cast (&ss), ss_len)) { + matched = true; + break; + } + } + if (!matched) { +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (sock); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = ::close (sock); + errno_assert (rc == 0); +#endif + return retired_fd; + } + } + + if (zmq::set_nosigpipe (sock)) { +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (sock); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = ::close (sock); + errno_assert (rc == 0); +#endif + return retired_fd; + } + + // Set the IP Type-Of-Service priority for this client socket + if (options.tos != 0) + set_ip_type_of_service (sock, options.tos); + + // Set the protocol-defined priority for this client socket + if (options.priority != 0) + set_socket_priority (sock, options.priority); + + return sock; +} diff --git a/3rd/libzmq/src/tcp_listener.hpp b/3rd/libzmq/src/tcp_listener.hpp new file mode 100644 index 00000000..3f41b8fb --- /dev/null +++ b/3rd/libzmq/src/tcp_listener.hpp @@ -0,0 +1,71 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_TCP_LISTENER_HPP_INCLUDED__ +#define __ZMQ_TCP_LISTENER_HPP_INCLUDED__ + +#include "fd.hpp" +#include "tcp_address.hpp" +#include "stream_listener_base.hpp" + +namespace zmq +{ +class tcp_listener_t ZMQ_FINAL : public stream_listener_base_t +{ + public: + tcp_listener_t (zmq::io_thread_t *io_thread_, + zmq::socket_base_t *socket_, + const options_t &options_); + + // Set address to listen on. + int set_local_address (const char *addr_); + + protected: + std::string get_socket_name (fd_t fd_, socket_end_t socket_end_) const; + + private: + // Handlers for I/O events. + void in_event (); + + // Accept the new connection. Returns the file descriptor of the + // newly created connection. The function may return retired_fd + // if the connection was dropped while waiting in the listen backlog + // or was denied because of accept filters. + fd_t accept (); + + int create_socket (const char *addr_); + + // Address to listen on. + tcp_address_t _address; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (tcp_listener_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/thread.cpp b/3rd/libzmq/src/thread.cpp new file mode 100644 index 00000000..c754e9cd --- /dev/null +++ b/3rd/libzmq/src/thread.cpp @@ -0,0 +1,413 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "thread.hpp" +#include "err.hpp" + +#ifdef ZMQ_HAVE_WINDOWS +#include +#endif + +bool zmq::thread_t::get_started () const +{ + return _started; +} + +#ifdef ZMQ_HAVE_WINDOWS + +extern "C" { +#if defined _WIN32_WCE +static DWORD thread_routine (LPVOID arg_) +#else +static unsigned int __stdcall thread_routine (void *arg_) +#endif +{ + zmq::thread_t *self = static_cast (arg_); + self->applyThreadName (); + self->_tfn (self->_arg); + return 0; +} +} + +void zmq::thread_t::start (thread_fn *tfn_, void *arg_, const char *name_) +{ + _tfn = tfn_; + _arg = arg_; + if (name_) + strncpy (_name, name_, sizeof (_name) - 1); + + // set default stack size to 4MB to avoid std::map stack overflow on x64 + unsigned int stack = 0; +#if defined _WIN64 + stack = 0x400000; +#endif + +#if defined _WIN32_WCE + _descriptor = (HANDLE) CreateThread (NULL, stack, &::thread_routine, this, + 0, &_thread_id); +#else + _descriptor = (HANDLE) _beginthreadex (NULL, stack, &::thread_routine, this, + 0, &_thread_id); +#endif + win_assert (_descriptor != NULL); + _started = true; +} + +bool zmq::thread_t::is_current_thread () const +{ + return GetCurrentThreadId () == _thread_id; +} + +void zmq::thread_t::stop () +{ + if (_started) { + const DWORD rc = WaitForSingleObject (_descriptor, INFINITE); + win_assert (rc != WAIT_FAILED); + const BOOL rc2 = CloseHandle (_descriptor); + win_assert (rc2 != 0); + } +} + +void zmq::thread_t::setSchedulingParameters ( + int priority_, int scheduling_policy_, const std::set &affinity_cpus_) +{ + // not implemented + LIBZMQ_UNUSED (priority_); + LIBZMQ_UNUSED (scheduling_policy_); + LIBZMQ_UNUSED (affinity_cpus_); +} + +void zmq::thread_t:: + applySchedulingParameters () // to be called in secondary thread context +{ + // not implemented +} + +namespace +{ +#pragma pack(push, 8) +struct thread_info_t +{ + DWORD _type; + LPCSTR _name; + DWORD _thread_id; + DWORD _flags; +}; +#pragma pack(pop) +} + +struct MY_EXCEPTION_REGISTRATION_RECORD +{ + typedef EXCEPTION_DISPOSITION (NTAPI *HandlerFunctionType) ( + EXCEPTION_RECORD *, void *, CONTEXT *, void *); + + MY_EXCEPTION_REGISTRATION_RECORD *Next; + HandlerFunctionType Handler; +}; + +static EXCEPTION_DISPOSITION NTAPI continue_execution (EXCEPTION_RECORD *rec, + void *frame, + CONTEXT *ctx, + void *disp) +{ + return ExceptionContinueExecution; +} + +void zmq::thread_t:: + applyThreadName () // to be called in secondary thread context +{ + if (!_name[0] || !IsDebuggerPresent ()) + return; + + thread_info_t thread_info; + thread_info._type = 0x1000; + thread_info._name = _name; + thread_info._thread_id = -1; + thread_info._flags = 0; + + NT_TIB *tib = ((NT_TIB *) NtCurrentTeb ()); + + MY_EXCEPTION_REGISTRATION_RECORD rec; + rec.Next = (MY_EXCEPTION_REGISTRATION_RECORD *) tib->ExceptionList; + rec.Handler = continue_execution; + + // push our handler, raise, and finally pop our handler + tib->ExceptionList = (_EXCEPTION_REGISTRATION_RECORD *) &rec; + const DWORD MS_VC_EXCEPTION = 0x406D1388; + RaiseException (MS_VC_EXCEPTION, 0, + sizeof (thread_info) / sizeof (ULONG_PTR), + (ULONG_PTR *) &thread_info); + tib->ExceptionList = + (_EXCEPTION_REGISTRATION_RECORD + *) (((MY_EXCEPTION_REGISTRATION_RECORD *) tib->ExceptionList)->Next); +} + +#elif defined ZMQ_HAVE_VXWORKS + +extern "C" { +static void *thread_routine (void *arg_) +{ + zmq::thread_t *self = (zmq::thread_t *) arg_; + self->applySchedulingParameters (); + self->_tfn (self->_arg); + return NULL; +} +} + +void zmq::thread_t::start (thread_fn *tfn_, void *arg_, const char *name_) +{ + LIBZMQ_UNUSED (name_); + _tfn = tfn_; + _arg = arg_; + _descriptor = taskSpawn (NULL, DEFAULT_PRIORITY, DEFAULT_OPTIONS, + DEFAULT_STACK_SIZE, (FUNCPTR) thread_routine, + (int) this, 0, 0, 0, 0, 0, 0, 0, 0, 0); + if (_descriptor != NULL || _descriptor > 0) + _started = true; +} + +void zmq::thread_t::stop () +{ + if (_started) + while ((_descriptor != NULL || _descriptor > 0) + && taskIdVerify (_descriptor) == 0) { + } +} + +bool zmq::thread_t::is_current_thread () const +{ + return taskIdSelf () == _descriptor; +} + +void zmq::thread_t::setSchedulingParameters ( + int priority_, int schedulingPolicy_, const std::set &affinity_cpus_) +{ + _thread_priority = priority_; + _thread_sched_policy = schedulingPolicy_; + _thread_affinity_cpus = affinity_cpus_; +} + +void zmq::thread_t:: + applySchedulingParameters () // to be called in secondary thread context +{ + int priority = + (_thread_priority >= 0 ? _thread_priority : DEFAULT_PRIORITY); + priority = (priority < UCHAR_MAX ? priority : DEFAULT_PRIORITY); + if (_descriptor != NULL || _descriptor > 0) { + taskPrioritySet (_descriptor, priority); + } +} + +void zmq::thread_t:: + applyThreadName () // to be called in secondary thread context +{ + // not implemented +} + +#else + +#include +#include +#include +#include + +extern "C" { +static void *thread_routine (void *arg_) +{ +#if !defined ZMQ_HAVE_OPENVMS && !defined ZMQ_HAVE_ANDROID + // Following code will guarantee more predictable latencies as it'll + // disallow any signal handling in the I/O thread. + sigset_t signal_set; + int rc = sigfillset (&signal_set); + errno_assert (rc == 0); + rc = pthread_sigmask (SIG_BLOCK, &signal_set, NULL); + posix_assert (rc); +#endif + zmq::thread_t *self = (zmq::thread_t *) arg_; + self->applySchedulingParameters (); + self->applyThreadName (); + self->_tfn (self->_arg); + return NULL; +} +} + +void zmq::thread_t::start (thread_fn *tfn_, void *arg_, const char *name_) +{ + _tfn = tfn_; + _arg = arg_; + if (name_) + strncpy (_name, name_, sizeof (_name) - 1); + int rc = pthread_create (&_descriptor, NULL, thread_routine, this); + posix_assert (rc); + _started = true; +} + +void zmq::thread_t::stop () +{ + if (_started) { + int rc = pthread_join (_descriptor, NULL); + posix_assert (rc); + } +} + +bool zmq::thread_t::is_current_thread () const +{ + return bool(pthread_equal (pthread_self (), _descriptor)); +} + +void zmq::thread_t::setSchedulingParameters ( + int priority_, int scheduling_policy_, const std::set &affinity_cpus_) +{ + _thread_priority = priority_; + _thread_sched_policy = scheduling_policy_; + _thread_affinity_cpus = affinity_cpus_; +} + +void zmq::thread_t:: + applySchedulingParameters () // to be called in secondary thread context +{ +#if defined _POSIX_THREAD_PRIORITY_SCHEDULING \ + && _POSIX_THREAD_PRIORITY_SCHEDULING >= 0 + int policy = 0; + struct sched_param param; + +#if _POSIX_THREAD_PRIORITY_SCHEDULING == 0 \ + && defined _SC_THREAD_PRIORITY_SCHEDULING + if (sysconf (_SC_THREAD_PRIORITY_SCHEDULING) < 0) { + return; + } +#endif + int rc = pthread_getschedparam (pthread_self (), &policy, ¶m); + posix_assert (rc); + + if (_thread_sched_policy != ZMQ_THREAD_SCHED_POLICY_DFLT) { + policy = _thread_sched_policy; + } + + /* Quoting docs: + "Linux allows the static priority range 1 to 99 for the SCHED_FIFO and + SCHED_RR policies, and the priority 0 for the remaining policies." + Other policies may use the "nice value" in place of the priority: + */ + bool use_nice_instead_priority = + (policy != SCHED_FIFO) && (policy != SCHED_RR); + + if (_thread_priority != ZMQ_THREAD_PRIORITY_DFLT) { + if (use_nice_instead_priority) + param.sched_priority = + 0; // this is the only supported priority for most scheduling policies + else + param.sched_priority = + _thread_priority; // user should provide a value between 1 and 99 + } + +#ifdef __NetBSD__ + if (policy == SCHED_OTHER) + param.sched_priority = -1; +#endif + + rc = pthread_setschedparam (pthread_self (), policy, ¶m); + +#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) + // If this feature is unavailable at run-time, don't abort. + if (rc == ENOSYS) + return; +#endif + + posix_assert (rc); + +#if !defined ZMQ_HAVE_VXWORKS + if (use_nice_instead_priority + && _thread_priority != ZMQ_THREAD_PRIORITY_DFLT) { + // assume the user wants to decrease the thread's nice value + // i.e., increase the chance of this thread being scheduled: try setting that to + // maximum priority. + rc = nice (-20); + + errno_assert (rc != -1); + // IMPORTANT: EPERM is typically returned for unprivileged processes: that's because + // CAP_SYS_NICE capability is required or RLIMIT_NICE resource limit should be changed to avoid EPERM! + } +#endif + +#ifdef ZMQ_HAVE_PTHREAD_SET_AFFINITY + if (!_thread_affinity_cpus.empty ()) { + cpu_set_t cpuset; + CPU_ZERO (&cpuset); + for (std::set::const_iterator it = _thread_affinity_cpus.begin (), + end = _thread_affinity_cpus.end (); + it != end; it++) { + CPU_SET ((int) (*it), &cpuset); + } + rc = + pthread_setaffinity_np (pthread_self (), sizeof (cpu_set_t), &cpuset); + posix_assert (rc); + } +#endif +#endif +} + +void zmq::thread_t:: + applyThreadName () // to be called in secondary thread context +{ + /* The thread name is a cosmetic string, added to ease debugging of + * multi-threaded applications. It is not a big issue if this value + * can not be set for any reason (such as Permission denied in some + * cases where the application changes its EUID, etc.) The value of + * "int rc" is retained where available, to help debuggers stepping + * through code to see its value - but otherwise it is ignored. + */ + if (!_name[0]) + return; + + /* Fails with permission denied on Android 5/6 */ +#if defined(ZMQ_HAVE_ANDROID) + return; +#endif + +#if defined(ZMQ_HAVE_PTHREAD_SETNAME_1) + int rc = pthread_setname_np (_name); + if (rc) + return; +#elif defined(ZMQ_HAVE_PTHREAD_SETNAME_2) + int rc = pthread_setname_np (pthread_self (), _name); + if (rc) + return; +#elif defined(ZMQ_HAVE_PTHREAD_SETNAME_3) + int rc = pthread_setname_np (pthread_self (), _name, NULL); + if (rc) + return; +#elif defined(ZMQ_HAVE_PTHREAD_SET_NAME) + pthread_set_name_np (pthread_self (), _name); +#endif +} + +#endif diff --git a/3rd/libzmq/src/thread.hpp b/3rd/libzmq/src/thread.hpp new file mode 100644 index 00000000..256bfef3 --- /dev/null +++ b/3rd/libzmq/src/thread.hpp @@ -0,0 +1,136 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_THREAD_HPP_INCLUDED__ +#define __ZMQ_THREAD_HPP_INCLUDED__ + +#if defined ZMQ_HAVE_VXWORKS +#include +#include +#elif !defined ZMQ_HAVE_WINDOWS +#include +#endif +#include +#include + +namespace zmq +{ +typedef void(thread_fn) (void *); + +// Class encapsulating OS thread. Thread initiation/termination is done +// using special functions rather than in constructor/destructor so that +// thread isn't created during object construction by accident, causing +// newly created thread to access half-initialised object. Same applies +// to the destruction process: Thread should be terminated before object +// destruction begins, otherwise it can access half-destructed object. + +class thread_t +{ + public: + thread_t () : + _tfn (NULL), + _arg (NULL), + _started (false), + _thread_priority (ZMQ_THREAD_PRIORITY_DFLT), + _thread_sched_policy (ZMQ_THREAD_SCHED_POLICY_DFLT) + { + memset (_name, 0, sizeof (_name)); + } + +#ifdef ZMQ_HAVE_VXWORKS + ~thread_t () + { + if (descriptor != NULL || descriptor > 0) { + taskDelete (descriptor); + } + } +#endif + + // Creates OS thread. 'tfn' is main thread function. It'll be passed + // 'arg' as an argument. + // Name is 16 characters max including terminating NUL. Thread naming is + // implemented only for pthread, and windows when a debugger is attached. + void start (thread_fn *tfn_, void *arg_, const char *name_); + + // Returns whether the thread was started, i.e. start was called. + bool get_started () const; + + // Returns whether the executing thread is the thread represented by the + // thread object. + bool is_current_thread () const; + + // Waits for thread termination. + void stop (); + + // Sets the thread scheduling parameters. Only implemented for + // pthread. Has no effect on other platforms. + void setSchedulingParameters (int priority_, + int scheduling_policy_, + const std::set &affinity_cpus_); + + // These are internal members. They should be private, however then + // they would not be accessible from the main C routine of the thread. + void applySchedulingParameters (); + void applyThreadName (); + thread_fn *_tfn; + void *_arg; + char _name[16]; + + private: + bool _started; + +#ifdef ZMQ_HAVE_WINDOWS + HANDLE _descriptor; +#if defined _WIN32_WCE + DWORD _thread_id; +#else + unsigned int _thread_id; +#endif +#elif defined ZMQ_HAVE_VXWORKS + int _descriptor; + enum + { + DEFAULT_PRIORITY = 100, + DEFAULT_OPTIONS = 0, + DEFAULT_STACK_SIZE = 4000 + }; +#else + pthread_t _descriptor; +#endif + + // Thread scheduling parameters. + int _thread_priority; + int _thread_sched_policy; + std::set _thread_affinity_cpus; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (thread_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/timers.cpp b/3rd/libzmq/src/timers.cpp new file mode 100644 index 00000000..e9b516e0 --- /dev/null +++ b/3rd/libzmq/src/timers.cpp @@ -0,0 +1,184 @@ +/* +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 . +*/ + +#include "precompiled.hpp" +#include "timers.hpp" +#include "err.hpp" + +#include + +zmq::timers_t::timers_t () : _tag (0xCAFEDADA), _next_timer_id (0) +{ +} + +zmq::timers_t::~timers_t () +{ + // Mark the timers as dead + _tag = 0xdeadbeef; +} + +bool zmq::timers_t::check_tag () const +{ + return _tag == 0xCAFEDADA; +} + +int zmq::timers_t::add (size_t interval_, timers_timer_fn handler_, void *arg_) +{ + if (handler_ == NULL) { + errno = EFAULT; + return -1; + } + + uint64_t when = _clock.now_ms () + interval_; + timer_t timer = {++_next_timer_id, interval_, handler_, arg_}; + _timers.insert (timersmap_t::value_type (when, timer)); + + return timer.timer_id; +} + +struct zmq::timers_t::match_by_id +{ + match_by_id (int timer_id_) : _timer_id (timer_id_) {} + + bool operator() (timersmap_t::value_type const &entry_) const + { + return entry_.second.timer_id == _timer_id; + } + + private: + int _timer_id; +}; + +int zmq::timers_t::cancel (int timer_id_) +{ + // check first if timer exists at all + if (_timers.end () + == std::find_if (_timers.begin (), _timers.end (), + match_by_id (timer_id_))) { + errno = EINVAL; + return -1; + } + + // check if timer was already canceled + if (_cancelled_timers.count (timer_id_)) { + errno = EINVAL; + return -1; + } + + _cancelled_timers.insert (timer_id_); + + return 0; +} + +int zmq::timers_t::set_interval (int timer_id_, size_t interval_) +{ + const timersmap_t::iterator end = _timers.end (); + const timersmap_t::iterator it = + std::find_if (_timers.begin (), end, match_by_id (timer_id_)); + if (it != end) { + timer_t timer = it->second; + timer.interval = interval_; + uint64_t when = _clock.now_ms () + interval_; + _timers.erase (it); + _timers.insert (timersmap_t::value_type (when, timer)); + + return 0; + } + + errno = EINVAL; + return -1; +} + +int zmq::timers_t::reset (int timer_id_) +{ + const timersmap_t::iterator end = _timers.end (); + const timersmap_t::iterator it = + std::find_if (_timers.begin (), end, match_by_id (timer_id_)); + if (it != end) { + timer_t timer = it->second; + uint64_t when = _clock.now_ms () + timer.interval; + _timers.erase (it); + _timers.insert (timersmap_t::value_type (when, timer)); + + return 0; + } + + errno = EINVAL; + return -1; +} + +long zmq::timers_t::timeout () +{ + const uint64_t now = _clock.now_ms (); + long res = -1; + + const timersmap_t::iterator begin = _timers.begin (); + const timersmap_t::iterator end = _timers.end (); + timersmap_t::iterator it = begin; + for (; it != end; ++it) { + if (0 == _cancelled_timers.erase (it->second.timer_id)) { + // Live timer, lets return the timeout + res = std::max (static_cast (it->first - now), 0l); + break; + } + } + + // Remove timed-out timers + _timers.erase (begin, it); + + return res; +} + +int zmq::timers_t::execute () +{ + const uint64_t now = _clock.now_ms (); + + const timersmap_t::iterator begin = _timers.begin (); + const timersmap_t::iterator end = _timers.end (); + timersmap_t::iterator it = _timers.begin (); + for (; it != end; ++it) { + if (0 == _cancelled_timers.erase (it->second.timer_id)) { + // Timer is not cancelled + + // Map is ordered, if we have to wait for current timer we can stop. + if (it->first > now) + break; + + const timer_t &timer = it->second; + + timer.handler (timer.timer_id, timer.arg); + + _timers.insert ( + timersmap_t::value_type (now + timer.interval, timer)); + } + } + _timers.erase (begin, it); + + return 0; +} diff --git a/3rd/libzmq/src/timers.hpp b/3rd/libzmq/src/timers.hpp new file mode 100644 index 00000000..384d5e3f --- /dev/null +++ b/3rd/libzmq/src/timers.hpp @@ -0,0 +1,108 @@ +/* +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 . +*/ + +#ifndef __ZMQ_TIMERS_HPP_INCLUDED__ +#define __ZMQ_TIMERS_HPP_INCLUDED__ + +#include +#include +#include + +#include "clock.hpp" + +namespace zmq +{ +typedef void(timers_timer_fn) (int timer_id_, void *arg_); + +class timers_t +{ + public: + timers_t (); + ~timers_t (); + + // Add timer to the set, timer repeats forever, or until cancel is called. + // Returns a timer_id that is used to cancel the timer. + // Returns -1 if there was an error. + int add (size_t interval_, timers_timer_fn handler_, void *arg_); + + // Set the interval of the timer. + // This method is slow, cancelling exsting and adding a new timer yield better performance. + // Returns 0 on success and -1 on error. + int set_interval (int timer_id_, size_t interval_); + + // Reset the timer. + // This method is slow, cancelling exsting and adding a new timer yield better performance. + // Returns 0 on success and -1 on error. + int reset (int timer_id_); + + // Cancel a timer. + // Returns 0 on success and -1 on error. + int cancel (int timer_id_); + + // Returns the time in millisecond until the next timer. + // Returns -1 if no timer is due. + long timeout (); + + // Execute timers. + // Return 0 if all succeed and -1 if error. + int execute (); + + // Return false if object is not a timers class. + bool check_tag () const; + + private: + // Used to check whether the object is a timers class. + uint32_t _tag; + + int _next_timer_id; + + // Clock instance. + clock_t _clock; + + typedef struct timer_t + { + int timer_id; + size_t interval; + timers_timer_fn *handler; + void *arg; + } timer_t; + + typedef std::multimap timersmap_t; + timersmap_t _timers; + + typedef std::set cancelled_timers_t; + cancelled_timers_t _cancelled_timers; + + struct match_by_id; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (timers_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/tipc_address.cpp b/3rd/libzmq/src/tipc_address.cpp new file mode 100644 index 00000000..758b6a56 --- /dev/null +++ b/3rd/libzmq/src/tipc_address.cpp @@ -0,0 +1,172 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "tipc_address.hpp" + +#if defined ZMQ_HAVE_TIPC + +#include "err.hpp" + +#include +#include + +zmq::tipc_address_t::tipc_address_t () +{ + memset (&address, 0, sizeof address); + _random = false; +} + +zmq::tipc_address_t::tipc_address_t (const sockaddr *sa_, socklen_t sa_len_) +{ + zmq_assert (sa_ && sa_len_ > 0); + + memset (&address, 0, sizeof address); + if (sa_->sa_family == AF_TIPC) + memcpy (&address, sa_, sa_len_); + + _random = false; +} + +void zmq::tipc_address_t::set_random () +{ + _random = true; +} +bool zmq::tipc_address_t::is_random () const +{ + return _random; +} +bool zmq::tipc_address_t::is_service () const +{ + if (address.addrtype == TIPC_ADDR_ID) + return false; + + return true; +} +int zmq::tipc_address_t::resolve (const char *name_) +{ + unsigned int type = 0; + unsigned int lower = 0; + unsigned int upper = 0; + unsigned int ref = 0; + unsigned int z = 1, c = 0, n = 0; + char eof; + const char *domain; + int res; + + + if (strncmp (name_, "<*>", 3) == 0) { + set_random (); + address.family = AF_TIPC; + address.addrtype = TIPC_ADDR_ID; + address.addr.id.node = 0; + address.addr.id.ref = 0; + address.scope = 0; + return 0; + } + + res = sscanf (name_, "{%u,%u,%u}", &type, &lower, &upper); + /* Fetch optional domain suffix. */ + if ((domain = strchr (name_, '@'))) { + if (sscanf (domain, "@%u.%u.%u%c", &z, &c, &n, &eof) != 3) + return EINVAL; + } + if (res == 3) { + if (type < TIPC_RESERVED_TYPES || upper < lower) + return EINVAL; + address.family = AF_TIPC; + address.addrtype = TIPC_ADDR_NAMESEQ; + address.addr.nameseq.type = type; + address.addr.nameseq.lower = lower; + address.addr.nameseq.upper = upper; + address.scope = TIPC_ZONE_SCOPE; + return 0; + } + if (res == 2 && type > TIPC_RESERVED_TYPES) { + address.family = AF_TIPC; + address.addrtype = TIPC_ADDR_NAME; + address.addr.name.name.type = type; + address.addr.name.name.instance = lower; + address.addr.name.domain = tipc_addr (z, c, n); + address.scope = 0; + return 0; + } else if (res == 0) { + res = sscanf (name_, "<%u.%u.%u:%u>", &z, &c, &n, &ref); + if (res == 4) { + address.family = AF_TIPC; + address.addrtype = TIPC_ADDR_ID; + address.addr.id.node = tipc_addr (z, c, n); + address.addr.id.ref = ref; + address.scope = 0; + return 0; + } + } + return EINVAL; +} + +int zmq::tipc_address_t::to_string (std::string &addr_) const +{ + if (address.family != AF_TIPC) { + addr_.clear (); + return -1; + } + std::stringstream s; + if (address.addrtype == TIPC_ADDR_NAMESEQ + || address.addrtype == TIPC_ADDR_NAME) { + s << "tipc://" + << "{" << address.addr.nameseq.type; + s << ", " << address.addr.nameseq.lower; + s << ", " << address.addr.nameseq.upper << "}"; + addr_ = s.str (); + } else if (address.addrtype == TIPC_ADDR_ID || is_random ()) { + s << "tipc://" + << "<" << tipc_zone (address.addr.id.node); + s << "." << tipc_cluster (address.addr.id.node); + s << "." << tipc_node (address.addr.id.node); + s << ":" << address.addr.id.ref << ">"; + addr_ = s.str (); + } else { + addr_.clear (); + return -1; + } + return 0; +} + +const sockaddr *zmq::tipc_address_t::addr () const +{ + return (sockaddr *) &address; +} + +socklen_t zmq::tipc_address_t::addrlen () const +{ + return static_cast (sizeof address); +} + +#endif diff --git a/3rd/libzmq/src/tipc_address.hpp b/3rd/libzmq/src/tipc_address.hpp new file mode 100644 index 00000000..55ae0e7d --- /dev/null +++ b/3rd/libzmq/src/tipc_address.hpp @@ -0,0 +1,76 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_TIPC_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_TIPC_ADDRESS_HPP_INCLUDED__ + +#include + +#include "platform.hpp" + +#if defined ZMQ_HAVE_TIPC + +#include +#if defined ZMQ_HAVE_VXWORKS +#include +#else +#include +#endif + +namespace zmq +{ +class tipc_address_t +{ + public: + tipc_address_t (); + tipc_address_t (const sockaddr *sa, socklen_t sa_len); + + // This function sets up the address "{type, lower, upper}" for TIPC transport + int resolve (const char *name); + + // The opposite to resolve() + int to_string (std::string &addr_) const; + + // Handling different TIPC address types + bool is_service () const; + bool is_random () const; + void set_random (); + + const sockaddr *addr () const; + socklen_t addrlen () const; + + private: + bool _random; + struct sockaddr_tipc address; +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/tipc_connecter.cpp b/3rd/libzmq/src/tipc_connecter.cpp new file mode 100644 index 00000000..6057997f --- /dev/null +++ b/3rd/libzmq/src/tipc_connecter.cpp @@ -0,0 +1,175 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "tipc_connecter.hpp" + +#if defined ZMQ_HAVE_TIPC + +#include +#include + +#include "io_thread.hpp" +#include "platform.hpp" +#include "random.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "address.hpp" +#include "tipc_address.hpp" +#include "session_base.hpp" + +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif + +zmq::tipc_connecter_t::tipc_connecter_t (class io_thread_t *io_thread_, + class session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_) : + stream_connecter_base_t ( + io_thread_, session_, options_, addr_, delayed_start_) +{ + zmq_assert (_addr->protocol == "tipc"); +} + +void zmq::tipc_connecter_t::out_event () +{ + fd_t fd = connect (); + rm_handle (); + + // Handle the error condition by attempt to reconnect. + if (fd == retired_fd) { + close (); + add_reconnect_timer (); + return; + } + + create_engine (fd, get_socket_name (fd, socket_end_local)); +} + +void zmq::tipc_connecter_t::start_connecting () +{ + // Open the connecting socket. + int rc = open (); + + // Connect may succeed in synchronous manner. + if (rc == 0) { + _handle = add_fd (_s); + out_event (); + } + + // Connection establishment may be delayed. Poll for its completion. + else if (rc == -1 && errno == EINPROGRESS) { + _handle = add_fd (_s); + set_pollout (_handle); + _socket->event_connect_delayed ( + make_unconnected_connect_endpoint_pair (_endpoint), zmq_errno ()); + } + + // Handle any other error condition by eventual reconnect. + else { + if (_s != retired_fd) + close (); + add_reconnect_timer (); + } +} + +int zmq::tipc_connecter_t::open () +{ + zmq_assert (_s == retired_fd); + + // Cannot connect to random tipc addresses + if (_addr->resolved.tipc_addr->is_random ()) { + errno = EINVAL; + return -1; + } + // Create the socket. + _s = open_socket (AF_TIPC, SOCK_STREAM, 0); + if (_s == retired_fd) + return -1; + + // Set the non-blocking flag. + unblock_socket (_s); + // Connect to the remote peer. +#ifdef ZMQ_HAVE_VXWORKS + int rc = ::connect (s, (sockaddr *) addr->resolved.tipc_addr->addr (), + addr->resolved.tipc_addr->addrlen ()); +#else + int rc = ::connect (_s, _addr->resolved.tipc_addr->addr (), + _addr->resolved.tipc_addr->addrlen ()); +#endif + // Connect was successful immediately. + if (rc == 0) + return 0; + + // Translate other error codes indicating asynchronous connect has been + // launched to a uniform EINPROGRESS. + if (rc == -1 && errno == EINTR) { + errno = EINPROGRESS; + return -1; + } + // Forward the error. + return -1; +} + +zmq::fd_t zmq::tipc_connecter_t::connect () +{ + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + int err = 0; +#ifdef ZMQ_HAVE_VXWORKS + int len = sizeof (err); +#else + socklen_t len = sizeof (err); +#endif + int rc = getsockopt (_s, SOL_SOCKET, SO_ERROR, + reinterpret_cast (&err), &len); + if (rc == -1) + err = errno; + if (err != 0) { + // Assert if the error was caused by 0MQ bug. + // Networking problems are OK. No need to assert. + errno = err; + errno_assert (errno == ECONNREFUSED || errno == ECONNRESET + || errno == ETIMEDOUT || errno == EHOSTUNREACH + || errno == ENETUNREACH || errno == ENETDOWN); + + return retired_fd; + } + fd_t result = _s; + _s = retired_fd; + return result; +} + +#endif diff --git a/3rd/libzmq/src/tipc_connecter.hpp b/3rd/libzmq/src/tipc_connecter.hpp new file mode 100644 index 00000000..e12643ff --- /dev/null +++ b/3rd/libzmq/src/tipc_connecter.hpp @@ -0,0 +1,75 @@ +/* + 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 . +*/ + +#ifndef __TIPC_CONNECTER_HPP_INCLUDED__ +#define __TIPC_CONNECTER_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_TIPC + +#include "fd.hpp" +#include "stream_connecter_base.hpp" + +namespace zmq +{ +class tipc_connecter_t ZMQ_FINAL : public stream_connecter_base_t +{ + public: + // If 'delayed_start' is true connecter first waits for a while, + // then starts connection process. + tipc_connecter_t (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_); + + private: + // Handlers for I/O events. + void out_event () ZMQ_FINAL; + + // Internal function to start the actual connection establishment. + void start_connecting () ZMQ_FINAL; + + // Get the file descriptor of newly created connection. Returns + // retired_fd if the connection was unsuccessful. + fd_t connect (); + + // Open IPC connecting socket. Returns -1 in case of error, + // 0 if connect was successful immediately. Returns -1 with + // EAGAIN errno if async connect was launched. + int open (); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (tipc_connecter_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/tipc_listener.cpp b/3rd/libzmq/src/tipc_listener.cpp new file mode 100644 index 00000000..338327bc --- /dev/null +++ b/3rd/libzmq/src/tipc_listener.cpp @@ -0,0 +1,175 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "tipc_listener.hpp" + +#if defined ZMQ_HAVE_TIPC + +#include + +#include + +#include "tipc_address.hpp" +#include "io_thread.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "socket_base.hpp" +#include "address.hpp" + +#include +#include +#include +#if defined ZMQ_HAVE_VXWORKS +#include +#include +#else +#include +#endif + +zmq::tipc_listener_t::tipc_listener_t (io_thread_t *io_thread_, + socket_base_t *socket_, + const options_t &options_) : + stream_listener_base_t (io_thread_, socket_, options_) +{ +} + +void zmq::tipc_listener_t::in_event () +{ + fd_t fd = accept (); + + // If connection was reset by the peer in the meantime, just ignore it. + // TODO: Handle specific errors like ENFILE/EMFILE etc. + if (fd == retired_fd) { + _socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); + return; + } + + // Create the engine object for this connection. + create_engine (fd); +} + +std::string +zmq::tipc_listener_t::get_socket_name (zmq::fd_t fd_, + socket_end_t socket_end_) const +{ + return zmq::get_socket_name (fd_, socket_end_); +} + +int zmq::tipc_listener_t::set_local_address (const char *addr_) +{ + // Convert str to address struct + int rc = _address.resolve (addr_); + if (rc != 0) + return -1; + + // Cannot bind non-random Port Identity + const sockaddr_tipc *const a = + reinterpret_cast (_address.addr ()); + if (!_address.is_random () && a->addrtype == TIPC_ADDR_ID) { + errno = EINVAL; + return -1; + } + + // Create a listening socket. + _s = open_socket (AF_TIPC, SOCK_STREAM, 0); + if (_s == retired_fd) + return -1; + + // If random Port Identity, update address object to reflect the assigned address + if (_address.is_random ()) { + struct sockaddr_storage ss; + const zmq_socklen_t sl = get_socket_address (_s, socket_end_local, &ss); + if (sl == 0) + goto error; + + _address = + tipc_address_t (reinterpret_cast (&ss), sl); + } + + + _address.to_string (_endpoint); + + // Bind the socket to tipc name + if (_address.is_service ()) { +#ifdef ZMQ_HAVE_VXWORKS + rc = bind (_s, (sockaddr *) address.addr (), address.addrlen ()); +#else + rc = bind (_s, _address.addr (), _address.addrlen ()); +#endif + if (rc != 0) + goto error; + } + + // Listen for incoming connections. + rc = listen (_s, options.backlog); + if (rc != 0) + goto error; + + _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint), + _s); + return 0; + +error: + int err = errno; + close (); + errno = err; + return -1; +} + +zmq::fd_t zmq::tipc_listener_t::accept () +{ + // Accept one connection and deal with different failure modes. + // The situation where connection cannot be accepted due to insufficient + // resources is considered valid and treated by ignoring the connection. + struct sockaddr_storage ss = {}; + socklen_t ss_len = sizeof (ss); + + zmq_assert (_s != retired_fd); +#ifdef ZMQ_HAVE_VXWORKS + fd_t sock = ::accept (_s, (struct sockaddr *) &ss, (int *) &ss_len); +#else + fd_t sock = + ::accept (_s, reinterpret_cast (&ss), &ss_len); +#endif + if (sock == -1) { + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK + || errno == ENOBUFS || errno == EINTR + || errno == ECONNABORTED || errno == EPROTO + || errno == EMFILE || errno == ENFILE); + return retired_fd; + } + /*FIXME Accept filters?*/ + return sock; +} + +#endif diff --git a/3rd/libzmq/src/tipc_listener.hpp b/3rd/libzmq/src/tipc_listener.hpp new file mode 100644 index 00000000..040a99e6 --- /dev/null +++ b/3rd/libzmq/src/tipc_listener.hpp @@ -0,0 +1,77 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_TIPC_LISTENER_HPP_INCLUDED__ +#define __ZMQ_TIPC_LISTENER_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_TIPC + +#include + +#include "fd.hpp" +#include "stream_listener_base.hpp" +#include "tipc_address.hpp" + +namespace zmq +{ +class tipc_listener_t ZMQ_FINAL : public stream_listener_base_t +{ + public: + tipc_listener_t (zmq::io_thread_t *io_thread_, + zmq::socket_base_t *socket_, + const options_t &options_); + + // Set address to listen on. + int set_local_address (const char *addr_); + + protected: + std::string get_socket_name (fd_t fd_, + socket_end_t socket_end_) const ZMQ_FINAL; + + private: + // Handlers for I/O events. + void in_event () ZMQ_FINAL; + + // Accept the new connection. Returns the file descriptor of the + // newly created connection. The function may return retired_fd + // if the connection was dropped while waiting in the listen backlog. + fd_t accept (); + + // Address to listen on + tipc_address_t _address; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (tipc_listener_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/trie.cpp b/3rd/libzmq/src/trie.cpp new file mode 100644 index 00000000..3c1e8565 --- /dev/null +++ b/3rd/libzmq/src/trie.cpp @@ -0,0 +1,323 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" +#include "err.hpp" +#include "trie.hpp" + +#include + +#include +#include + +zmq::trie_t::trie_t () : _refcnt (0), _min (0), _count (0), _live_nodes (0) +{ +} + +zmq::trie_t::~trie_t () +{ + if (_count == 1) { + zmq_assert (_next.node); + LIBZMQ_DELETE (_next.node); + } else if (_count > 1) { + for (unsigned short i = 0; i != _count; ++i) { + LIBZMQ_DELETE (_next.table[i]); + } + free (_next.table); + } +} + +bool zmq::trie_t::add (unsigned char *prefix_, size_t size_) +{ + // We are at the node corresponding to the prefix. We are done. + if (!size_) { + ++_refcnt; + return _refcnt == 1; + } + + const unsigned char c = *prefix_; + if (c < _min || c >= _min + _count) { + // The character is out of range of currently handled + // characters. We have to extend the table. + if (!_count) { + _min = c; + _count = 1; + _next.node = NULL; + } else if (_count == 1) { + const unsigned char oldc = _min; + trie_t *oldp = _next.node; + _count = (_min < c ? c - _min : _min - c) + 1; + _next.table = + static_cast (malloc (sizeof (trie_t *) * _count)); + alloc_assert (_next.table); + for (unsigned short i = 0; i != _count; ++i) + _next.table[i] = 0; + _min = std::min (_min, c); + _next.table[oldc - _min] = oldp; + } else if (_min < c) { + // The new character is above the current character range. + const unsigned short old_count = _count; + _count = c - _min + 1; + _next.table = static_cast ( + realloc (_next.table, sizeof (trie_t *) * _count)); + zmq_assert (_next.table); + for (unsigned short i = old_count; i != _count; i++) + _next.table[i] = NULL; + } else { + // The new character is below the current character range. + const unsigned short old_count = _count; + _count = (_min + old_count) - c; + _next.table = static_cast ( + realloc (_next.table, sizeof (trie_t *) * _count)); + zmq_assert (_next.table); + memmove (_next.table + _min - c, _next.table, + old_count * sizeof (trie_t *)); + for (unsigned short i = 0; i != _min - c; i++) + _next.table[i] = NULL; + _min = c; + } + } + + // If next node does not exist, create one. + if (_count == 1) { + if (!_next.node) { + _next.node = new (std::nothrow) trie_t; + alloc_assert (_next.node); + ++_live_nodes; + zmq_assert (_live_nodes == 1); + } + return _next.node->add (prefix_ + 1, size_ - 1); + } + if (!_next.table[c - _min]) { + _next.table[c - _min] = new (std::nothrow) trie_t; + alloc_assert (_next.table[c - _min]); + ++_live_nodes; + zmq_assert (_live_nodes > 1); + } + return _next.table[c - _min]->add (prefix_ + 1, size_ - 1); +} + +bool zmq::trie_t::rm (unsigned char *prefix_, size_t size_) +{ + // TODO: Shouldn't an error be reported if the key does not exist? + if (!size_) { + if (!_refcnt) + return false; + _refcnt--; + return _refcnt == 0; + } + const unsigned char c = *prefix_; + if (!_count || c < _min || c >= _min + _count) + return false; + + trie_t *next_node = _count == 1 ? _next.node : _next.table[c - _min]; + + if (!next_node) + return false; + + const bool ret = next_node->rm (prefix_ + 1, size_ - 1); + + // Prune redundant nodes + if (next_node->is_redundant ()) { + LIBZMQ_DELETE (next_node); + zmq_assert (_count > 0); + + if (_count == 1) { + // The just pruned node is was the only live node + _next.node = 0; + _count = 0; + --_live_nodes; + zmq_assert (_live_nodes == 0); + } else { + _next.table[c - _min] = 0; + zmq_assert (_live_nodes > 1); + --_live_nodes; + + // Compact the table if possible + if (_live_nodes == 1) { + // We can switch to using the more compact single-node + // representation since the table only contains one live node + trie_t *node = 0; + // Since we always compact the table the pruned node must + // either be the left-most or right-most ptr in the node + // table + if (c == _min) { + // The pruned node is the left-most node ptr in the + // node table => keep the right-most node + node = _next.table[_count - 1]; + _min += _count - 1; + } else if (c == _min + _count - 1) { + // The pruned node is the right-most node ptr in the + // node table => keep the left-most node + node = _next.table[0]; + } + zmq_assert (node); + free (_next.table); + _next.node = node; + _count = 1; + } else if (c == _min) { + // We can compact the table "from the left". + // Find the left-most non-null node ptr, which we'll use as + // our new min + unsigned char new_min = _min; + for (unsigned short i = 1; i < _count; ++i) { + if (_next.table[i]) { + new_min = i + _min; + break; + } + } + zmq_assert (new_min != _min); + + trie_t **old_table = _next.table; + zmq_assert (new_min > _min); + zmq_assert (_count > new_min - _min); + + _count = _count - (new_min - _min); + _next.table = + static_cast (malloc (sizeof (trie_t *) * _count)); + alloc_assert (_next.table); + + memmove (_next.table, old_table + (new_min - _min), + sizeof (trie_t *) * _count); + free (old_table); + + _min = new_min; + } else if (c == _min + _count - 1) { + // We can compact the table "from the right". + // Find the right-most non-null node ptr, which we'll use to + // determine the new table size + unsigned short new_count = _count; + for (unsigned short i = 1; i < _count; ++i) { + if (_next.table[_count - 1 - i]) { + new_count = _count - i; + break; + } + } + zmq_assert (new_count != _count); + _count = new_count; + + trie_t **old_table = _next.table; + _next.table = + static_cast (malloc (sizeof (trie_t *) * _count)); + alloc_assert (_next.table); + + memmove (_next.table, old_table, sizeof (trie_t *) * _count); + free (old_table); + } + } + } + return ret; +} + +bool zmq::trie_t::check (const unsigned char *data_, size_t size_) const +{ + // This function is on critical path. It deliberately doesn't use + // recursion to get a bit better performance. + const trie_t *current = this; + while (true) { + // We've found a corresponding subscription! + if (current->_refcnt) + return true; + + // We've checked all the data and haven't found matching subscription. + if (!size_) + return false; + + // If there's no corresponding slot for the first character + // of the prefix, the message does not match. + const unsigned char c = *data_; + if (c < current->_min || c >= current->_min + current->_count) + return false; + + // Move to the next character. + if (current->_count == 1) + current = current->_next.node; + else { + current = current->_next.table[c - current->_min]; + if (!current) + return false; + } + data_++; + size_--; + } +} + +void zmq::trie_t::apply ( + void (*func_) (unsigned char *data_, size_t size_, void *arg_), void *arg_) +{ + unsigned char *buff = NULL; + apply_helper (&buff, 0, 0, func_, arg_); + free (buff); +} + +void zmq::trie_t::apply_helper (unsigned char **buff_, + size_t buffsize_, + size_t maxbuffsize_, + void (*func_) (unsigned char *data_, + size_t size_, + void *arg_), + void *arg_) const +{ + // If this node is a subscription, apply the function. + if (_refcnt) + func_ (*buff_, buffsize_, arg_); + + // Adjust the buffer. + if (buffsize_ >= maxbuffsize_) { + maxbuffsize_ = buffsize_ + 256; + *buff_ = static_cast (realloc (*buff_, maxbuffsize_)); + zmq_assert (*buff_); + } + + // If there are no subnodes in the trie, return. + if (_count == 0) + return; + + // If there's one subnode (optimisation). + if (_count == 1) { + (*buff_)[buffsize_] = _min; + buffsize_++; + _next.node->apply_helper (buff_, buffsize_, maxbuffsize_, func_, arg_); + return; + } + + // If there are multiple subnodes. + for (unsigned short c = 0; c != _count; c++) { + (*buff_)[buffsize_] = _min + c; + if (_next.table[c]) + _next.table[c]->apply_helper (buff_, buffsize_ + 1, maxbuffsize_, + func_, arg_); + } +} + +bool zmq::trie_t::is_redundant () const +{ + return _refcnt == 0 && _live_nodes == 0; +} diff --git a/3rd/libzmq/src/trie.hpp b/3rd/libzmq/src/trie.hpp new file mode 100644 index 00000000..32a2c2cb --- /dev/null +++ b/3rd/libzmq/src/trie.hpp @@ -0,0 +1,85 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_TRIE_HPP_INCLUDED__ +#define __ZMQ_TRIE_HPP_INCLUDED__ + +#include + +#include "macros.hpp" +#include "stdint.hpp" + +namespace zmq +{ +class trie_t +{ + public: + trie_t (); + ~trie_t (); + + // Add key to the trie. Returns true if this is a new item in the trie + // rather than a duplicate. + bool add (unsigned char *prefix_, size_t size_); + + // Remove key from the trie. Returns true if the item is actually + // removed from the trie. + bool rm (unsigned char *prefix_, size_t size_); + + // Check whether particular key is in the trie. + bool check (const unsigned char *data_, size_t size_) const; + + // Apply the function supplied to each subscription in the trie. + void apply (void (*func_) (unsigned char *data_, size_t size_, void *arg_), + void *arg_); + + private: + void apply_helper (unsigned char **buff_, + size_t buffsize_, + size_t maxbuffsize_, + void (*func_) (unsigned char *data_, + size_t size_, + void *arg_), + void *arg_) const; + bool is_redundant () const; + + uint32_t _refcnt; + unsigned char _min; + unsigned short _count; + unsigned short _live_nodes; + union + { + class trie_t *node; + class trie_t **table; + } _next; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (trie_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/tweetnacl.c b/3rd/libzmq/src/tweetnacl.c new file mode 100644 index 00000000..6e6f657c --- /dev/null +++ b/3rd/libzmq/src/tweetnacl.c @@ -0,0 +1,993 @@ +/* + Copyright (c) 2016-2017 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 . +*/ + +/* + The precompiled header is not used for c files so this is required here. +*/ +#include "platform.hpp" + +#if defined(ZMQ_USE_TWEETNACL) + +/* + Disable warnings for this source only, rather than for the whole + codebase when building with C99 (gcc >= 4.2) or with Microsoft's compiler +*/ +#if defined __GNUC__ \ + && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) \ + && __STDC_VERSION__ < 201112L +#pragma GCC diagnostic ignored "-Wsign-compare" +#elif defined _MSC_VER +#pragma warning(disable : 4018 4244 4146) +#endif + +/* clang-format off */ + +#include "tweetnacl.h" + +#define FOR(i,n) for (i = 0;i < n;++i) +#define sv static void + +static const u8 + _0[16], + _9[32] = {9}; +static const gf + gf0, + gf1 = {1}, + _121665 = {0xDB41,1}, + D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203}, + D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406}, + X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169}, + Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666}, + I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83}; + +static u32 L32(u32 x,int c) { return (x << c) | ((x&0xffffffff) >> (32 - c)); } + +static u32 ld32(const u8 *x) +{ + u32 u = x[3]; + u = (u<<8)|x[2]; + u = (u<<8)|x[1]; + return (u<<8)|x[0]; +} + +static u64 dl64(const u8 *x) +{ + u64 i,u=0; + FOR(i,8) u=(u<<8)|x[i]; + return u; +} + +sv st32(u8 *x,u32 u) +{ + int i; + FOR(i,4) { x[i] = u; u >>= 8; } +} + +sv ts64(u8 *x,u64 u) +{ + int i; + for (i = 7;i >= 0;--i) { x[i] = u; u >>= 8; } +} + +static int vn(const u8 *x,const u8 *y,int n) +{ + u32 i,d = 0; + FOR(i,n) d |= x[i]^y[i]; + return (1 & ((d - 1) >> 8)) - 1; +} + +int crypto_verify_16(const u8 *x,const u8 *y) +{ + return vn(x,y,16); +} + +int crypto_verify_32(const u8 *x,const u8 *y) +{ + return vn(x,y,32); +} + +sv core(u8 *out,const u8 *in,const u8 *k,const u8 *c,int h) +{ + u32 w[16],x[16],y[16],t[4]; + int i,j,m; + + FOR(i,4) { + x[5*i] = ld32(c+4*i); + x[1+i] = ld32(k+4*i); + x[6+i] = ld32(in+4*i); + x[11+i] = ld32(k+16+4*i); + } + + FOR(i,16) y[i] = x[i]; + + FOR(i,20) { + FOR(j,4) { + FOR(m,4) t[m] = x[(5*j+4*m)%16]; + t[1] ^= L32(t[0]+t[3], 7); + t[2] ^= L32(t[1]+t[0], 9); + t[3] ^= L32(t[2]+t[1],13); + t[0] ^= L32(t[3]+t[2],18); + FOR(m,4) w[4*j+(j+m)%4] = t[m]; + } + FOR(m,16) x[m] = w[m]; + } + + if (h) { + FOR(i,16) x[i] += y[i]; + FOR(i,4) { + x[5*i] -= ld32(c+4*i); + x[6+i] -= ld32(in+4*i); + } + FOR(i,4) { + st32(out+4*i,x[5*i]); + st32(out+16+4*i,x[6+i]); + } + } else + FOR(i,16) st32(out + 4 * i,x[i] + y[i]); +} + +int crypto_core_salsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) +{ + core(out,in,k,c,0); + return 0; +} + +int crypto_core_hsalsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) +{ + core(out,in,k,c,1); + return 0; +} + +static const u8 sigma[16] = "expand 32-byte k"; + +int crypto_stream_salsa20_xor(u8 *c,const u8 *m,u64 b,const u8 *n,const u8 *k) +{ + u8 z[16],x[64]; + u32 u,i; + if (!b) return 0; + FOR(i,16) z[i] = 0; + FOR(i,8) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + FOR(i,64) c[i] = (m?m[i]:0) ^ x[i]; + u = 1; + for (i = 8;i < 16;++i) { + u += (u32) z[i]; + z[i] = u; + u >>= 8; + } + b -= 64; + c += 64; + if (m) m += 64; + } + if (b) { + crypto_core_salsa20(x,z,k,sigma); + FOR(i,b) c[i] = (m?m[i]:0) ^ x[i]; + } + return 0; +} + +int crypto_stream_salsa20(u8 *c,u64 d,const u8 *n,const u8 *k) +{ + return crypto_stream_salsa20_xor(c,0,d,n,k); +} + +int crypto_stream(u8 *c,u64 d,const u8 *n,const u8 *k) +{ + u8 s[32]; + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20(c,d,n+16,s); +} + +int crypto_stream_xor(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + u8 s[32]; + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20_xor(c,m,d,n+16,s); +} + +sv add1305(u32 *h,const u32 *c) +{ + u32 j,u = 0; + FOR(j,17) { + u += h[j] + c[j]; + h[j] = u & 255; + u >>= 8; + } +} + +static const u32 minusp[17] = { + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 +} ; + +int crypto_onetimeauth(u8 *out,const u8 *m,u64 n,const u8 *k) +{ + u32 s,i,j,u,x[17],r[17],h[17],c[17],g[17]; + + FOR(j,17) r[j]=h[j]=0; + FOR(j,16) r[j]=k[j]; + r[3]&=15; + r[4]&=252; + r[7]&=15; + r[8]&=252; + r[11]&=15; + r[12]&=252; + r[15]&=15; + + while (n > 0) { + FOR(j,17) c[j] = 0; + for (j = 0;(j < 16) && (j < n);++j) c[j] = m[j]; + c[j] = 1; + m += j; n -= j; + add1305(h,c); + FOR(i,17) { + x[i] = 0; + FOR(j,17) x[i] += h[j] * ((j <= i) ? r[i - j] : 320 * r[i + 17 - j]); + } + FOR(i,17) h[i] = x[i]; + u = 0; + FOR(j,16) { + u += h[j]; + h[j] = u & 255; + u >>= 8; + } + u += h[16]; h[16] = u & 3; + u = 5 * (u >> 2); + FOR(j,16) { + u += h[j]; + h[j] = u & 255; + u >>= 8; + } + u += h[16]; h[16] = u; + } + + FOR(j,17) g[j] = h[j]; + add1305(h,minusp); + s = -(h[16] >> 7); + FOR(j,17) h[j] ^= s & (g[j] ^ h[j]); + + FOR(j,16) c[j] = k[j + 16]; + c[16] = 0; + add1305(h,c); + FOR(j,16) out[j] = h[j]; + return 0; +} + +int crypto_onetimeauth_verify(const u8 *h,const u8 *m,u64 n,const u8 *k) +{ + u8 x[16]; + crypto_onetimeauth(x,m,n,k); + return crypto_verify_16(h,x); +} + +int crypto_secretbox(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + int i; + if (d < 32) return -1; + crypto_stream_xor(c,m,d,n,k); + crypto_onetimeauth(c + 16,c + 32,d - 32,c); + FOR(i,16) c[i] = 0; + return 0; +} + +int crypto_secretbox_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) +{ + int i; + u8 x[32]; + if (d < 32) return -1; + crypto_stream(x,32,n,k); + if (crypto_onetimeauth_verify(c + 16,c + 32,d - 32,x) != 0) return -1; + crypto_stream_xor(m,c,d,n,k); + FOR(i,32) m[i] = 0; + return 0; +} + +sv set25519(gf r, const gf a) +{ + int i; + FOR(i,16) r[i]=a[i]; +} + +sv car25519(gf o) +{ + int i; + i64 c; + FOR(i,16) { + o[i]+=(1LL<<16); + c=o[i]>>16; + o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15); + o[i]-=c<<16; + } +} + +sv sel25519(gf p,gf q,int b) +{ + i64 t,i,c=~(b-1); + FOR(i,16) { + t= c&(p[i]^q[i]); + p[i]^=t; + q[i]^=t; + } +} + +sv pack25519(u8 *o,const gf n) +{ + int i,j,b; + gf m,t; + FOR(i,16) t[i]=n[i]; + car25519(t); + car25519(t); + car25519(t); + FOR(j,2) { + m[0]=t[0]-0xffed; + for(i=1;i<15;i++) { + m[i]=t[i]-0xffff-((m[i-1]>>16)&1); + m[i-1]&=0xffff; + } + m[15]=t[15]-0x7fff-((m[14]>>16)&1); + b=(m[15]>>16)&1; + m[14]&=0xffff; + sel25519(t,m,1-b); + } + FOR(i,16) { + o[2*i]=t[i]&0xff; + o[2*i+1]=t[i]>>8; + } +} + +static int neq25519(const gf a, const gf b) +{ + u8 c[32],d[32]; + pack25519(c,a); + pack25519(d,b); + return crypto_verify_32(c,d); +} + +static u8 par25519(const gf a) +{ + u8 d[32]; + pack25519(d,a); + return d[0]&1; +} + +sv unpack25519(gf o, const u8 *n) +{ + int i; + FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8); + o[15]&=0x7fff; +} + +sv A(gf o,const gf a,const gf b) +{ + int i; + FOR(i,16) o[i]=a[i]+b[i]; +} + +sv Z(gf o,const gf a,const gf b) +{ + int i; + FOR(i,16) o[i]=a[i]-b[i]; +} + +sv M(gf o,const gf a,const gf b) +{ + i64 i,j,t[31]; + FOR(i,31) t[i]=0; + FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j]; + FOR(i,15) t[i]+=38*t[i+16]; + FOR(i,16) o[i]=t[i]; + car25519(o); + car25519(o); +} + +sv S(gf o,const gf a) +{ + M(o,a,a); +} + +sv inv25519(gf o,const gf i) +{ + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=253;a>=0;a--) { + S(c,c); + if(a!=2&&a!=4) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +sv pow2523(gf o,const gf i) +{ + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=250;a>=0;a--) { + S(c,c); + if(a!=1) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +int crypto_scalarmult(u8 *q,const u8 *n,const u8 *p) +{ + u8 z[32]; + i64 x[80],r,i; + gf a,b,c,d,e,f; + FOR(i,31) z[i]=n[i]; + z[31]=(n[31]&127)|64; + z[0]&=248; + unpack25519(x,p); + FOR(i,16) { + b[i]=x[i]; + d[i]=a[i]=c[i]=0; + } + a[0]=d[0]=1; + for(i=254;i>=0;--i) { + r=(z[i>>3]>>(i&7))&1; + sel25519(a,b,r); + sel25519(c,d,r); + A(e,a,c); + Z(a,a,c); + A(c,b,d); + Z(b,b,d); + S(d,e); + S(f,a); + M(a,c,a); + M(c,b,e); + A(e,a,c); + Z(a,a,c); + S(b,a); + Z(c,d,f); + M(a,c,_121665); + A(a,a,d); + M(c,c,a); + M(a,d,f); + M(d,b,x); + S(b,e); + sel25519(a,b,r); + sel25519(c,d,r); + } + FOR(i,16) { + x[i+16]=a[i]; + x[i+32]=c[i]; + x[i+48]=b[i]; + x[i+64]=d[i]; + } + inv25519(x+32,x+32); + M(x+16,x+16,x+32); + pack25519(q,x+16); + return 0; +} + +int crypto_scalarmult_base(u8 *q,const u8 *n) +{ + return crypto_scalarmult(q,n,_9); +} + +int crypto_box_keypair(u8 *y,u8 *x) +{ + randombytes(x,32); + return crypto_scalarmult_base(y,x); +} + +int crypto_box_beforenm(u8 *k,const u8 *y,const u8 *x) +{ + u8 s[32]; + crypto_scalarmult(s,x,y); + return crypto_core_hsalsa20(k,_0,s,sigma); +} + +int crypto_box_afternm(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + return crypto_secretbox(c,m,d,n,k); +} + +int crypto_box_open_afternm(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) +{ + return crypto_secretbox_open(m,c,d,n,k); +} + +int crypto_box(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *y,const u8 *x) +{ + u8 k[32]; + crypto_box_beforenm(k,y,x); + return crypto_box_afternm(c,m,d,n,k); +} + +int crypto_box_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *y,const u8 *x) +{ + u8 k[32]; + crypto_box_beforenm(k,y,x); + return crypto_box_open_afternm(m,c,d,n,k); +} + +static u64 R(u64 x,int c) { return (x >> c) | (x << (64 - c)); } +static u64 Ch(u64 x,u64 y,u64 z) { return (x & y) ^ (~x & z); } +static u64 Maj(u64 x,u64 y,u64 z) { return (x & y) ^ (x & z) ^ (y & z); } +static u64 Sigma0(u64 x) { return R(x,28) ^ R(x,34) ^ R(x,39); } +static u64 Sigma1(u64 x) { return R(x,14) ^ R(x,18) ^ R(x,41); } +static u64 sigma0(u64 x) { return R(x, 1) ^ R(x, 8) ^ (x >> 7); } +static u64 sigma1(u64 x) { return R(x,19) ^ R(x,61) ^ (x >> 6); } + +static const u64 K[80] = +{ + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +int crypto_hashblocks(u8 *x,const u8 *m,u64 n) +{ + u64 z[8],b[8],a[8],w[16],t; + int i,j; + + FOR(i,8) z[i] = a[i] = dl64(x + 8 * i); + + while (n >= 128) { + FOR(i,16) w[i] = dl64(m + 8 * i); + + FOR(i,80) { + FOR(j,8) b[j] = a[j]; + t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16]; + b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]); + b[3] += t; + FOR(j,8) a[(j+1)%8] = b[j]; + if (i%16 == 15) + FOR(j,16) + w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]); + } + + FOR(i,8) { a[i] += z[i]; z[i] = a[i]; } + + m += 128; + n -= 128; + } + + FOR(i,8) ts64(x+8*i,z[i]); + + return n; +} + +static const u8 iv[64] = { + 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, + 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, + 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, + 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, + 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, + 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, + 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, + 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 +} ; + +int crypto_hash(u8 *out,const u8 *m,u64 n) +{ + u8 h[64],x[256]; + u64 i,b = n; + + FOR(i,64) h[i] = iv[i]; + + crypto_hashblocks(h,m,n); + m += n; + n &= 127; + m -= n; + + FOR(i,256) x[i] = 0; + FOR(i,n) x[i] = m[i]; + x[n] = 128; + + n = 256-128*(n<112); + x[n-9] = b >> 61; + ts64(x+n-8,b<<3); + crypto_hashblocks(h,x,n); + + FOR(i,64) out[i] = h[i]; + + return 0; +} + +sv add(gf p[4],gf q[4]) +{ + gf a,b,c,d,t,e,f,g,h; + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +sv cswap(gf p[4],gf q[4],u8 b) +{ + int i; + FOR(i,4) + sel25519(p[i],q[i],b); +} + +sv pack(u8 *r,gf p[4]) +{ + gf tx, ty, zi; + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +sv scalarmult(gf p[4],gf q[4],const u8 *s) +{ + int i; + set25519(p[0],gf0); + set25519(p[1],gf1); + set25519(p[2],gf1); + set25519(p[3],gf0); + for (i = 255;i >= 0;--i) { + const u8 b = (s[i/8]>>(i&7))&1; + cswap(p,q,b); + add(q,p); + add(p,p); + cswap(p,q,b); + } +} + +sv scalarbase(gf p[4],const u8 *s) +{ + gf q[4]; + set25519(q[0],X); + set25519(q[1],Y); + set25519(q[2],gf1); + M(q[3],X,Y); + scalarmult(p,q,s); +} + +int crypto_sign_keypair(u8 *pk, u8 *sk) +{ + u8 d[64]; + gf p[4]; + int i; + + randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase(p,d); + pack(pk,p); + + FOR(i,32) sk[32 + i] = pk[i]; + return 0; +} + +static const u64 L[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10}; + +sv modL(u8 *r,i64 x[64]) +{ + i64 carry,i,j; + for (i = 63;i >= 32;--i) { + carry = 0; + for (j = i - 32;j < i - 12;++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = (x[j] + 128) >> 8; + x[j] -= carry << 8; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + FOR(j,32) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + FOR(j,32) x[j] -= carry * L[j]; + FOR(i,32) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +sv reduce(u8 *r) +{ + i64 x[64],i; + FOR(i,64) x[i] = (u64) r[i]; + FOR(i,64) r[i] = 0; + modL(r,x); +} + +int crypto_sign(u8 *sm,u64 *smlen,const u8 *m,u64 n,const u8 *sk) +{ + u8 d[64],h[64],r[64]; + i64 i,j,x[64]; + gf p[4]; + + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + *smlen = n+64; + FOR(i,n) sm[64 + i] = m[i]; + FOR(i,32) sm[32 + i] = d[32 + i]; + + crypto_hash(r, sm+32, n+32); + reduce(r); + scalarbase(p,r); + pack(sm,p); + + FOR(i,32) sm[i+32] = sk[i+32]; + crypto_hash(h,sm,n + 64); + reduce(h); + + FOR(i,64) x[i] = 0; + FOR(i,32) x[i] = (u64) r[i]; + FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j]; + modL(sm + 32,x); + + return 0; +} + +static int unpackneg(gf r[4],const u8 p[32]) +{ + gf t, chk, num, den, den2, den4, den6; + set25519(r[2],gf1); + unpack25519(r[1],p); + S(num,r[1]); + M(den,num,D); + Z(num,num,r[2]); + A(den,r[2],den); + + S(den2,den); + S(den4,den2); + M(den6,den4,den2); + M(t,den6,num); + M(t,t,den); + + pow2523(t,t); + M(t,t,num); + M(t,t,den); + M(t,t,den); + M(r[0],t,den); + + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) M(r[0],r[0],I); + + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) return -1; + + if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]); + + M(r[3],r[0],r[1]); + return 0; +} + +int crypto_sign_open(u8 *m,u64 *mlen,const u8 *sm,u64 n,const u8 *pk) +{ + int i; + u8 t[32],h[64]; + gf p[4],q[4]; + + *mlen = -1; + if (n < 64) return -1; + + if (unpackneg(q,pk)) return -1; + + FOR(i,n) m[i] = sm[i]; + FOR(i,32) m[i+32] = pk[i]; + crypto_hash(h,m,n); + reduce(h); + scalarmult(p,q,h); + + scalarbase(q,sm + 32); + add(p,q); + pack(t,p); + + n -= 64; + if (crypto_verify_32(sm, t)) { + FOR(i,n) m[i] = 0; + return -1; + } + + FOR(i,n) m[i] = sm[i + 64]; + *mlen = n; + return 0; +} + + +#ifdef ZMQ_HAVE_WINDOWS + +#include +#include + +#define NCP ((HCRYPTPROV) 0) + +HCRYPTPROV hProvider = NCP; + +void randombytes(unsigned char *x,unsigned long long xlen) +{ + unsigned i; + BOOL ret; + + if (hProvider == NCP) { + for (;;) { + ret = CryptAcquireContext(&hProvider, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); + if (ret != FALSE) + break; + Sleep (1); + } + } + while (xlen > 0) { + if (xlen < 1048576) + i = (unsigned) xlen; + else + i = 1048576; + + ret = CryptGenRandom(hProvider, i, x); + if (ret == FALSE) { + Sleep(1); + continue; + } + x += i; + xlen -= i; + } +} + +int randombytes_close(void) +{ + int rc = -1; + if ((hProvider != NCP) && (CryptReleaseContext(hProvider, 0) != FALSE)) { + hProvider = NCP; + rc = 0; + } + return rc; +} + +int sodium_init (void) +{ + return 0; +} + +#else + +#include +#include + +#ifdef ZMQ_HAVE_GETRANDOM +#include +#else +#include +#include +#include + +static int fd = -1; +#endif + +void randombytes (unsigned char *x,unsigned long long xlen) +{ + int i; +#ifndef ZMQ_HAVE_GETRANDOM + /* Require that random_open has already been called, to avoid + race conditions. */ + assert (fd != -1); +#endif + while (xlen > 0) { + if (xlen < 1048576) + i = xlen; + else + i = 1048576; + +#ifdef ZMQ_HAVE_GETRANDOM + i = getrandom (x, i, 0); +#else + i = read(fd,x,i); +#endif + if (i < 1) { + sleep (1); + continue; + } + x += i; + xlen -= i; + } +} + +/* Do not call manually! Use random_close from random.hpp */ +int randombytes_close (void) +{ + int rc = -1; +#ifndef ZMQ_HAVE_GETRANDOM + if (fd != -1 && close(fd) == 0) { + fd = -1; + rc = 0; + } +#endif /* ZMQ_HAVE_GETRANDOM */ + return rc; +} + +/* Do not call manually! Use random_open from random.hpp */ +int sodium_init (void) +{ +#ifndef ZMQ_HAVE_GETRANDOM + if (fd == -1) { + for (;;) { + int flags = O_RDONLY; +#ifdef ZMQ_HAVE_O_CLOEXEC + flags |= O_CLOEXEC; +#endif + fd = open ("/dev/urandom", flags); + if (fd != -1) + break; + sleep (1); + } +#if !defined ZMQ_HAVE_O_CLOEXEC && defined FD_CLOEXEC + int rc = fcntl (fd, F_SETFD, FD_CLOEXEC); + assert (rc != -1); +#endif + } +#endif /* ZMQ_HAVE_GETRANDOM */ + return 0; +} + +#endif + +#endif +/* clang-format on */ diff --git a/3rd/libzmq/src/tweetnacl.h b/3rd/libzmq/src/tweetnacl.h new file mode 100644 index 00000000..28fd9c19 --- /dev/null +++ b/3rd/libzmq/src/tweetnacl.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2016-2017 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 . +*/ + +#ifndef TWEETNACL_H +#define TWEETNACL_H + +#if defined(ZMQ_USE_TWEETNACL) + +#define crypto_box_SECRETKEYBYTES 32 +#define crypto_box_BOXZEROBYTES 16 +#define crypto_box_NONCEBYTES 24 +#define crypto_box_ZEROBYTES 32 +#define crypto_box_PUBLICKEYBYTES 32 +#define crypto_box_BEFORENMBYTES 32 +#define crypto_secretbox_KEYBYTES 32 +#define crypto_secretbox_NONCEBYTES 24 +#define crypto_secretbox_ZEROBYTES 32 +#define crypto_secretbox_BOXZEROBYTES 16 +typedef unsigned char u8; +typedef unsigned long u32; +typedef unsigned long long u64; +typedef long long i64; +typedef i64 gf[16]; + +#ifdef __cplusplus +extern "C" { +#endif +void randombytes (unsigned char *, unsigned long long); +/* Do not call manually! Use random_close from random.hpp */ +int randombytes_close (void); +/* Do not call manually! Use random_open from random.hpp */ +int sodium_init (void); + +int crypto_box_keypair (u8 *y_, u8 *x_); +int crypto_box_afternm ( + u8 *c_, const u8 *m_, u64 d_, const u8 *n_, const u8 *k_); +int crypto_box_open_afternm ( + u8 *m_, const u8 *c_, u64 d_, const u8 *n_, const u8 *k_); +int crypto_box ( + u8 *c_, const u8 *m_, u64 d_, const u8 *n_, const u8 *y_, const u8 *x_); +int crypto_box_open ( + u8 *m_, const u8 *c_, u64 d_, const u8 *n_, const u8 *y_, const u8 *x_); +int crypto_box_beforenm (u8 *k_, const u8 *y_, const u8 *x_); +int crypto_scalarmult_base (u8 *q_, const u8 *n_); +int crypto_secretbox (u8 *c_, const u8 *m_, u64 d_, const u8 *n_, const u8 *k_); +int crypto_secretbox_open ( + u8 *m_, const u8 *c_, u64 d_, const u8 *n_, const u8 *k_); +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/3rd/libzmq/src/udp_address.cpp b/3rd/libzmq/src/udp_address.cpp new file mode 100644 index 00000000..41e4770d --- /dev/null +++ b/3rd/libzmq/src/udp_address.cpp @@ -0,0 +1,210 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "macros.hpp" +#include "udp_address.hpp" +#include "stdint.hpp" +#include "err.hpp" +#include "ip.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#endif + +zmq::udp_address_t::udp_address_t () : + _bind_interface (-1), + _is_multicast (false) +{ + _bind_address = ip_addr_t::any (AF_INET); + _target_address = ip_addr_t::any (AF_INET); +} + +zmq::udp_address_t::~udp_address_t () +{ +} + +int zmq::udp_address_t::resolve (const char *name_, bool bind_, bool ipv6_) +{ + // No IPv6 support yet + bool has_interface = false; + + _address = name_; + + // If we have a semicolon then we should have an interface specifier in the + // URL + const char *src_delimiter = strrchr (name_, ';'); + if (src_delimiter) { + const std::string src_name (name_, src_delimiter - name_); + + ip_resolver_options_t src_resolver_opts; + + src_resolver_opts + .bindable (true) + // Restrict hostname/service to literals to avoid any DNS + // lookups or service-name irregularity due to + // indeterminate socktype. + .allow_dns (false) + .allow_nic_name (true) + .ipv6 (ipv6_) + .expect_port (false); + + ip_resolver_t src_resolver (src_resolver_opts); + + const int rc = src_resolver.resolve (&_bind_address, src_name.c_str ()); + + if (rc != 0) { + return -1; + } + + if (_bind_address.is_multicast ()) { + // It doesn't make sense to have a multicast address as a source + errno = EINVAL; + return -1; + } + + // This is a hack because we need the interface index when binding + // multicast IPv6, we can't do it by address. Unfortunately for the + // time being we don't have a generic platform-independent function to + // resolve an interface index from an address, so we only support it + // when an actual interface name is provided. + if (src_name == "*") { + _bind_interface = 0; + } else { +#ifdef HAVE_IF_NAMETOINDEX + _bind_interface = if_nametoindex (src_name.c_str ()); + if (_bind_interface == 0) { + // Error, probably not an interface name. + _bind_interface = -1; + } +#endif + } + + has_interface = true; + name_ = src_delimiter + 1; + } + + ip_resolver_options_t resolver_opts; + + resolver_opts.bindable (bind_) + .allow_dns (!bind_) + .allow_nic_name (bind_) + .expect_port (true) + .ipv6 (ipv6_); + + ip_resolver_t resolver (resolver_opts); + + const int rc = resolver.resolve (&_target_address, name_); + if (rc != 0) { + return -1; + } + + _is_multicast = _target_address.is_multicast (); + const uint16_t port = _target_address.port (); + + if (has_interface) { + // If we have an interface specifier then the target address must be a + // multicast address + if (!_is_multicast) { + errno = EINVAL; + return -1; + } + + _bind_address.set_port (port); + } else { + // If we don't have an explicit interface specifier then the URL is + // ambiguous: if the target address is multicast then it's the + // destination address and the bind address is ANY, if it's unicast + // then it's the bind address when 'bind_' is true and the destination + // otherwise + if (_is_multicast || !bind_) { + _bind_address = ip_addr_t::any (_target_address.family ()); + _bind_address.set_port (port); + _bind_interface = 0; + } else { + // If we were asked for a bind socket and the address + // provided was not multicast then it was really meant as + // a bind address and the target_address is useless. + _bind_address = _target_address; + } + } + + if (_bind_address.family () != _target_address.family ()) { + errno = EINVAL; + return -1; + } + + // For IPv6 multicast we *must* have an interface index since we can't + // bind by address. + if (ipv6_ && _is_multicast && _bind_interface < 0) { + errno = ENODEV; + return -1; + } + + return 0; +} + +int zmq::udp_address_t::family () const +{ + return _bind_address.family (); +} + +bool zmq::udp_address_t::is_mcast () const +{ + return _is_multicast; +} + +const zmq::ip_addr_t *zmq::udp_address_t::bind_addr () const +{ + return &_bind_address; +} + +int zmq::udp_address_t::bind_if () const +{ + return _bind_interface; +} + +const zmq::ip_addr_t *zmq::udp_address_t::target_addr () const +{ + return &_target_address; +} + +int zmq::udp_address_t::to_string (std::string &addr_) +{ + // XXX what do (factor TCP code?) + addr_ = _address; + return 0; +} diff --git a/3rd/libzmq/src/udp_address.hpp b/3rd/libzmq/src/udp_address.hpp new file mode 100644 index 00000000..a2530d8f --- /dev/null +++ b/3rd/libzmq/src/udp_address.hpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_UDP_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_UDP_ADDRESS_HPP_INCLUDED__ + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#endif + +#include + +#include "ip_resolver.hpp" + +namespace zmq +{ +class udp_address_t +{ + public: + udp_address_t (); + virtual ~udp_address_t (); + + int resolve (const char *name_, bool bind_, bool ipv6_); + + // The opposite to resolve() + virtual int to_string (std::string &addr_); + + + int family () const; + + bool is_mcast () const; + + const ip_addr_t *bind_addr () const; + int bind_if () const; + const ip_addr_t *target_addr () const; + + private: + ip_addr_t _bind_address; + int _bind_interface; + ip_addr_t _target_address; + bool _is_multicast; + std::string _address; +}; +} + +#endif diff --git a/3rd/libzmq/src/udp_engine.cpp b/3rd/libzmq/src/udp_engine.cpp new file mode 100644 index 00000000..0f7f58b3 --- /dev/null +++ b/3rd/libzmq/src/udp_engine.cpp @@ -0,0 +1,629 @@ +/* +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 . +*/ + +#include "precompiled.hpp" + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#endif + +#include "udp_address.hpp" +#include "udp_engine.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "ip.hpp" + +// OSX uses a different name for this socket option +#ifndef IPV6_ADD_MEMBERSHIP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif + +#ifdef __APPLE__ +#include +#endif + +zmq::udp_engine_t::udp_engine_t (const options_t &options_) : + _plugged (false), + _fd (-1), + _session (NULL), + _handle (static_cast (NULL)), + _address (NULL), + _options (options_), + _send_enabled (false), + _recv_enabled (false) +{ +} + +zmq::udp_engine_t::~udp_engine_t () +{ + zmq_assert (!_plugged); + + if (_fd != retired_fd) { +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (_fd); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = close (_fd); + errno_assert (rc == 0); +#endif + _fd = retired_fd; + } +} + +int zmq::udp_engine_t::init (address_t *address_, bool send_, bool recv_) +{ + zmq_assert (address_); + zmq_assert (send_ || recv_); + _send_enabled = send_; + _recv_enabled = recv_; + _address = address_; + + _fd = open_socket (_address->resolved.udp_addr->family (), SOCK_DGRAM, + IPPROTO_UDP); + if (_fd == retired_fd) + return -1; + + unblock_socket (_fd); + + return 0; +} + +void zmq::udp_engine_t::plug (io_thread_t *io_thread_, session_base_t *session_) +{ + zmq_assert (!_plugged); + _plugged = true; + + zmq_assert (!_session); + zmq_assert (session_); + _session = session_; + + // Connect to I/O threads poller object. + io_object_t::plug (io_thread_); + _handle = add_fd (_fd); + + const udp_address_t *const udp_addr = _address->resolved.udp_addr; + + int rc = 0; + + // Bind the socket to a device if applicable + if (!_options.bound_device.empty ()) { + rc = rc | bind_to_device (_fd, _options.bound_device); + if (rc != 0) { + assert_success_or_recoverable (_fd, rc); + error (connection_error); + return; + } + } + + if (_send_enabled) { + if (!_options.raw_socket) { + const ip_addr_t *out = udp_addr->target_addr (); + _out_address = out->as_sockaddr (); + _out_address_len = out->sockaddr_len (); + + if (out->is_multicast ()) { + const bool is_ipv6 = (out->family () == AF_INET6); + rc = rc + | set_udp_multicast_loop (_fd, is_ipv6, + _options.multicast_loop); + + if (_options.multicast_hops > 0) { + rc = rc + | set_udp_multicast_ttl (_fd, is_ipv6, + _options.multicast_hops); + } + + rc = rc | set_udp_multicast_iface (_fd, is_ipv6, udp_addr); + } + } else { + /// XXX fixme ? + _out_address = reinterpret_cast (&_raw_address); + _out_address_len = + static_cast (sizeof (sockaddr_in)); + } + } + + if (_recv_enabled) { + rc = rc | set_udp_reuse_address (_fd, true); + + const ip_addr_t *bind_addr = udp_addr->bind_addr (); + ip_addr_t any = ip_addr_t::any (bind_addr->family ()); + const ip_addr_t *real_bind_addr; + + const bool multicast = udp_addr->is_mcast (); + + if (multicast) { + // Multicast addresses should be allowed to bind to more than + // one port as all ports should receive the message + rc = rc | set_udp_reuse_port (_fd, true); + + // In multicast we should bind ANY and use the mreq struct to + // specify the interface + any.set_port (bind_addr->port ()); + + real_bind_addr = &any; + } else { + real_bind_addr = bind_addr; + } + + if (rc != 0) { + error (protocol_error); + return; + } + +#ifdef ZMQ_HAVE_VXWORKS + rc = rc + | bind (_fd, (sockaddr *) real_bind_addr->as_sockaddr (), + real_bind_addr->sockaddr_len ()); +#else + rc = rc + | bind (_fd, real_bind_addr->as_sockaddr (), + real_bind_addr->sockaddr_len ()); +#endif + if (rc != 0) { + assert_success_or_recoverable (_fd, rc); + error (connection_error); + return; + } + + if (multicast) { + rc = rc | add_membership (_fd, udp_addr); + } + } + + if (rc != 0) { + error (protocol_error); + } else { + if (_send_enabled) { + set_pollout (_handle); + } + + if (_recv_enabled) { + set_pollin (_handle); + + // Call restart output to drop all join/leave commands + restart_output (); + } + } +} + +int zmq::udp_engine_t::set_udp_multicast_loop (fd_t s_, + bool is_ipv6_, + bool loop_) +{ + int level; + int optname; + + if (is_ipv6_) { + level = IPPROTO_IPV6; + optname = IPV6_MULTICAST_LOOP; + } else { + level = IPPROTO_IP; + optname = IP_MULTICAST_LOOP; + } + + int loop = loop_ ? 1 : 0; + const int rc = setsockopt (s_, level, optname, + reinterpret_cast (&loop), sizeof (loop)); + assert_success_or_recoverable (s_, rc); + return rc; +} + +int zmq::udp_engine_t::set_udp_multicast_ttl (fd_t s_, bool is_ipv6_, int hops_) +{ + int level; + + if (is_ipv6_) { + level = IPPROTO_IPV6; + } else { + level = IPPROTO_IP; + } + + const int rc = + setsockopt (s_, level, IP_MULTICAST_TTL, + reinterpret_cast (&hops_), sizeof (hops_)); + assert_success_or_recoverable (s_, rc); + return rc; +} + +int zmq::udp_engine_t::set_udp_multicast_iface (fd_t s_, + bool is_ipv6_, + const udp_address_t *addr_) +{ + int rc = 0; + + if (is_ipv6_) { + int bind_if = addr_->bind_if (); + + if (bind_if > 0) { + // If a bind interface is provided we tell the + // kernel to use it to send multicast packets + rc = setsockopt (s_, IPPROTO_IPV6, IPV6_MULTICAST_IF, + reinterpret_cast (&bind_if), + sizeof (bind_if)); + } + } else { + struct in_addr bind_addr = addr_->bind_addr ()->ipv4.sin_addr; + + if (bind_addr.s_addr != INADDR_ANY) { + rc = setsockopt (s_, IPPROTO_IP, IP_MULTICAST_IF, + reinterpret_cast (&bind_addr), + sizeof (bind_addr)); + } + } + + assert_success_or_recoverable (s_, rc); + return rc; +} + +int zmq::udp_engine_t::set_udp_reuse_address (fd_t s_, bool on_) +{ + int on = on_ ? 1 : 0; + const int rc = setsockopt (s_, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast (&on), sizeof (on)); + assert_success_or_recoverable (s_, rc); + return rc; +} + +int zmq::udp_engine_t::set_udp_reuse_port (fd_t s_, bool on_) +{ +#ifndef SO_REUSEPORT + return 0; +#else + int on = on_ ? 1 : 0; + int rc = setsockopt (s_, SOL_SOCKET, SO_REUSEPORT, + reinterpret_cast (&on), sizeof (on)); + assert_success_or_recoverable (s_, rc); + return rc; +#endif +} + +int zmq::udp_engine_t::add_membership (fd_t s_, const udp_address_t *addr_) +{ + const ip_addr_t *mcast_addr = addr_->target_addr (); + int rc = 0; + + if (mcast_addr->family () == AF_INET) { + struct ip_mreq mreq; + mreq.imr_multiaddr = mcast_addr->ipv4.sin_addr; + mreq.imr_interface = addr_->bind_addr ()->ipv4.sin_addr; + + rc = setsockopt (s_, IPPROTO_IP, IP_ADD_MEMBERSHIP, + reinterpret_cast (&mreq), sizeof (mreq)); + + } else if (mcast_addr->family () == AF_INET6) { + struct ipv6_mreq mreq; + const int iface = addr_->bind_if (); + + zmq_assert (iface >= -1); + + mreq.ipv6mr_multiaddr = mcast_addr->ipv6.sin6_addr; + mreq.ipv6mr_interface = iface; + + rc = setsockopt (s_, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + reinterpret_cast (&mreq), sizeof (mreq)); + } + + assert_success_or_recoverable (s_, rc); + return rc; +} + +void zmq::udp_engine_t::error (error_reason_t reason_) +{ + zmq_assert (_session); + _session->engine_error (false, reason_); + terminate (); +} + +void zmq::udp_engine_t::terminate () +{ + zmq_assert (_plugged); + _plugged = false; + + rm_fd (_handle); + + // Disconnect from I/O threads poller object. + io_object_t::unplug (); + + delete this; +} + +void zmq::udp_engine_t::sockaddr_to_msg (zmq::msg_t *msg_, + const sockaddr_in *addr_) +{ + const char *const name = inet_ntoa (addr_->sin_addr); + + char port[6]; + const int port_len = + sprintf (port, "%d", static_cast (ntohs (addr_->sin_port))); + zmq_assert (port_len > 0); + + const size_t name_len = strlen (name); + const int size = static_cast (name_len) + 1 /* colon */ + + port_len + 1; // terminating NUL + const int rc = msg_->init_size (size); + errno_assert (rc == 0); + msg_->set_flags (msg_t::more); + + // use memcpy instead of strcpy/strcat, since this is more efficient when + // we already know the lengths, which we calculated above + char *address = static_cast (msg_->data ()); + memcpy (address, name, name_len); + address += name_len; + *address++ = ':'; + memcpy (address, port, static_cast (port_len)); + address += port_len; + *address = 0; +} + +int zmq::udp_engine_t::resolve_raw_address (const char *name_, size_t length_) +{ + memset (&_raw_address, 0, sizeof _raw_address); + + const char *delimiter = NULL; + + // Find delimiter, cannot use memrchr as it is not supported on windows + if (length_ != 0) { + int chars_left = static_cast (length_); + const char *current_char = name_ + length_; + do { + if (*(--current_char) == ':') { + delimiter = current_char; + break; + } + } while (--chars_left != 0); + } + + if (!delimiter) { + errno = EINVAL; + return -1; + } + + const std::string addr_str (name_, delimiter - name_); + const std::string port_str (delimiter + 1, name_ + length_ - delimiter - 1); + + // Parse the port number (0 is not a valid port). + const uint16_t port = static_cast (atoi (port_str.c_str ())); + if (port == 0) { + errno = EINVAL; + return -1; + } + + _raw_address.sin_family = AF_INET; + _raw_address.sin_port = htons (port); + _raw_address.sin_addr.s_addr = inet_addr (addr_str.c_str ()); + + if (_raw_address.sin_addr.s_addr == INADDR_NONE) { + errno = EINVAL; + return -1; + } + + return 0; +} + +void zmq::udp_engine_t::out_event () +{ + msg_t group_msg; + int rc = _session->pull_msg (&group_msg); + errno_assert (rc == 0 || (rc == -1 && errno == EAGAIN)); + + if (rc == 0) { + msg_t body_msg; + rc = _session->pull_msg (&body_msg); + // If there's a group, there should also be a body + errno_assert (rc == 0); + + const size_t group_size = group_msg.size (); + const size_t body_size = body_msg.size (); + size_t size; + + if (_options.raw_socket) { + rc = resolve_raw_address (static_cast (group_msg.data ()), + group_size); + + // We discard the message if address is not valid + if (rc != 0) { + rc = group_msg.close (); + errno_assert (rc == 0); + + rc = body_msg.close (); + errno_assert (rc == 0); + + return; + } + + size = body_size; + + memcpy (_out_buffer, body_msg.data (), body_size); + } else { + size = group_size + body_size + 1; + + // TODO: check if larger than maximum size + _out_buffer[0] = static_cast (group_size); + memcpy (_out_buffer + 1, group_msg.data (), group_size); + memcpy (_out_buffer + 1 + group_size, body_msg.data (), body_size); + } + + rc = group_msg.close (); + errno_assert (rc == 0); + + body_msg.close (); + errno_assert (rc == 0); + +#ifdef ZMQ_HAVE_WINDOWS + rc = sendto (_fd, _out_buffer, static_cast (size), 0, _out_address, + _out_address_len); +#elif defined ZMQ_HAVE_VXWORKS + rc = sendto (_fd, reinterpret_cast (_out_buffer), size, 0, + (sockaddr *) _out_address, _out_address_len); +#else + rc = sendto (_fd, _out_buffer, size, 0, _out_address, _out_address_len); +#endif + if (rc < 0) { +#ifdef ZMQ_HAVE_WINDOWS + if (WSAGetLastError () != WSAEWOULDBLOCK) { + assert_success_or_recoverable (_fd, rc); + error (connection_error); + } +#else + if (rc != EWOULDBLOCK) { + assert_success_or_recoverable (_fd, rc); + error (connection_error); + } +#endif + } + } else { + reset_pollout (_handle); + } +} + +const zmq::endpoint_uri_pair_t &zmq::udp_engine_t::get_endpoint () const +{ + return _empty_endpoint; +} + +void zmq::udp_engine_t::restart_output () +{ + // If we don't support send we just drop all messages + if (!_send_enabled) { + msg_t msg; + while (_session->pull_msg (&msg) == 0) + msg.close (); + } else { + set_pollout (_handle); + out_event (); + } +} + +void zmq::udp_engine_t::in_event () +{ + sockaddr_storage in_address; + zmq_socklen_t in_addrlen = + static_cast (sizeof (sockaddr_storage)); + + const int nbytes = + recvfrom (_fd, _in_buffer, MAX_UDP_MSG, 0, + reinterpret_cast (&in_address), &in_addrlen); + + if (nbytes < 0) { +#ifdef ZMQ_HAVE_WINDOWS + if (WSAGetLastError () != WSAEWOULDBLOCK) { + assert_success_or_recoverable (_fd, nbytes); + error (connection_error); + } +#else + if (nbytes != EWOULDBLOCK) { + assert_success_or_recoverable (_fd, nbytes); + error (connection_error); + } +#endif + return; + } + + int rc; + int body_size; + int body_offset; + msg_t msg; + + if (_options.raw_socket) { + zmq_assert (in_address.ss_family == AF_INET); + sockaddr_to_msg (&msg, reinterpret_cast (&in_address)); + + body_size = nbytes; + body_offset = 0; + } else { + // TODO in out_event, the group size is an *unsigned* char. what is + // the maximum value? + const char *group_buffer = _in_buffer + 1; + const int group_size = _in_buffer[0]; + + rc = msg.init_size (group_size); + errno_assert (rc == 0); + msg.set_flags (msg_t::more); + memcpy (msg.data (), group_buffer, group_size); + + // This doesn't fit, just ingore + if (nbytes - 1 < group_size) + return; + + body_size = nbytes - 1 - group_size; + body_offset = 1 + group_size; + } + // Push group description to session + rc = _session->push_msg (&msg); + errno_assert (rc == 0 || (rc == -1 && errno == EAGAIN)); + + // Group description message doesn't fit in the pipe, drop + if (rc != 0) { + rc = msg.close (); + errno_assert (rc == 0); + + reset_pollin (_handle); + return; + } + + rc = msg.close (); + errno_assert (rc == 0); + rc = msg.init_size (body_size); + errno_assert (rc == 0); + memcpy (msg.data (), _in_buffer + body_offset, body_size); + + // Push message body to session + rc = _session->push_msg (&msg); + // Message body doesn't fit in the pipe, drop and reset session state + if (rc != 0) { + rc = msg.close (); + errno_assert (rc == 0); + + _session->reset (); + reset_pollin (_handle); + return; + } + + rc = msg.close (); + errno_assert (rc == 0); + _session->flush (); +} + +bool zmq::udp_engine_t::restart_input () +{ + if (_recv_enabled) { + set_pollin (_handle); + in_event (); + } + + return true; +} diff --git a/3rd/libzmq/src/udp_engine.hpp b/3rd/libzmq/src/udp_engine.hpp new file mode 100644 index 00000000..4208181e --- /dev/null +++ b/3rd/libzmq/src/udp_engine.hpp @@ -0,0 +1,92 @@ + +#ifndef __ZMQ_UDP_ENGINE_HPP_INCLUDED__ +#define __ZMQ_UDP_ENGINE_HPP_INCLUDED__ + +#include "io_object.hpp" +#include "i_engine.hpp" +#include "address.hpp" +#include "msg.hpp" + +#define MAX_UDP_MSG 8192 + +namespace zmq +{ +class io_thread_t; +class session_base_t; + +class udp_engine_t ZMQ_FINAL : public io_object_t, public i_engine +{ + public: + udp_engine_t (const options_t &options_); + ~udp_engine_t (); + + int init (address_t *address_, bool send_, bool recv_); + + bool has_handshake_stage () ZMQ_FINAL { return false; }; + + // i_engine interface implementation. + // Plug the engine to the session. + void plug (zmq::io_thread_t *io_thread_, class session_base_t *session_); + + // Terminate and deallocate the engine. Note that 'detached' + // events are not fired on termination. + void terminate (); + + // This method is called by the session to signalise that more + // messages can be written to the pipe. + bool restart_input (); + + // This method is called by the session to signalise that there + // are messages to send available. + void restart_output (); + + void zap_msg_available (){}; + + void in_event (); + void out_event (); + + const endpoint_uri_pair_t &get_endpoint () const; + + private: + int resolve_raw_address (const char *name_, size_t length_); + static void sockaddr_to_msg (zmq::msg_t *msg_, const sockaddr_in *addr_); + + static int set_udp_reuse_address (fd_t s_, bool on_); + static int set_udp_reuse_port (fd_t s_, bool on_); + // Indicate, if the multicast data being sent should be looped back + static int set_udp_multicast_loop (fd_t s_, bool is_ipv6_, bool loop_); + // Set multicast TTL + static int set_udp_multicast_ttl (fd_t s_, bool is_ipv6_, int hops_); + // Set multicast address/interface + int set_udp_multicast_iface (fd_t s_, + bool is_ipv6_, + const udp_address_t *addr_); + // Join a multicast group + int add_membership (fd_t s_, const udp_address_t *addr_); + + // Function to handle network issues. + void error (error_reason_t reason_); + + const endpoint_uri_pair_t _empty_endpoint; + + bool _plugged; + + fd_t _fd; + session_base_t *_session; + handle_t _handle; + address_t *_address; + + options_t _options; + + sockaddr_in _raw_address; + const struct sockaddr *_out_address; + zmq_socklen_t _out_address_len; + + char _out_buffer[MAX_UDP_MSG]; + char _in_buffer[MAX_UDP_MSG]; + bool _send_enabled; + bool _recv_enabled; +}; +} + +#endif diff --git a/3rd/libzmq/src/v1_decoder.cpp b/3rd/libzmq/src/v1_decoder.cpp new file mode 100644 index 00000000..e988ed72 --- /dev/null +++ b/3rd/libzmq/src/v1_decoder.cpp @@ -0,0 +1,156 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include +#include +#include + +#include "decoder.hpp" +#include "v1_decoder.hpp" +#include "likely.hpp" +#include "wire.hpp" +#include "err.hpp" + +zmq::v1_decoder_t::v1_decoder_t (size_t bufsize_, int64_t maxmsgsize_) : + decoder_base_t (bufsize_), + _max_msg_size (maxmsgsize_) +{ + int rc = _in_progress.init (); + errno_assert (rc == 0); + + // At the beginning, read one byte and go to one_byte_size_ready state. + next_step (_tmpbuf, 1, &v1_decoder_t::one_byte_size_ready); +} + +zmq::v1_decoder_t::~v1_decoder_t () +{ + const int rc = _in_progress.close (); + errno_assert (rc == 0); +} + +int zmq::v1_decoder_t::one_byte_size_ready (unsigned char const *) +{ + // First byte of size is read. If it is UCHAR_MAX (0xff) read 8-byte size. + // Otherwise allocate the buffer for message data and read the + // message data into it. + if (*_tmpbuf == UCHAR_MAX) + next_step (_tmpbuf, 8, &v1_decoder_t::eight_byte_size_ready); + else { + // There has to be at least one byte (the flags) in the message). + if (!*_tmpbuf) { + errno = EPROTO; + return -1; + } + + if (_max_msg_size >= 0 + && static_cast (*_tmpbuf - 1) > _max_msg_size) { + errno = EMSGSIZE; + return -1; + } + + int rc = _in_progress.close (); + assert (rc == 0); + rc = _in_progress.init_size (*_tmpbuf - 1); + if (rc != 0) { + errno_assert (errno == ENOMEM); + rc = _in_progress.init (); + errno_assert (rc == 0); + errno = ENOMEM; + return -1; + } + + next_step (_tmpbuf, 1, &v1_decoder_t::flags_ready); + } + return 0; +} + +int zmq::v1_decoder_t::eight_byte_size_ready (unsigned char const *) +{ + // 8-byte payload length is read. Allocate the buffer + // for message body and read the message data into it. + const uint64_t payload_length = get_uint64 (_tmpbuf); + + // There has to be at least one byte (the flags) in the message). + if (payload_length == 0) { + errno = EPROTO; + return -1; + } + + // Message size must not exceed the maximum allowed size. + if (_max_msg_size >= 0 + && payload_length - 1 > static_cast (_max_msg_size)) { + errno = EMSGSIZE; + return -1; + } + +#ifndef __aarch64__ + // Message size must fit within range of size_t data type. + if (payload_length - 1 > std::numeric_limits::max ()) { + errno = EMSGSIZE; + return -1; + } +#endif + + const size_t msg_size = static_cast (payload_length - 1); + + int rc = _in_progress.close (); + assert (rc == 0); + rc = _in_progress.init_size (msg_size); + if (rc != 0) { + errno_assert (errno == ENOMEM); + rc = _in_progress.init (); + errno_assert (rc == 0); + errno = ENOMEM; + return -1; + } + + next_step (_tmpbuf, 1, &v1_decoder_t::flags_ready); + return 0; +} + +int zmq::v1_decoder_t::flags_ready (unsigned char const *) +{ + // Store the flags from the wire into the message structure. + _in_progress.set_flags (_tmpbuf[0] & msg_t::more); + + next_step (_in_progress.data (), _in_progress.size (), + &v1_decoder_t::message_ready); + + return 0; +} + +int zmq::v1_decoder_t::message_ready (unsigned char const *) +{ + // Message is completely read. Push it further and start reading + // new message. (in_progress is a 0-byte message after this point.) + next_step (_tmpbuf, 1, &v1_decoder_t::one_byte_size_ready); + return 1; +} diff --git a/3rd/libzmq/src/v1_decoder.hpp b/3rd/libzmq/src/v1_decoder.hpp new file mode 100644 index 00000000..ccf63fc0 --- /dev/null +++ b/3rd/libzmq/src/v1_decoder.hpp @@ -0,0 +1,62 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_V1_DECODER_HPP_INCLUDED__ +#define __ZMQ_V1_DECODER_HPP_INCLUDED__ + +#include "decoder.hpp" + +namespace zmq +{ +// Decoder for ZMTP/1.0 protocol. Converts data batches into messages. + +class v1_decoder_t ZMQ_FINAL : public decoder_base_t +{ + public: + v1_decoder_t (size_t bufsize_, int64_t maxmsgsize_); + ~v1_decoder_t (); + + msg_t *msg () { return &_in_progress; } + + private: + int one_byte_size_ready (unsigned char const *); + int eight_byte_size_ready (unsigned char const *); + int flags_ready (unsigned char const *); + int message_ready (unsigned char const *); + + unsigned char _tmpbuf[8]; + msg_t _in_progress; + + const int64_t _max_msg_size; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (v1_decoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/v1_encoder.cpp b/3rd/libzmq/src/v1_encoder.cpp new file mode 100644 index 00000000..38e7e982 --- /dev/null +++ b/3rd/libzmq/src/v1_encoder.cpp @@ -0,0 +1,95 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "encoder.hpp" +#include "v1_encoder.hpp" +#include "msg.hpp" +#include "wire.hpp" + +#include + +zmq::v1_encoder_t::v1_encoder_t (size_t bufsize_) : + encoder_base_t (bufsize_) +{ + // Write 0 bytes to the batch and go to message_ready state. + next_step (NULL, 0, &v1_encoder_t::message_ready, true); +} + +zmq::v1_encoder_t::~v1_encoder_t () +{ +} + +void zmq::v1_encoder_t::size_ready () +{ + // Write message body into the buffer. + next_step (in_progress ()->data (), in_progress ()->size (), + &v1_encoder_t::message_ready, true); +} + +void zmq::v1_encoder_t::message_ready () +{ + size_t header_size = 2; // flags byte + size byte + // Get the message size. + size_t size = in_progress ()->size (); + + // Account for the 'flags' byte. + size++; + + // Account for the subscribe/cancel byte. + if (in_progress ()->is_subscribe () || in_progress ()->is_cancel ()) + size++; + + // For messages less than 255 bytes long, write one byte of message size. + // For longer messages write 0xff escape character followed by 8-byte + // message size. In both cases 'flags' field follows. + if (size < UCHAR_MAX) { + _tmpbuf[0] = static_cast (size); + _tmpbuf[1] = (in_progress ()->flags () & msg_t::more); + } else { + _tmpbuf[0] = UCHAR_MAX; + put_uint64 (_tmpbuf + 1, size); + _tmpbuf[9] = (in_progress ()->flags () & msg_t::more); + header_size = 10; + } + + // Encode the subscribe/cancel byte. This is done in the encoder as + // opposed to when the subscribe message is created to allow different + // protocol behaviour on the wire in the v3.1 and legacy encoders. + // It results in the work being done multiple times in case the sub + // is sending the subscription/cancel to multiple pubs, but it cannot + // be avoided. This processing can be moved to xsub once support for + // ZMTP < 3.1 is dropped. + if (in_progress ()->is_subscribe ()) + _tmpbuf[header_size++] = 1; + else if (in_progress ()->is_cancel ()) + _tmpbuf[header_size++] = 0; + + next_step (_tmpbuf, header_size, &v1_encoder_t::size_ready, false); +} diff --git a/3rd/libzmq/src/v1_encoder.hpp b/3rd/libzmq/src/v1_encoder.hpp new file mode 100644 index 00000000..3f7a57e3 --- /dev/null +++ b/3rd/libzmq/src/v1_encoder.hpp @@ -0,0 +1,55 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_V1_ENCODER_HPP_INCLUDED__ +#define __ZMQ_V1_ENCODER_HPP_INCLUDED__ + +#include "encoder.hpp" + +namespace zmq +{ +// Encoder for ZMTP/1.0 protocol. Converts messages into data batches. + +class v1_encoder_t ZMQ_FINAL : public encoder_base_t +{ + public: + v1_encoder_t (size_t bufsize_); + ~v1_encoder_t (); + + private: + void size_ready (); + void message_ready (); + + unsigned char _tmpbuf[11]; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (v1_encoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/v2_decoder.cpp b/3rd/libzmq/src/v2_decoder.cpp new file mode 100644 index 00000000..5848d6a0 --- /dev/null +++ b/3rd/libzmq/src/v2_decoder.cpp @@ -0,0 +1,167 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include +#include + +#include "v2_protocol.hpp" +#include "v2_decoder.hpp" +#include "likely.hpp" +#include "wire.hpp" +#include "err.hpp" + +zmq::v2_decoder_t::v2_decoder_t (size_t bufsize_, + int64_t maxmsgsize_, + bool zero_copy_) : + decoder_base_t (bufsize_), + _msg_flags (0), + _zero_copy (zero_copy_), + _max_msg_size (maxmsgsize_) +{ + int rc = _in_progress.init (); + errno_assert (rc == 0); + + // At the beginning, read one byte and go to flags_ready state. + next_step (_tmpbuf, 1, &v2_decoder_t::flags_ready); +} + +zmq::v2_decoder_t::~v2_decoder_t () +{ + const int rc = _in_progress.close (); + errno_assert (rc == 0); +} + +int zmq::v2_decoder_t::flags_ready (unsigned char const *) +{ + _msg_flags = 0; + if (_tmpbuf[0] & v2_protocol_t::more_flag) + _msg_flags |= msg_t::more; + if (_tmpbuf[0] & v2_protocol_t::command_flag) + _msg_flags |= msg_t::command; + + // The payload length is either one or eight bytes, + // depending on whether the 'large' bit is set. + if (_tmpbuf[0] & v2_protocol_t::large_flag) + next_step (_tmpbuf, 8, &v2_decoder_t::eight_byte_size_ready); + else + next_step (_tmpbuf, 1, &v2_decoder_t::one_byte_size_ready); + + return 0; +} + +int zmq::v2_decoder_t::one_byte_size_ready (unsigned char const *read_from_) +{ + return size_ready (_tmpbuf[0], read_from_); +} + +int zmq::v2_decoder_t::eight_byte_size_ready (unsigned char const *read_from_) +{ + // The payload size is encoded as 64-bit unsigned integer. + // The most significant byte comes first. + const uint64_t msg_size = get_uint64 (_tmpbuf); + + return size_ready (msg_size, read_from_); +} + +int zmq::v2_decoder_t::size_ready (uint64_t msg_size_, + unsigned char const *read_pos_) +{ + // Message size must not exceed the maximum allowed size. + if (_max_msg_size >= 0) + if (unlikely (msg_size_ > static_cast (_max_msg_size))) { + errno = EMSGSIZE; + return -1; + } + + // Message size must fit into size_t data type. + if (unlikely (msg_size_ != static_cast (msg_size_))) { + errno = EMSGSIZE; + return -1; + } + + int rc = _in_progress.close (); + assert (rc == 0); + + // the current message can exceed the current buffer. We have to copy the buffer + // data into a new message and complete it in the next receive. + + shared_message_memory_allocator &allocator = get_allocator (); + if (unlikely (!_zero_copy + || msg_size_ > static_cast ( + allocator.data () + allocator.size () - read_pos_))) { + // a new message has started, but the size would exceed the pre-allocated arena + // this happens every time when a message does not fit completely into the buffer + rc = _in_progress.init_size (static_cast (msg_size_)); + } else { + // construct message using n bytes from the buffer as storage + // increase buffer ref count + // if the message will be a large message, pass a valid refcnt memory location as well + rc = + _in_progress.init (const_cast (read_pos_), + static_cast (msg_size_), + shared_message_memory_allocator::call_dec_ref, + allocator.buffer (), allocator.provide_content ()); + + // For small messages, data has been copied and refcount does not have to be increased + if (_in_progress.is_zcmsg ()) { + allocator.advance_content (); + allocator.inc_ref (); + } + } + + if (unlikely (rc)) { + errno_assert (errno == ENOMEM); + rc = _in_progress.init (); + errno_assert (rc == 0); + errno = ENOMEM; + return -1; + } + + _in_progress.set_flags (_msg_flags); + // this sets read_pos to + // the message data address if the data needs to be copied + // for small message / messages exceeding the current buffer + // or + // to the current start address in the buffer because the message + // was constructed to use n bytes from the address passed as argument + next_step (_in_progress.data (), _in_progress.size (), + &v2_decoder_t::message_ready); + + return 0; +} + +int zmq::v2_decoder_t::message_ready (unsigned char const *) +{ + // Message is completely read. Signal this to the caller + // and prepare to decode next message. + next_step (_tmpbuf, 1, &v2_decoder_t::flags_ready); + return 1; +} diff --git a/3rd/libzmq/src/v2_decoder.hpp b/3rd/libzmq/src/v2_decoder.hpp new file mode 100644 index 00000000..4c5d135d --- /dev/null +++ b/3rd/libzmq/src/v2_decoder.hpp @@ -0,0 +1,70 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_V2_DECODER_HPP_INCLUDED__ +#define __ZMQ_V2_DECODER_HPP_INCLUDED__ + +#include "decoder.hpp" +#include "decoder_allocators.hpp" + +namespace zmq +{ +// Decoder for ZMTP/2.x framing protocol. Converts data stream into messages. +// The class has to inherit from shared_message_memory_allocator because +// the base class calls allocate in its constructor. +class v2_decoder_t ZMQ_FINAL + : public decoder_base_t +{ + public: + v2_decoder_t (size_t bufsize_, int64_t maxmsgsize_, bool zero_copy_); + ~v2_decoder_t (); + + // i_decoder interface. + msg_t *msg () { return &_in_progress; } + + private: + int flags_ready (unsigned char const *); + int one_byte_size_ready (unsigned char const *); + int eight_byte_size_ready (unsigned char const *); + int message_ready (unsigned char const *); + + int size_ready (uint64_t size_, unsigned char const *); + + unsigned char _tmpbuf[8]; + unsigned char _msg_flags; + msg_t _in_progress; + + const bool _zero_copy; + const int64_t _max_msg_size; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (v2_decoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/v2_encoder.cpp b/3rd/libzmq/src/v2_encoder.cpp new file mode 100644 index 00000000..f331adee --- /dev/null +++ b/3rd/libzmq/src/v2_encoder.cpp @@ -0,0 +1,96 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "v2_protocol.hpp" +#include "v2_encoder.hpp" +#include "msg.hpp" +#include "likely.hpp" +#include "wire.hpp" + +#include + +zmq::v2_encoder_t::v2_encoder_t (size_t bufsize_) : + encoder_base_t (bufsize_) +{ + // Write 0 bytes to the batch and go to message_ready state. + next_step (NULL, 0, &v2_encoder_t::message_ready, true); +} + +zmq::v2_encoder_t::~v2_encoder_t () +{ +} + +void zmq::v2_encoder_t::message_ready () +{ + // Encode flags. + size_t size = in_progress ()->size (); + size_t header_size = 2; // flags byte + size byte + unsigned char &protocol_flags = _tmp_buf[0]; + protocol_flags = 0; + if (in_progress ()->flags () & msg_t::more) + protocol_flags |= v2_protocol_t::more_flag; + if (in_progress ()->size () > UCHAR_MAX) + protocol_flags |= v2_protocol_t::large_flag; + if (in_progress ()->flags () & msg_t::command) + protocol_flags |= v2_protocol_t::command_flag; + if (in_progress ()->is_subscribe () || in_progress ()->is_cancel ()) + ++size; + + // Encode the message length. For messages less then 256 bytes, + // the length is encoded as 8-bit unsigned integer. For larger + // messages, 64-bit unsigned integer in network byte order is used. + if (unlikely (size > UCHAR_MAX)) { + put_uint64 (_tmp_buf + 1, size); + header_size = 9; // flags byte + size 8 bytes + } else { + _tmp_buf[1] = static_cast (size); + } + + // Encode the subscribe/cancel byte. This is done in the encoder as + // opposed to when the subscribe message is created to allow different + // protocol behaviour on the wire in the v3.1 and legacy encoders. + // It results in the work being done multiple times in case the sub + // is sending the subscription/cancel to multiple pubs, but it cannot + // be avoided. This processing can be moved to xsub once support for + // ZMTP < 3.1 is dropped. + if (in_progress ()->is_subscribe ()) + _tmp_buf[header_size++] = 1; + else if (in_progress ()->is_cancel ()) + _tmp_buf[header_size++] = 0; + + next_step (_tmp_buf, header_size, &v2_encoder_t::size_ready, false); +} + +void zmq::v2_encoder_t::size_ready () +{ + // Write message body into the buffer. + next_step (in_progress ()->data (), in_progress ()->size (), + &v2_encoder_t::message_ready, true); +} diff --git a/3rd/libzmq/src/v2_encoder.hpp b/3rd/libzmq/src/v2_encoder.hpp new file mode 100644 index 00000000..4f60616f --- /dev/null +++ b/3rd/libzmq/src/v2_encoder.hpp @@ -0,0 +1,56 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_V2_ENCODER_HPP_INCLUDED__ +#define __ZMQ_V2_ENCODER_HPP_INCLUDED__ + +#include "encoder.hpp" + +namespace zmq +{ +// Encoder for 0MQ framing protocol. Converts messages into data stream. + +class v2_encoder_t ZMQ_FINAL : public encoder_base_t +{ + public: + v2_encoder_t (size_t bufsize_); + ~v2_encoder_t (); + + private: + void size_ready (); + void message_ready (); + + // flags byte + size byte (or 8 bytes) + sub/cancel byte + unsigned char _tmp_buf[10]; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (v2_encoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/v2_protocol.hpp b/3rd/libzmq/src/v2_protocol.hpp new file mode 100644 index 00000000..f1297295 --- /dev/null +++ b/3rd/libzmq/src/v2_protocol.hpp @@ -0,0 +1,49 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_V2_PROTOCOL_HPP_INCLUDED__ +#define __ZMQ_V2_PROTOCOL_HPP_INCLUDED__ + +namespace zmq +{ +// Definition of constants for ZMTP/2.0 transport protocol. +class v2_protocol_t +{ + public: + // Message flags. + enum + { + more_flag = 1, + large_flag = 2, + command_flag = 4 + }; +}; +} + +#endif diff --git a/3rd/libzmq/src/v3_1_encoder.cpp b/3rd/libzmq/src/v3_1_encoder.cpp new file mode 100644 index 00000000..f9e11dfa --- /dev/null +++ b/3rd/libzmq/src/v3_1_encoder.cpp @@ -0,0 +1,105 @@ +/* + Copyright (c) 2020 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 . +*/ + +#include "precompiled.hpp" +#include "v2_protocol.hpp" +#include "v3_1_encoder.hpp" +#include "msg.hpp" +#include "likely.hpp" +#include "wire.hpp" + +#include + +zmq::v3_1_encoder_t::v3_1_encoder_t (size_t bufsize_) : + encoder_base_t (bufsize_) +{ + // Write 0 bytes to the batch and go to message_ready state. + next_step (NULL, 0, &v3_1_encoder_t::message_ready, true); +} + +zmq::v3_1_encoder_t::~v3_1_encoder_t () +{ +} + +void zmq::v3_1_encoder_t::message_ready () +{ + // Encode flags. + size_t size = in_progress ()->size (); + size_t header_size = 2; // flags byte + size byte + unsigned char &protocol_flags = _tmp_buf[0]; + protocol_flags = 0; + if (in_progress ()->flags () & msg_t::more) + protocol_flags |= v2_protocol_t::more_flag; + if (in_progress ()->size () > UCHAR_MAX) + protocol_flags |= v2_protocol_t::large_flag; + if (in_progress ()->flags () & msg_t::command + || in_progress ()->is_subscribe () || in_progress ()->is_cancel ()) { + protocol_flags |= v2_protocol_t::command_flag; + if (in_progress ()->is_subscribe ()) + size += zmq::msg_t::sub_cmd_name_size; + else if (in_progress ()->is_cancel ()) + size += zmq::msg_t::cancel_cmd_name_size; + } + + // Encode the message length. For messages less then 256 bytes, + // the length is encoded as 8-bit unsigned integer. For larger + // messages, 64-bit unsigned integer in network byte order is used. + if (unlikely (size > UCHAR_MAX)) { + put_uint64 (_tmp_buf + 1, size); + header_size = 9; // flags byte + size 8 bytes + } else { + _tmp_buf[1] = static_cast (size); + } + + // Encode the sub/cancel command string. This is done in the encoder as + // opposed to when the subscribe message is created to allow different + // protocol behaviour on the wire in the v3.1 and legacy encoders. + // It results in the work being done multiple times in case the sub + // is sending the subscription/cancel to multiple pubs, but it cannot + // be avoided. This processing can be moved to xsub once support for + // ZMTP < 3.1 is dropped. + if (in_progress ()->is_subscribe ()) { + memcpy (_tmp_buf + header_size, zmq::sub_cmd_name, + zmq::msg_t::sub_cmd_name_size); + header_size += zmq::msg_t::sub_cmd_name_size; + } else if (in_progress ()->is_cancel ()) { + memcpy (_tmp_buf + header_size, zmq::cancel_cmd_name, + zmq::msg_t::cancel_cmd_name_size); + header_size += zmq::msg_t::cancel_cmd_name_size; + } + + next_step (_tmp_buf, header_size, &v3_1_encoder_t::size_ready, false); +} + +void zmq::v3_1_encoder_t::size_ready () +{ + // Write message body into the buffer. + next_step (in_progress ()->data (), in_progress ()->size (), + &v3_1_encoder_t::message_ready, true); +} diff --git a/3rd/libzmq/src/v3_1_encoder.hpp b/3rd/libzmq/src/v3_1_encoder.hpp new file mode 100644 index 00000000..d5d9c136 --- /dev/null +++ b/3rd/libzmq/src/v3_1_encoder.hpp @@ -0,0 +1,56 @@ +/* + Copyright (c) 2020 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 . +*/ + +#ifndef __ZMQ_V3_1_ENCODER_HPP_INCLUDED__ +#define __ZMQ_V3_1_ENCODER_HPP_INCLUDED__ + +#include "encoder.hpp" +#include "msg.hpp" + +namespace zmq +{ +// Encoder for 0MQ framing protocol. Converts messages into data stream. + +class v3_1_encoder_t ZMQ_FINAL : public encoder_base_t +{ + public: + v3_1_encoder_t (size_t bufsize_); + ~v3_1_encoder_t () ZMQ_FINAL; + + private: + void size_ready (); + void message_ready (); + + unsigned char _tmp_buf[9 + zmq::msg_t::sub_cmd_name_size]; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (v3_1_encoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/version.rc.in b/3rd/libzmq/src/version.rc.in new file mode 100644 index 00000000..19806002 --- /dev/null +++ b/3rd/libzmq/src/version.rc.in @@ -0,0 +1,93 @@ +///////////////////////////////////////////////////////////////////////////// +// +// VERSIONINFO resource +// +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx + +// @MAJOR@,@MINOR@,@BUILD@,@PATCH@ +#define VER_FILEVERSION @ZMQ_VERSION_MAJOR@,@ZMQ_VERSION_MINOR@,@ZMQ_VERSION_PATCH@,0 +#define VER_FILEVERSION_STR "@ZMQ_VERSION_MAJOR@.@ZMQ_VERSION_MINOR@.@ZMQ_VERSION_PATCH@.0\0" + +#define VER_PRODUCTVERSION VER_FILEVERSION +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + + +// versionID +// Version-information resource identifier. This value must be 1. +1 VERSIONINFO + +//// fixed-info +// Binary version number for the file. + FILEVERSION VER_FILEVERSION + +// Binary version number for the product with which the file is distributed. + PRODUCTVERSION VER_PRODUCTVERSION + +// Bits in the FILEFLAGS statement are valid. + FILEFLAGSMASK 0x17L + +// Attributes of the file. +// VS_FF_DEBUG = 1 : File contains debugging information or is compiled with debugging features enabled. +// VS_FF_PATCHED = 4 : File has been modified and is not identical to the original shipping file of the +// same version number. +// VS_FF_PRERELEASE = 2 : File is a development version, not a commercially released product. +// VS_FF_PRIVATEBUILD = 8 : File was not built using standard release procedures. +// VS_FF_SPECIALBUILD = 20 : File was built by the original company using standard release procedures but is a +// : variation of the standard file of the same version number. +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x2L +#endif + +// Operating system for which this file was designed. +// VOS_DOS = 0x10000 : File was designed for MS-DOS. +// VOS_NT = 0x40000 : File was designed for 32-bit Windows. +// VOS_WINDOWS16 = 0x1 : File was designed for 16-bit Windows. +// VOS_WINDOWS32 = 0x4 : File was designed for 32-bit Windows. +// VOS_DOS_WINDOWS16 = 0x10001 : File was designed for 16-bit Windows running with MS-DOS. +// VOS_DOS_WINDOWS32 = 0x10004 : File was designed for 32-bit Windows running with MS-DOS. +// VOS_NT_WINDOWS32 = 0x40004 : File was designed for 32-bit Windows. +// NB: appears obsolete, nothing for x64. + FILEOS 0x4L + +// General type of file. +// VFT_APP = 0x1 : File contains an application. +// VFT_DLL = 0x2 : File contains a dynamic-link library (DLL). +// VFT_DRV = 0x3 : File contains a device driver. +// VFT_FONT = 0x4 : File contains a font. +// VFT_VXD = 0x5 : File contains a virtual device. +// VFT_STATIC_LIB = 0x7 : File contains a static-link library. + FILETYPE 0x2L + +// Function of the file. + FILESUBTYPE 0x0L + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "CompanyName", "iMatix Corporation" + VALUE "FileDescription", "ZeroMQ lightweight messaging kernel" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "zeromq" + VALUE "LegalCopyright", "Copyright (c) 2012 The ZeroMQ Authors." + VALUE "OriginalFilename", "libzmq.dll" + VALUE "ProductName", "ZeroMQ" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN +// langID, one of the following language codes. +// 0x409 : U.S. English +// 0x809 : U.K. English +// charsetID, one of the following character-set identifiers. +// 1200 : Unicode + VALUE "Translation", 0x809, 1200 + END +END + +// end of file. diff --git a/3rd/libzmq/src/vmci.cpp b/3rd/libzmq/src/vmci.cpp new file mode 100644 index 00000000..4edd096b --- /dev/null +++ b/3rd/libzmq/src/vmci.cpp @@ -0,0 +1,100 @@ +/* + 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 . +*/ +#include "precompiled.hpp" + +#include "vmci.hpp" + +#if defined ZMQ_HAVE_VMCI + +#include +#include + +void zmq::tune_vmci_buffer_size (ctx_t *context_, + fd_t sockfd_, + uint64_t default_size_, + uint64_t min_size_, + uint64_t max_size_) +{ + int family = context_->get_vmci_socket_family (); + assert (family != -1); + + if (default_size_ != 0) { + int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, + (char *) &default_size_, sizeof default_size_); +#if defined ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + errno_assert (rc == 0); +#endif + } + + if (min_size_ != 0) { + int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, + (char *) &min_size_, sizeof min_size_); +#if defined ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + errno_assert (rc == 0); +#endif + } + + if (max_size_ != 0) { + int rc = setsockopt (sockfd_, family, SO_VMCI_BUFFER_SIZE, + (char *) &max_size_, sizeof max_size_); +#if defined ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + errno_assert (rc == 0); +#endif + } +} + +#if defined ZMQ_HAVE_WINDOWS +void zmq::tune_vmci_connect_timeout (ctx_t *context_, + fd_t sockfd_, + DWORD timeout_) +#else +void zmq::tune_vmci_connect_timeout (ctx_t *context_, + fd_t sockfd_, + struct timeval timeout_) +#endif +{ + int family = context_->get_vmci_socket_family (); + assert (family != -1); + + int rc = setsockopt (sockfd_, family, SO_VMCI_CONNECT_TIMEOUT, + (char *) &timeout_, sizeof timeout_); +#if defined ZMQ_HAVE_WINDOWS + wsa_assert (rc != SOCKET_ERROR); +#else + errno_assert (rc == 0); +#endif +} + +#endif diff --git a/3rd/libzmq/src/vmci.hpp b/3rd/libzmq/src/vmci.hpp new file mode 100644 index 00000000..16a0c734 --- /dev/null +++ b/3rd/libzmq/src/vmci.hpp @@ -0,0 +1,66 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_VMCI_HPP_INCLUDED__ +#define __ZMQ_VMCI_HPP_INCLUDED__ + +#include + +#include "platform.hpp" +#include "fd.hpp" +#include "ctx.hpp" + +#if defined ZMQ_HAVE_VMCI + +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#endif + +namespace zmq +{ +void tune_vmci_buffer_size (ctx_t *context_, + fd_t sockfd_, + uint64_t default_size_, + uint64_t min_size_, + uint64_t max_size_); + +#if defined ZMQ_HAVE_WINDOWS +void tune_vmci_connect_timeout (ctx_t *context_, fd_t sockfd_, DWORD timeout_); +#else +void tune_vmci_connect_timeout (ctx_t *context_, + fd_t sockfd_, + struct timeval timeout_); +#endif +} + +#endif + +#endif diff --git a/3rd/libzmq/src/vmci_address.cpp b/3rd/libzmq/src/vmci_address.cpp new file mode 100644 index 00000000..cf97d99c --- /dev/null +++ b/3rd/libzmq/src/vmci_address.cpp @@ -0,0 +1,167 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "vmci_address.hpp" + +#if defined(ZMQ_HAVE_VMCI) + +#include +#include +#include + +#include "err.hpp" + +zmq::vmci_address_t::vmci_address_t (ctx_t *parent_) : parent (parent_) +{ + memset (&address, 0, sizeof address); +} + +zmq::vmci_address_t::vmci_address_t (const sockaddr *sa, + socklen_t sa_len, + ctx_t *parent_) : + parent (parent_) +{ + zmq_assert (sa && sa_len > 0); + + memset (&address, 0, sizeof address); + if (sa->sa_family == parent->get_vmci_socket_family ()) + memcpy (&address, sa, sa_len); +} + +zmq::vmci_address_t::~vmci_address_t () +{ +} + +int zmq::vmci_address_t::resolve (const char *path_) +{ + // Find the ':' at end that separates address from the port number. + const char *delimiter = strrchr (path_, ':'); + if (!delimiter) { + errno = EINVAL; + return -1; + } + + // Separate the address/port. + std::string addr_str (path_, delimiter - path_); + std::string port_str (delimiter + 1); + + unsigned int cid = VMADDR_CID_ANY; + unsigned int port = VMADDR_PORT_ANY; + + if (!addr_str.length ()) { + errno = EINVAL; + return -1; + } else if (addr_str == "@") { + cid = VMCISock_GetLocalCID (); + + if (cid == VMADDR_CID_ANY) { + errno = ENODEV; + return -1; + } + } else if (addr_str != "*" && addr_str != "-1") { + const char *begin = addr_str.c_str (); + char *end = NULL; + unsigned long l = strtoul (begin, &end, 10); + + if ((l == 0 && end == begin) || (l == ULONG_MAX && errno == ERANGE) + || l > UINT_MAX) { + errno = EINVAL; + return -1; + } + + cid = static_cast (l); + } + + if (!port_str.length ()) { + errno = EINVAL; + return -1; + } else if (port_str != "*" && port_str != "-1") { + const char *begin = port_str.c_str (); + char *end = NULL; + unsigned long l = strtoul (begin, &end, 10); + + if ((l == 0 && end == begin) || (l == ULONG_MAX && errno == ERANGE) + || l > UINT_MAX) { + errno = EINVAL; + return -1; + } + + port = static_cast (l); + } + + address.svm_family = + static_cast (parent->get_vmci_socket_family ()); + address.svm_cid = cid; + address.svm_port = port; + + return 0; +} + +int zmq::vmci_address_t::to_string (std::string &addr_) +{ + if (address.svm_family != parent->get_vmci_socket_family ()) { + addr_.clear (); + return -1; + } + + std::stringstream s; + + s << "vmci://"; + + if (address.svm_cid == VMADDR_CID_ANY) { + s << "*"; + } else { + s << address.svm_cid; + } + + s << ":"; + + if (address.svm_port == VMADDR_PORT_ANY) { + s << "*"; + } else { + s << address.svm_port; + } + + addr_ = s.str (); + return 0; +} + +const sockaddr *zmq::vmci_address_t::addr () const +{ + return reinterpret_cast (&address); +} + +socklen_t zmq::vmci_address_t::addrlen () const +{ + return static_cast (sizeof address); +} + +#endif diff --git a/3rd/libzmq/src/vmci_address.hpp b/3rd/libzmq/src/vmci_address.hpp new file mode 100644 index 00000000..16dab4a3 --- /dev/null +++ b/3rd/libzmq/src/vmci_address.hpp @@ -0,0 +1,71 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_VMCI_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_VMCI_ADDRESS_HPP_INCLUDED__ + +#include + +#include "platform.hpp" +#include "ctx.hpp" + +#if defined(ZMQ_HAVE_VMCI) +#include + +namespace zmq +{ +class vmci_address_t +{ + public: + vmci_address_t (ctx_t *parent_); + vmci_address_t (const sockaddr *sa, socklen_t sa_len, ctx_t *parent_); + ~vmci_address_t (); + + // This function sets up the address for VMCI transport. + int resolve (const char *path_); + + // The opposite to resolve() + int to_string (std::string &addr_); + + const sockaddr *addr () const; + socklen_t addrlen () const; + + private: + struct sockaddr_vm address; + ctx_t *parent; + + vmci_address_t (); + + ZMQ_NON_COPYABLE_NOR_MOVABLE (vmci_address_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/vmci_connecter.cpp b/3rd/libzmq/src/vmci_connecter.cpp new file mode 100644 index 00000000..5768c78e --- /dev/null +++ b/3rd/libzmq/src/vmci_connecter.cpp @@ -0,0 +1,302 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "vmci_connecter.hpp" + +#if defined ZMQ_HAVE_VMCI + +#include + +#include "stream_engine.hpp" +#include "io_thread.hpp" +#include "platform.hpp" +#include "random.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "address.hpp" +#include "session_base.hpp" +#include "vmci_address.hpp" +#include "vmci.hpp" + +zmq::vmci_connecter_t::vmci_connecter_t (class io_thread_t *io_thread_, + class session_base_t *session_, + const options_t &options_, + const address_t *addr_, + bool delayed_start_) : + own_t (io_thread_, options_), + io_object_t (io_thread_), + addr (addr_), + s (retired_fd), + handle_valid (false), + delayed_start (delayed_start_), + timer_started (false), + session (session_), + current_reconnect_ivl (options.reconnect_ivl) +{ + zmq_assert (addr); + zmq_assert (addr->protocol == "vmci"); + addr->to_string (endpoint); + socket = session->get_socket (); +} + +zmq::vmci_connecter_t::~vmci_connecter_t () +{ + zmq_assert (!timer_started); + zmq_assert (!handle_valid); + zmq_assert (s == retired_fd); +} + +void zmq::vmci_connecter_t::process_plug () +{ + if (delayed_start) + add_reconnect_timer (); + else + start_connecting (); +} + +void zmq::vmci_connecter_t::process_term (int linger_) +{ + if (timer_started) { + cancel_timer (reconnect_timer_id); + timer_started = false; + } + + if (handle_valid) { + rm_fd (handle); + handle_valid = false; + } + + if (s != retired_fd) + close (); + + own_t::process_term (linger_); +} + +void zmq::vmci_connecter_t::in_event () +{ + // We are not polling for incoming data, so we are actually called + // because of error here. However, we can get error on out event as well + // on some platforms, so we'll simply handle both events in the same way. + out_event (); +} + +void zmq::vmci_connecter_t::out_event () +{ + fd_t fd = connect (); + rm_fd (handle); + handle_valid = false; + + // Handle the error condition by attempt to reconnect. + if (fd == retired_fd) { + close (); + add_reconnect_timer (); + return; + } + + tune_vmci_buffer_size (this->get_ctx (), fd, options.vmci_buffer_size, + options.vmci_buffer_min_size, + options.vmci_buffer_max_size); + + if (options.vmci_connect_timeout > 0) { +#if defined ZMQ_HAVE_WINDOWS + tune_vmci_connect_timeout (this->get_ctx (), fd, + options.vmci_connect_timeout); +#else + struct timeval timeout = {0, options.vmci_connect_timeout * 1000}; + tune_vmci_connect_timeout (this->get_ctx (), fd, timeout); +#endif + } + + // Create the engine object for this connection. + stream_engine_t *engine = new (std::nothrow) stream_engine_t ( + fd, options, make_unconnected_bind_endpoint_pair (endpoint)); + alloc_assert (engine); + + // Attach the engine to the corresponding session object. + send_attach (session, engine); + + // Shut the connecter down. + terminate (); + + socket->event_connected (make_unconnected_bind_endpoint_pair (endpoint), + fd); +} + +void zmq::vmci_connecter_t::timer_event (int id_) +{ + zmq_assert (id_ == reconnect_timer_id); + timer_started = false; + start_connecting (); +} + +void zmq::vmci_connecter_t::start_connecting () +{ + // Open the connecting socket. + int rc = open (); + + // Connect may succeed in synchronous manner. + if (rc == 0) { + handle = add_fd (s); + handle_valid = true; + out_event (); + } + + // Handle any other error condition by eventual reconnect. + else { + if (s != retired_fd) + close (); + add_reconnect_timer (); + } +} + +void zmq::vmci_connecter_t::add_reconnect_timer () +{ + if (options.reconnect_ivl > 0) { + int rc_ivl = get_new_reconnect_ivl (); + add_timer (rc_ivl, reconnect_timer_id); + socket->event_connect_retried ( + make_unconnected_bind_endpoint_pair (endpoint), rc_ivl); + timer_started = true; + } +} + +int zmq::vmci_connecter_t::get_new_reconnect_ivl () +{ + // The new interval is the current interval + random value. + int this_interval = + current_reconnect_ivl + (generate_random () % options.reconnect_ivl); + + // Only change the current reconnect interval if the maximum reconnect + // interval was set and if it's larger than the reconnect interval. + if (options.reconnect_ivl_max > 0 + && options.reconnect_ivl_max > options.reconnect_ivl) { + // Calculate the next interval + current_reconnect_ivl = current_reconnect_ivl * 2; + if (current_reconnect_ivl >= options.reconnect_ivl_max) { + current_reconnect_ivl = options.reconnect_ivl_max; + } + } + return this_interval; +} + +int zmq::vmci_connecter_t::open () +{ + zmq_assert (s == retired_fd); + + int family = this->get_ctx ()->get_vmci_socket_family (); + if (family == -1) + return -1; + + // Create the socket. + s = open_socket (family, SOCK_STREAM, 0); +#ifdef ZMQ_HAVE_WINDOWS + if (s == INVALID_SOCKET) { + errno = wsa_error_to_errno (WSAGetLastError ()); + return -1; + } +#else + if (s == -1) + return -1; +#endif + + // Connect to the remote peer. + int rc = ::connect (s, addr->resolved.vmci_addr->addr (), + addr->resolved.vmci_addr->addrlen ()); + + // Connect was successful immediately. + if (rc == 0) + return 0; + + // Forward the error. + return -1; +} + +void zmq::vmci_connecter_t::close () +{ + zmq_assert (s != retired_fd); +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (s); + wsa_assert (rc != SOCKET_ERROR); +#else + const int rc = ::close (s); + errno_assert (rc == 0); +#endif + socket->event_closed (make_unconnected_bind_endpoint_pair (endpoint), s); + s = retired_fd; +} + +zmq::fd_t zmq::vmci_connecter_t::connect () +{ + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + int err = 0; +#if defined ZMQ_HAVE_HPUX + int len = sizeof (err); +#else + socklen_t len = sizeof (err); +#endif + int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char *) &err, &len); + + // Assert if the error was caused by 0MQ bug. + // Networking problems are OK. No need to assert. +#ifdef ZMQ_HAVE_WINDOWS + zmq_assert (rc == 0); + if (err != 0) { + if (err != WSAECONNREFUSED && err != WSAETIMEDOUT + && err != WSAECONNABORTED && err != WSAEHOSTUNREACH + && err != WSAENETUNREACH && err != WSAENETDOWN && err != WSAEACCES + && err != WSAEINVAL && err != WSAEADDRINUSE + && err != WSAECONNRESET) { + wsa_assert_no (err); + } + return retired_fd; + } +#else + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + if (rc == -1) + err = errno; + if (err != 0) { + errno = err; + errno_assert (errno == ECONNREFUSED || errno == ECONNRESET + || errno == ETIMEDOUT || errno == EHOSTUNREACH + || errno == ENETUNREACH || errno == ENETDOWN + || errno == EINVAL); + return retired_fd; + } +#endif + + fd_t result = s; + s = retired_fd; + return result; +} + +#endif diff --git a/3rd/libzmq/src/vmci_connecter.hpp b/3rd/libzmq/src/vmci_connecter.hpp new file mode 100644 index 00000000..d0ebaac9 --- /dev/null +++ b/3rd/libzmq/src/vmci_connecter.hpp @@ -0,0 +1,137 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_VMCI_CONNECTER_HPP_INCLUDED__ +#define __ZMQ_VMCI_CONNECTER_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_VMCI + +#include "fd.hpp" +#include "own.hpp" +#include "stdint.hpp" +#include "io_object.hpp" + +namespace zmq +{ +class io_thread_t; +class session_base_t; +struct address_t; + +// TODO consider refactoring this to derive from stream_connecter_base_t +class vmci_connecter_t ZMQ_FINAL : public own_t, public io_object_t +{ + public: + // If 'delayed_start' is true connecter first waits for a while, + // then starts connection process. + vmci_connecter_t (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const options_t &options_, + const address_t *addr_, + bool delayed_start_); + ~vmci_connecter_t (); + + private: + // ID of the timer used to delay the reconnection. + enum + { + reconnect_timer_id = 1 + }; + + // Handlers for incoming commands. + void process_plug (); + void process_term (int linger_); + + // Handlers for I/O events. + void in_event (); + void out_event (); + void timer_event (int id_); + + // Internal function to start the actual connection establishment. + void start_connecting (); + + // Internal function to add a reconnect timer + void add_reconnect_timer (); + + // Internal function to return a reconnect backoff delay. + // Will modify the current_reconnect_ivl used for next call + // Returns the currently used interval + int get_new_reconnect_ivl (); + + // Open VMCI connecting socket. Returns -1 in case of error, + // 0 if connect was successful immediately. Returns -1 with + // EAGAIN errno if async connect was launched. + int open (); + + // Close the connecting socket. + void close (); + + // Get the file descriptor of newly created connection. Returns + // retired_fd if the connection was unsuccessful. + fd_t connect (); + + // Address to connect to. Owned by session_base_t. + const address_t *addr; + + // Underlying socket. + fd_t s; + + // Handle corresponding to the listening socket. + handle_t handle; + + // If true file descriptor is registered with the poller and 'handle' + // contains valid value. + bool handle_valid; + + // If true, connecter is waiting a while before trying to connect. + const bool delayed_start; + + // True iff a timer has been started. + bool timer_started; + + // Reference to the session we belong to. + zmq::session_base_t *session; + + // Current reconnect ivl, updated for backoff strategy + int current_reconnect_ivl; + + // String representation of endpoint to connect to + std::string endpoint; + + // Socket + zmq::socket_base_t *socket; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (vmci_connecter_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/vmci_listener.cpp b/3rd/libzmq/src/vmci_listener.cpp new file mode 100644 index 00000000..0ac6db34 --- /dev/null +++ b/3rd/libzmq/src/vmci_listener.cpp @@ -0,0 +1,267 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "vmci_listener.hpp" + +#if defined ZMQ_HAVE_VMCI + +#include + +#include "stream_engine.hpp" +#include "vmci_address.hpp" +#include "io_thread.hpp" +#include "session_base.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "socket_base.hpp" +#include "vmci.hpp" + +#if defined ZMQ_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#include +#endif + +zmq::vmci_listener_t::vmci_listener_t (io_thread_t *io_thread_, + socket_base_t *socket_, + const options_t &options_) : + own_t (io_thread_, options_), + io_object_t (io_thread_), + s (retired_fd), + socket (socket_) +{ +} + +zmq::vmci_listener_t::~vmci_listener_t () +{ + zmq_assert (s == retired_fd); +} + +void zmq::vmci_listener_t::process_plug () +{ + // Start polling for incoming connections. + handle = add_fd (s); + set_pollin (handle); +} + +void zmq::vmci_listener_t::process_term (int linger_) +{ + rm_fd (handle); + close (); + own_t::process_term (linger_); +} + +void zmq::vmci_listener_t::in_event () +{ + fd_t fd = accept (); + + // If connection was reset by the peer in the meantime, just ignore it. + if (fd == retired_fd) { + socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (endpoint), zmq_errno ()); + return; + } + + tune_vmci_buffer_size (this->get_ctx (), fd, options.vmci_buffer_size, + options.vmci_buffer_min_size, + options.vmci_buffer_max_size); + + if (options.vmci_connect_timeout > 0) { +#if defined ZMQ_HAVE_WINDOWS + tune_vmci_connect_timeout (this->get_ctx (), fd, + options.vmci_connect_timeout); +#else + struct timeval timeout = {0, options.vmci_connect_timeout * 1000}; + tune_vmci_connect_timeout (this->get_ctx (), fd, timeout); +#endif + } + + // Create the engine object for this connection. + stream_engine_t *engine = new (std::nothrow) stream_engine_t ( + fd, options, make_unconnected_bind_endpoint_pair (endpoint)); + alloc_assert (engine); + + // Choose I/O thread to run connecter in. Given that we are already + // running in an I/O thread, there must be at least one available. + io_thread_t *io_thread = choose_io_thread (options.affinity); + zmq_assert (io_thread); + + // Create and launch a session object. + session_base_t *session = + session_base_t::create (io_thread, false, socket, options, NULL); + errno_assert (session); + session->inc_seqnum (); + launch_child (session); + send_attach (session, engine, false); + socket->event_accepted (make_unconnected_bind_endpoint_pair (endpoint), fd); +} + +int zmq::vmci_listener_t::get_local_address (std::string &addr_) +{ + struct sockaddr_storage ss; +#ifdef ZMQ_HAVE_HPUX + int sl = sizeof (ss); +#else + socklen_t sl = sizeof (ss); +#endif + int rc = getsockname (s, (sockaddr *) &ss, &sl); + if (rc != 0) { + addr_.clear (); + return rc; + } + + vmci_address_t addr ((struct sockaddr *) &ss, sl, this->get_ctx ()); + return addr.to_string (addr_); +} + +int zmq::vmci_listener_t::set_local_address (const char *addr_) +{ + // Create addr on stack for auto-cleanup + std::string addr (addr_); + + // Initialise the address structure. + vmci_address_t address (this->get_ctx ()); + int rc = address.resolve (addr.c_str ()); + if (rc != 0) + return -1; + + // Create a listening socket. + s = + open_socket (this->get_ctx ()->get_vmci_socket_family (), SOCK_STREAM, 0); +#ifdef ZMQ_HAVE_WINDOWS + if (s == INVALID_SOCKET) { + errno = wsa_error_to_errno (WSAGetLastError ()); + return -1; + } +#if !defined _WIN32_WCE + // On Windows, preventing sockets to be inherited by child processes. + BOOL brc = SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0); + win_assert (brc); +#endif +#else + if (s == -1) + return -1; +#endif + + address.to_string (endpoint); + + // Bind the socket. + rc = bind (s, address.addr (), address.addrlen ()); +#ifdef ZMQ_HAVE_WINDOWS + if (rc == SOCKET_ERROR) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error; + } +#else + if (rc != 0) + goto error; +#endif + + // Listen for incoming connections. + rc = listen (s, options.backlog); +#ifdef ZMQ_HAVE_WINDOWS + if (rc == SOCKET_ERROR) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error; + } +#else + if (rc != 0) + goto error; +#endif + + socket->event_listening (make_unconnected_bind_endpoint_pair (endpoint), s); + return 0; + +error: + int err = errno; + close (); + errno = err; + return -1; +} + +void zmq::vmci_listener_t::close () +{ + zmq_assert (s != retired_fd); +#ifdef ZMQ_HAVE_WINDOWS + int rc = closesocket (s); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = ::close (s); + errno_assert (rc == 0); +#endif + socket->event_closed (make_unconnected_bind_endpoint_pair (endpoint), s); + s = retired_fd; +} + +zmq::fd_t zmq::vmci_listener_t::accept () +{ + // Accept one connection and deal with different failure modes. + // The situation where connection cannot be accepted due to insufficient + // resources is considered valid and treated by ignoring the connection. + zmq_assert (s != retired_fd); + fd_t sock = ::accept (s, NULL, NULL); + +#ifdef ZMQ_HAVE_WINDOWS + if (sock == INVALID_SOCKET) { + wsa_assert (WSAGetLastError () == WSAEWOULDBLOCK + || WSAGetLastError () == WSAECONNRESET + || WSAGetLastError () == WSAEMFILE + || WSAGetLastError () == WSAENOBUFS); + return retired_fd; + } +#if !defined _WIN32_WCE + // On Windows, preventing sockets to be inherited by child processes. + BOOL brc = SetHandleInformation ((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); + win_assert (brc); +#endif +#else + if (sock == -1) { + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR + || errno == ECONNABORTED || errno == EPROTO + || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE + || errno == ENFILE); + return retired_fd; + } +#endif + + // Race condition can cause socket not to be closed (if fork happens + // between accept and this point). +#ifdef FD_CLOEXEC + int rc = fcntl (sock, F_SETFD, FD_CLOEXEC); + errno_assert (rc != -1); +#endif + + return sock; +} + +#endif diff --git a/3rd/libzmq/src/vmci_listener.hpp b/3rd/libzmq/src/vmci_listener.hpp new file mode 100644 index 00000000..4ab11842 --- /dev/null +++ b/3rd/libzmq/src/vmci_listener.hpp @@ -0,0 +1,98 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_VMCI_LISTENER_HPP_INCLUDED__ +#define __ZMQ_VMCI_LISTENER_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZMQ_HAVE_VMCI + +#include + +#include "fd.hpp" +#include "own.hpp" +#include "stdint.hpp" +#include "io_object.hpp" + +namespace zmq +{ +class io_thread_t; +class socket_base_t; + +// TODO consider refactoring this to derive from stream_listener_base_t +class vmci_listener_t ZMQ_FINAL : public own_t, public io_object_t +{ + public: + vmci_listener_t (zmq::io_thread_t *io_thread_, + zmq::socket_base_t *socket_, + const options_t &options_); + ~vmci_listener_t (); + + // Set address to listen on. + int set_local_address (const char *addr_); + + // Get the bound address for use with wildcards + int get_local_address (std::string &addr_); + + private: + // Handlers for incoming commands. + void process_plug (); + void process_term (int linger_); + + // Handlers for I/O events. + void in_event (); + + // Close the listening socket. + void close (); + + // Accept the new connection. Returns the file descriptor of the + // newly created connection. The function may return retired_fd + // if the connection was dropped while waiting in the listen backlog. + fd_t accept (); + + // Underlying socket. + fd_t s; + + // Handle corresponding to the listening socket. + handle_t handle; + + // Socket the listerner belongs to. + zmq::socket_base_t *socket; + + // String representation of endpoint to bind to + std::string endpoint; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (vmci_listener_t) +}; +} + +#endif + +#endif diff --git a/3rd/libzmq/src/windows.hpp b/3rd/libzmq/src/windows.hpp new file mode 100644 index 00000000..11c7581d --- /dev/null +++ b/3rd/libzmq/src/windows.hpp @@ -0,0 +1,100 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WINDOWS_HPP_INCLUDED__ +#define __ZMQ_WINDOWS_HPP_INCLUDED__ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#ifndef NOMINMAX +#define NOMINMAX // Macros min(a,b) and max(a,b) +#endif + +// Set target version to Windows Server 2008, Windows Vista or higher. +// Windows XP (0x0501) is supported but without client & server socket types. +#if !defined _WIN32_WINNT && !defined ZMQ_HAVE_WINDOWS_UWP +#define _WIN32_WINNT 0x0600 +#endif + +#ifdef __MINGW32__ +// Require Windows XP or higher with MinGW for getaddrinfo(). +#if (_WIN32_WINNT >= 0x0501) +#else +#error You need at least Windows XP target +#endif +#endif + +#include +#include +#include +#include + +#if !defined __MINGW32__ +#include +#endif + +// Workaround missing mstcpip.h in mingw32 (MinGW64 provides this) +// __MINGW64_VERSION_MAJOR is only defined when using in mingw-w64 +#if defined __MINGW32__ && !defined SIO_KEEPALIVE_VALS \ + && !defined __MINGW64_VERSION_MAJOR +struct tcp_keepalive +{ + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; +}; +#define SIO_KEEPALIVE_VALS _WSAIOW (IOC_VENDOR, 4) +#endif + +#include +#include +#if !defined _WIN32_WCE +#include +#endif + +#if defined ZMQ_IOTHREAD_POLLER_USE_POLL || defined ZMQ_POLL_BASED_ON_POLL +static inline int poll (struct pollfd *pfd, unsigned long nfds, int timeout) +{ + return WSAPoll (pfd, nfds, timeout); +} +#endif + +// In MinGW environment AI_NUMERICSERV is not defined. +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 +#endif +#endif + +// In MSVC prior to v14, snprintf is not available +// The closest implementation is the _snprintf_s function +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf(buffer_, count_, format_, ...) \ + _snprintf_s (buffer_, count_, _TRUNCATE, format_, __VA_ARGS__) +#endif diff --git a/3rd/libzmq/src/wire.hpp b/3rd/libzmq/src/wire.hpp new file mode 100644 index 00000000..47b61b00 --- /dev/null +++ b/3rd/libzmq/src/wire.hpp @@ -0,0 +1,103 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WIRE_HPP_INCLUDED__ +#define __ZMQ_WIRE_HPP_INCLUDED__ + +#include "stdint.hpp" + +namespace zmq +{ +// Helper functions to convert different integer types to/from network +// byte order. + +inline void put_uint8 (unsigned char *buffer_, uint8_t value_) +{ + *buffer_ = value_; +} + +inline uint8_t get_uint8 (const unsigned char *buffer_) +{ + return *buffer_; +} + +inline void put_uint16 (unsigned char *buffer_, uint16_t value_) +{ + buffer_[0] = static_cast (((value_) >> 8) & 0xff); + buffer_[1] = static_cast (value_ & 0xff); +} + +inline uint16_t get_uint16 (const unsigned char *buffer_) +{ + return ((static_cast (buffer_[0])) << 8) + | (static_cast (buffer_[1])); +} + +inline void put_uint32 (unsigned char *buffer_, uint32_t value_) +{ + buffer_[0] = static_cast (((value_) >> 24) & 0xff); + buffer_[1] = static_cast (((value_) >> 16) & 0xff); + buffer_[2] = static_cast (((value_) >> 8) & 0xff); + buffer_[3] = static_cast (value_ & 0xff); +} + +inline uint32_t get_uint32 (const unsigned char *buffer_) +{ + return ((static_cast (buffer_[0])) << 24) + | ((static_cast (buffer_[1])) << 16) + | ((static_cast (buffer_[2])) << 8) + | (static_cast (buffer_[3])); +} + +inline void put_uint64 (unsigned char *buffer_, uint64_t value_) +{ + buffer_[0] = static_cast (((value_) >> 56) & 0xff); + buffer_[1] = static_cast (((value_) >> 48) & 0xff); + buffer_[2] = static_cast (((value_) >> 40) & 0xff); + buffer_[3] = static_cast (((value_) >> 32) & 0xff); + buffer_[4] = static_cast (((value_) >> 24) & 0xff); + buffer_[5] = static_cast (((value_) >> 16) & 0xff); + buffer_[6] = static_cast (((value_) >> 8) & 0xff); + buffer_[7] = static_cast (value_ & 0xff); +} + +inline uint64_t get_uint64 (const unsigned char *buffer_) +{ + return ((static_cast (buffer_[0])) << 56) + | ((static_cast (buffer_[1])) << 48) + | ((static_cast (buffer_[2])) << 40) + | ((static_cast (buffer_[3])) << 32) + | ((static_cast (buffer_[4])) << 24) + | ((static_cast (buffer_[5])) << 16) + | ((static_cast (buffer_[6])) << 8) + | (static_cast (buffer_[7])); +} +} + +#endif diff --git a/3rd/libzmq/src/ws_address.cpp b/3rd/libzmq/src/ws_address.cpp new file mode 100644 index 00000000..9e5224b9 --- /dev/null +++ b/3rd/libzmq/src/ws_address.cpp @@ -0,0 +1,166 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "macros.hpp" +#include "ws_address.hpp" +#include "stdint.hpp" +#include "err.hpp" +#include "ip.hpp" + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +zmq::ws_address_t::ws_address_t () +{ + memset (&_address, 0, sizeof (_address)); +} + +zmq::ws_address_t::ws_address_t (const sockaddr *sa_, socklen_t sa_len_) +{ + zmq_assert (sa_ && sa_len_ > 0); + + memset (&_address, 0, sizeof (_address)); + if (sa_->sa_family == AF_INET + && sa_len_ >= static_cast (sizeof (_address.ipv4))) + memcpy (&_address.ipv4, sa_, sizeof (_address.ipv4)); + else if (sa_->sa_family == AF_INET6 + && sa_len_ >= static_cast (sizeof (_address.ipv6))) + memcpy (&_address.ipv6, sa_, sizeof (_address.ipv6)); + + _path = std::string (""); + + char hbuf[NI_MAXHOST]; + const int rc = getnameinfo (addr (), addrlen (), hbuf, sizeof (hbuf), NULL, + 0, NI_NUMERICHOST); + if (rc != 0) { + _host = std::string ("localhost"); + return; + } + + std::ostringstream os; + + if (_address.family () == AF_INET6) + os << std::string ("["); + + os << std::string (hbuf); + + if (_address.family () == AF_INET6) + os << std::string ("]"); + + _host = os.str (); +} + +int zmq::ws_address_t::resolve (const char *name_, bool local_, bool ipv6_) +{ + // find the host part, It's important to use str*r*chr to only get + // the latest colon since IPv6 addresses use colons as delemiters. + const char *delim = strrchr (name_, ':'); + if (delim == NULL) { + errno = EINVAL; + return -1; + } + _host = std::string (name_, delim - name_); + + // find the path part, which is optional + delim = strrchr (name_, '/'); + std::string host_name; + if (delim) { + _path = std::string (delim); + // remove the path, otherwise resolving the port will fail with wildcard + host_name = std::string (name_, delim - name_); + } else { + _path = std::string ("/"); + host_name = name_; + } + + ip_resolver_options_t resolver_opts; + resolver_opts.bindable (local_) + .allow_dns (!local_) + .allow_nic_name (local_) + .ipv6 (ipv6_) + .allow_path (true) + .expect_port (true); + + ip_resolver_t resolver (resolver_opts); + + return resolver.resolve (&_address, host_name.c_str ()); +} + +int zmq::ws_address_t::to_string (std::string &addr_) const +{ + std::ostringstream os; + os << std::string ("ws://") << host () << std::string (":") + << _address.port () << _path; + addr_ = os.str (); + + return 0; +} + +const sockaddr *zmq::ws_address_t::addr () const +{ + return _address.as_sockaddr (); +} + +socklen_t zmq::ws_address_t::addrlen () const +{ + return _address.sockaddr_len (); +} + +const char *zmq::ws_address_t::host () const +{ + return _host.c_str (); +} + +const char *zmq::ws_address_t::path () const +{ + return _path.c_str (); +} + +#if defined ZMQ_HAVE_WINDOWS +unsigned short zmq::ws_address_t::family () const +#else +sa_family_t zmq::ws_address_t::family () const +#endif +{ + return _address.family (); +} diff --git a/3rd/libzmq/src/ws_address.hpp b/3rd/libzmq/src/ws_address.hpp new file mode 100644 index 00000000..a5d5de89 --- /dev/null +++ b/3rd/libzmq/src/ws_address.hpp @@ -0,0 +1,77 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WS_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_WS_ADDRESS_HPP_INCLUDED__ + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#endif + +#include "ip_resolver.hpp" + +namespace zmq +{ +class ws_address_t +{ + public: + ws_address_t (); + ws_address_t (const sockaddr *sa_, socklen_t sa_len_); + + // This function translates textual WS address into an address + // structure. If 'local' is true, names are resolved as local interface + // names. If it is false, names are resolved as remote hostnames. + // If 'ipv6' is true, the name may resolve to IPv6 address. + int resolve (const char *name_, bool local_, bool ipv6_); + + // The opposite to resolve() + int to_string (std::string &addr_) const; + +#if defined ZMQ_HAVE_WINDOWS + unsigned short family () const; +#else + sa_family_t family () const; +#endif + const sockaddr *addr () const; + socklen_t addrlen () const; + + const char *host () const; + const char *path () const; + + protected: + ip_addr_t _address; + + private: + std::string _host; + std::string _path; +}; +} + +#endif diff --git a/3rd/libzmq/src/ws_connecter.cpp b/3rd/libzmq/src/ws_connecter.cpp new file mode 100644 index 00000000..f9321d9b --- /dev/null +++ b/3rd/libzmq/src/ws_connecter.cpp @@ -0,0 +1,306 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "macros.hpp" +#include "ws_connecter.hpp" +#include "io_thread.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "address.hpp" +#include "ws_address.hpp" +#include "ws_engine.hpp" +#include "session_base.hpp" + +#ifdef ZMQ_HAVE_WSS +#include "wss_engine.hpp" +#include "wss_address.hpp" +#endif + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#ifdef ZMQ_HAVE_OPENVMS +#include +#endif +#endif + +#ifdef __APPLE__ +#include +#endif + +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 wss_, + const std::string &tls_hostname_) : + stream_connecter_base_t ( + io_thread_, session_, options_, addr_, delayed_start_), + _connect_timer_started (false), + _wss (wss_), + _hostname (tls_hostname_) +{ +} + +zmq::ws_connecter_t::~ws_connecter_t () +{ + zmq_assert (!_connect_timer_started); +} + +void zmq::ws_connecter_t::process_term (int linger_) +{ + if (_connect_timer_started) { + cancel_timer (connect_timer_id); + _connect_timer_started = false; + } + + stream_connecter_base_t::process_term (linger_); +} + +void zmq::ws_connecter_t::out_event () +{ + if (_connect_timer_started) { + cancel_timer (connect_timer_id); + _connect_timer_started = false; + } + + // TODO this is still very similar to (t)ipc_connecter_t, maybe the + // differences can be factored out + + rm_handle (); + + const fd_t fd = connect (); + + // Handle the error condition by attempt to reconnect. + if (fd == retired_fd || !tune_socket (fd)) { + close (); + add_reconnect_timer (); + return; + } + + if (_wss) +#ifdef ZMQ_HAVE_WSS + create_engine (fd, + get_socket_name (fd, socket_end_local)); +#else + assert (false); +#endif + else + create_engine (fd, + get_socket_name (fd, socket_end_local)); +} + +void zmq::ws_connecter_t::timer_event (int id_) +{ + if (id_ == connect_timer_id) { + _connect_timer_started = false; + rm_handle (); + close (); + add_reconnect_timer (); + } else + stream_connecter_base_t::timer_event (id_); +} + +void zmq::ws_connecter_t::start_connecting () +{ + // Open the connecting socket. + const int rc = open (); + + // Connect may succeed in synchronous manner. + if (rc == 0) { + _handle = add_fd (_s); + out_event (); + } + + // Connection establishment may be delayed. Poll for its completion. + else if (rc == -1 && errno == EINPROGRESS) { + _handle = add_fd (_s); + set_pollout (_handle); + _socket->event_connect_delayed ( + make_unconnected_connect_endpoint_pair (_endpoint), zmq_errno ()); + + // add userspace connect timeout + add_connect_timer (); + } + + // Handle any other error condition by eventual reconnect. + else { + if (_s != retired_fd) + close (); + add_reconnect_timer (); + } +} + +void zmq::ws_connecter_t::add_connect_timer () +{ + if (options.connect_timeout > 0) { + add_timer (options.connect_timeout, connect_timer_id); + _connect_timer_started = true; + } +} + +int zmq::ws_connecter_t::open () +{ + zmq_assert (_s == retired_fd); + + tcp_address_t tcp_addr; + _s = tcp_open_socket (_addr->address.c_str (), options, false, true, + &tcp_addr); + if (_s == retired_fd) + return -1; + + // Set the socket to non-blocking mode so that we get async connect(). + unblock_socket (_s); + + // Connect to the remote peer. +#ifdef ZMQ_HAVE_VXWORKS + int rc = ::connect (_s, (sockaddr *) tcp_addr.addr (), tcp_addr.addrlen ()); +#else + const int rc = ::connect (_s, tcp_addr.addr (), tcp_addr.addrlen ()); +#endif + // Connect was successful immediately. + if (rc == 0) { + return 0; + } + + // Translate error codes indicating asynchronous connect has been + // launched to a uniform EINPROGRESS. +#ifdef ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + if (last_error == WSAEINPROGRESS || last_error == WSAEWOULDBLOCK) + errno = EINPROGRESS; + else + errno = wsa_error_to_errno (last_error); +#else + if (errno == EINTR) + errno = EINPROGRESS; +#endif + return -1; +} + +zmq::fd_t zmq::ws_connecter_t::connect () +{ + // Async connect has finished. Check whether an error occurred + int err = 0; +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int len = sizeof err; +#else + socklen_t len = sizeof err; +#endif + + const int rc = getsockopt (_s, SOL_SOCKET, SO_ERROR, + reinterpret_cast (&err), &len); + + // Assert if the error was caused by 0MQ bug. + // Networking problems are OK. No need to assert. +#ifdef ZMQ_HAVE_WINDOWS + zmq_assert (rc == 0); + if (err != 0) { + if (err == WSAEBADF || err == WSAENOPROTOOPT || err == WSAENOTSOCK + || err == WSAENOBUFS) { + wsa_assert_no (err); + } + return retired_fd; + } +#else + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + if (rc == -1) + err = errno; + if (err != 0) { + errno = err; +#if !defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE + errno_assert (errno != EBADF && errno != ENOPROTOOPT + && errno != ENOTSOCK && errno != ENOBUFS); +#else + errno_assert (errno != ENOPROTOOPT && errno != ENOTSOCK + && errno != ENOBUFS); +#endif + return retired_fd; + } +#endif + + // Return the newly connected socket. + const fd_t result = _s; + _s = retired_fd; + return result; +} + +bool zmq::ws_connecter_t::tune_socket (const fd_t fd_) +{ + const int rc = + tune_tcp_socket (fd_) | tune_tcp_maxrt (fd_, options.tcp_maxrt); + return rc == 0; +} + +void zmq::ws_connecter_t::create_engine (fd_t fd_, + const std::string &local_address_) +{ + const endpoint_uri_pair_t endpoint_pair (local_address_, _endpoint, + endpoint_type_connect); + + // Create the engine object for this connection. + 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 + LIBZMQ_UNUSED (_hostname); + 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. + send_attach (_session, engine); + + // Shut the connecter down. + terminate (); + + _socket->event_connected (endpoint_pair, fd_); +} diff --git a/3rd/libzmq/src/ws_connecter.hpp b/3rd/libzmq/src/ws_connecter.hpp new file mode 100644 index 00000000..9755b216 --- /dev/null +++ b/3rd/libzmq/src/ws_connecter.hpp @@ -0,0 +1,98 @@ +/* + 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 . +*/ + +#ifndef __WS_CONNECTER_HPP_INCLUDED__ +#define __WS_CONNECTER_HPP_INCLUDED__ + +#include "fd.hpp" +#include "stdint.hpp" +#include "stream_connecter_base.hpp" + +namespace zmq +{ +class ws_connecter_t ZMQ_FINAL : public stream_connecter_base_t +{ + public: + // If 'delayed_start' is true connecter first waits for a while, + // then starts connection process. + ws_connecter_t (zmq::io_thread_t *io_thread_, + zmq::session_base_t *session_, + const options_t &options_, + address_t *addr_, + bool delayed_start_, + bool wss_, + const std::string &tls_hostname_); + ~ws_connecter_t (); + + protected: + void create_engine (fd_t fd, const std::string &local_address_); + + private: + // ID of the timer used to check the connect timeout, must be different from stream_connecter_base_t::reconnect_timer_id. + enum + { + connect_timer_id = 2 + }; + + // Handlers for incoming commands. + void process_term (int linger_); + + // Handlers for I/O events. + void out_event (); + void timer_event (int id_); + + // Internal function to start the actual connection establishment. + void start_connecting (); + + // Internal function to add a connect timer + void add_connect_timer (); + + // Open TCP connecting socket. Returns -1 in case of error, + // 0 if connect was successful immediately. Returns -1 with + // EAGAIN errno if async connect was launched. + int open (); + + // Get the file descriptor of newly created connection. Returns + // retired_fd if the connection was unsuccessful. + fd_t connect (); + + // Tunes a connected socket. + bool tune_socket (fd_t fd_); + + // True iff a timer has been started. + bool _connect_timer_started; + + bool _wss; + const std::string &_hostname; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ws_connecter_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/ws_decoder.cpp b/3rd/libzmq/src/ws_decoder.cpp new file mode 100644 index 00000000..58b12707 --- /dev/null +++ b/3rd/libzmq/src/ws_decoder.cpp @@ -0,0 +1,276 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include +#include +#include + +#include "ws_protocol.hpp" +#include "ws_decoder.hpp" +#include "likely.hpp" +#include "wire.hpp" +#include "err.hpp" + +zmq::ws_decoder_t::ws_decoder_t (size_t bufsize_, + int64_t maxmsgsize_, + bool zero_copy_, + bool must_mask_) : + decoder_base_t (bufsize_), + _msg_flags (0), + _zero_copy (zero_copy_), + _max_msg_size (maxmsgsize_), + _must_mask (must_mask_), + _size (0) +{ + memset (_tmpbuf, 0, sizeof (_tmpbuf)); + int rc = _in_progress.init (); + errno_assert (rc == 0); + + // At the beginning, read one byte and go to opcode_ready state. + next_step (_tmpbuf, 1, &ws_decoder_t::opcode_ready); +} + +zmq::ws_decoder_t::~ws_decoder_t () +{ + const int rc = _in_progress.close (); + errno_assert (rc == 0); +} + +int zmq::ws_decoder_t::opcode_ready (unsigned char const *) +{ + const bool final = (_tmpbuf[0] & 0x80) != 0; // final bit + if (!final) + return -1; // non final messages are not supported + + _opcode = static_cast (_tmpbuf[0] & 0xF); + + _msg_flags = 0; + + switch (_opcode) { + case zmq::ws_protocol_t::opcode_binary: + break; + case zmq::ws_protocol_t::opcode_close: + _msg_flags = msg_t::command | msg_t::close_cmd; + break; + case zmq::ws_protocol_t::opcode_ping: + _msg_flags = msg_t::ping | msg_t::command; + break; + case zmq::ws_protocol_t::opcode_pong: + _msg_flags = msg_t::pong | msg_t::command; + break; + default: + return -1; + } + + next_step (_tmpbuf, 1, &ws_decoder_t::size_first_byte_ready); + + return 0; +} + +int zmq::ws_decoder_t::size_first_byte_ready (unsigned char const *read_from_) +{ + const bool is_masked = (_tmpbuf[0] & 0x80) != 0; + + if (is_masked != _must_mask) // wrong mask value + return -1; + + _size = static_cast (_tmpbuf[0] & 0x7F); + + if (_size < 126) { + if (_must_mask) + next_step (_tmpbuf, 4, &ws_decoder_t::mask_ready); + else if (_opcode == ws_protocol_t::opcode_binary) { + if (_size == 0) + return -1; + next_step (_tmpbuf, 1, &ws_decoder_t::flags_ready); + } else + return size_ready (read_from_); + } else if (_size == 126) + next_step (_tmpbuf, 2, &ws_decoder_t::short_size_ready); + else + next_step (_tmpbuf, 8, &ws_decoder_t::long_size_ready); + + return 0; +} + + +int zmq::ws_decoder_t::short_size_ready (unsigned char const *read_from_) +{ + _size = (_tmpbuf[0] << 8) | _tmpbuf[1]; + + if (_must_mask) + next_step (_tmpbuf, 4, &ws_decoder_t::mask_ready); + else if (_opcode == ws_protocol_t::opcode_binary) { + if (_size == 0) + return -1; + next_step (_tmpbuf, 1, &ws_decoder_t::flags_ready); + } else + return size_ready (read_from_); + + return 0; +} + +int zmq::ws_decoder_t::long_size_ready (unsigned char const *read_from_) +{ + // The payload size is encoded as 64-bit unsigned integer. + // The most significant byte comes first. + _size = get_uint64 (_tmpbuf); + + if (_must_mask) + next_step (_tmpbuf, 4, &ws_decoder_t::mask_ready); + else if (_opcode == ws_protocol_t::opcode_binary) { + if (_size == 0) + return -1; + next_step (_tmpbuf, 1, &ws_decoder_t::flags_ready); + } else + return size_ready (read_from_); + + return 0; +} + +int zmq::ws_decoder_t::mask_ready (unsigned char const *read_from_) +{ + memcpy (_mask, _tmpbuf, 4); + + if (_opcode == ws_protocol_t::opcode_binary) { + if (_size == 0) + return -1; + + next_step (_tmpbuf, 1, &ws_decoder_t::flags_ready); + } else + return size_ready (read_from_); + + return 0; +} + +int zmq::ws_decoder_t::flags_ready (unsigned char const *read_from_) +{ + unsigned char flags; + + if (_must_mask) + flags = _tmpbuf[0] ^ _mask[0]; + else + flags = _tmpbuf[0]; + + if (flags & ws_protocol_t::more_flag) + _msg_flags |= msg_t::more; + if (flags & ws_protocol_t::command_flag) + _msg_flags |= msg_t::command; + + _size--; + + return size_ready (read_from_); +} + + +int zmq::ws_decoder_t::size_ready (unsigned char const *read_pos_) +{ + // Message size must not exceed the maximum allowed size. + if (_max_msg_size >= 0) + if (unlikely (_size > static_cast (_max_msg_size))) { + errno = EMSGSIZE; + return -1; + } + + // Message size must fit into size_t data type. + if (unlikely (_size != static_cast (_size))) { + errno = EMSGSIZE; + return -1; + } + + int rc = _in_progress.close (); + assert (rc == 0); + + // the current message can exceed the current buffer. We have to copy the buffer + // data into a new message and complete it in the next receive. + + shared_message_memory_allocator &allocator = get_allocator (); + if (unlikely (!_zero_copy || allocator.data () > read_pos_ + || static_cast (read_pos_ - allocator.data ()) + > allocator.size () + || _size > static_cast ( + allocator.data () + allocator.size () - read_pos_))) { + // a new message has started, but the size would exceed the pre-allocated arena + // (or read_pos_ is in the initial handshake buffer) + // this happens every time when a message does not fit completely into the buffer + rc = _in_progress.init_size (static_cast (_size)); + } else { + // construct message using n bytes from the buffer as storage + // increase buffer ref count + // if the message will be a large message, pass a valid refcnt memory location as well + rc = _in_progress.init ( + const_cast (read_pos_), static_cast (_size), + shared_message_memory_allocator::call_dec_ref, allocator.buffer (), + allocator.provide_content ()); + + // For small messages, data has been copied and refcount does not have to be increased + if (_in_progress.is_zcmsg ()) { + allocator.advance_content (); + allocator.inc_ref (); + } + } + + if (unlikely (rc)) { + errno_assert (errno == ENOMEM); + rc = _in_progress.init (); + errno_assert (rc == 0); + errno = ENOMEM; + return -1; + } + + _in_progress.set_flags (_msg_flags); + // this sets read_pos to + // the message data address if the data needs to be copied + // for small message / messages exceeding the current buffer + // or + // to the current start address in the buffer because the message + // was constructed to use n bytes from the address passed as argument + next_step (_in_progress.data (), _in_progress.size (), + &ws_decoder_t::message_ready); + + return 0; +} + +int zmq::ws_decoder_t::message_ready (unsigned char const *) +{ + if (_must_mask) { + int mask_index = _opcode == ws_protocol_t::opcode_binary ? 1 : 0; + + unsigned char *data = + static_cast (_in_progress.data ()); + for (size_t i = 0; i < _size; ++i, mask_index++) + data[i] = data[i] ^ _mask[mask_index % 4]; + } + + // Message is completely read. Signal this to the caller + // and prepare to decode next message. + next_step (_tmpbuf, 1, &ws_decoder_t::opcode_ready); + return 1; +} diff --git a/3rd/libzmq/src/ws_decoder.hpp b/3rd/libzmq/src/ws_decoder.hpp new file mode 100644 index 00000000..a12f7d1e --- /dev/null +++ b/3rd/libzmq/src/ws_decoder.hpp @@ -0,0 +1,81 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WS_DECODER_HPP_INCLUDED__ +#define __ZMQ_WS_DECODER_HPP_INCLUDED__ + +#include "decoder.hpp" +#include "decoder_allocators.hpp" +#include "ws_protocol.hpp" + +namespace zmq +{ +// Decoder for Web socket framing protocol. Converts data stream into messages. +// The class has to inherit from shared_message_memory_allocator because +// the base class calls allocate in its constructor. +class ws_decoder_t ZMQ_FINAL + : public decoder_base_t +{ + public: + ws_decoder_t (size_t bufsize_, + int64_t maxmsgsize_, + bool zero_copy_, + bool must_mask_); + ~ws_decoder_t (); + + // i_decoder interface. + msg_t *msg () { return &_in_progress; } + + private: + int opcode_ready (unsigned char const *); + int size_first_byte_ready (unsigned char const *); + int short_size_ready (unsigned char const *); + int long_size_ready (unsigned char const *); + int mask_ready (unsigned char const *); + int flags_ready (unsigned char const *); + int message_ready (unsigned char const *); + + int size_ready (unsigned char const *); + + unsigned char _tmpbuf[8]; + unsigned char _msg_flags; + msg_t _in_progress; + + const bool _zero_copy; + const int64_t _max_msg_size; + const bool _must_mask; + uint64_t _size; + zmq::ws_protocol_t::opcode_t _opcode; + unsigned char _mask[4]; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ws_decoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/ws_encoder.cpp b/3rd/libzmq/src/ws_encoder.cpp new file mode 100644 index 00000000..bc0b38c7 --- /dev/null +++ b/3rd/libzmq/src/ws_encoder.cpp @@ -0,0 +1,154 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "ws_protocol.hpp" +#include "ws_encoder.hpp" +#include "msg.hpp" +#include "likely.hpp" +#include "wire.hpp" +#include "random.hpp" + +#include + +zmq::ws_encoder_t::ws_encoder_t (size_t bufsize_, bool must_mask_) : + encoder_base_t (bufsize_), + _must_mask (must_mask_) +{ + // Write 0 bytes to the batch and go to message_ready state. + next_step (NULL, 0, &ws_encoder_t::message_ready, true); + _masked_msg.init (); +} + +zmq::ws_encoder_t::~ws_encoder_t () +{ + _masked_msg.close (); +} + +void zmq::ws_encoder_t::message_ready () +{ + int offset = 0; + + _is_binary = false; + + if (in_progress ()->is_ping ()) + _tmp_buf[offset++] = 0x80 | zmq::ws_protocol_t::opcode_ping; + else if (in_progress ()->is_pong ()) + _tmp_buf[offset++] = 0x80 | zmq::ws_protocol_t::opcode_pong; + else if (in_progress ()->is_close_cmd ()) + _tmp_buf[offset++] = 0x80 | zmq::ws_protocol_t::opcode_close; + else { + _tmp_buf[offset++] = 0x82; // Final | binary + _is_binary = true; + } + + _tmp_buf[offset] = _must_mask ? 0x80 : 0x00; + + size_t size = in_progress ()->size (); + if (_is_binary) + size++; + // TODO: create an opcode for subscribe/cancel + if (in_progress ()->is_subscribe () || in_progress ()->is_cancel ()) + size++; + + if (size <= 125) + _tmp_buf[offset++] |= static_cast (size & 127); + else if (size <= 0xFFFF) { + _tmp_buf[offset++] |= 126; + _tmp_buf[offset++] = static_cast ((size >> 8) & 0xFF); + _tmp_buf[offset++] = static_cast (size & 0xFF); + } else { + _tmp_buf[offset++] |= 127; + put_uint64 (_tmp_buf + offset, size); + offset += 8; + } + + if (_must_mask) { + const uint32_t random = generate_random (); + put_uint32 (_tmp_buf + offset, random); + put_uint32 (_mask, random); + offset += 4; + } + + int mask_index = 0; + if (_is_binary) { + // Encode flags. + unsigned char protocol_flags = 0; + if (in_progress ()->flags () & msg_t::more) + protocol_flags |= ws_protocol_t::more_flag; + if (in_progress ()->flags () & msg_t::command) + protocol_flags |= ws_protocol_t::command_flag; + + _tmp_buf[offset++] = + _must_mask ? protocol_flags ^ _mask[mask_index++] : protocol_flags; + } + + // Encode the subscribe/cancel byte. + // TODO: remove once there is an opcode for subscribe/cancel + if (in_progress ()->is_subscribe ()) + _tmp_buf[offset++] = _must_mask ? 1 ^ _mask[mask_index++] : 1; + else if (in_progress ()->is_cancel ()) + _tmp_buf[offset++] = _must_mask ? 0 ^ _mask[mask_index++] : 0; + + next_step (_tmp_buf, offset, &ws_encoder_t::size_ready, false); +} + +void zmq::ws_encoder_t::size_ready () +{ + if (_must_mask) { + assert (in_progress () != &_masked_msg); + const size_t size = in_progress ()->size (); + + unsigned char *src = + static_cast (in_progress ()->data ()); + unsigned char *dest = src; + + // If msg is shared or data is constant we cannot mask in-place, allocate a new msg for it + if (in_progress ()->flags () & msg_t::shared + || in_progress ()->is_cmsg ()) { + _masked_msg.close (); + _masked_msg.init_size (size); + dest = static_cast (_masked_msg.data ()); + } + + int mask_index = 0; + if (_is_binary) + ++mask_index; + // TODO: remove once there is an opcode for subscribe/cancel + if (in_progress ()->is_subscribe () || in_progress ()->is_cancel ()) + ++mask_index; + for (size_t i = 0; i < size; ++i, mask_index++) + dest[i] = src[i] ^ _mask[mask_index % 4]; + + next_step (dest, size, &ws_encoder_t::message_ready, true); + } else { + next_step (in_progress ()->data (), in_progress ()->size (), + &ws_encoder_t::message_ready, true); + } +} diff --git a/3rd/libzmq/src/ws_encoder.hpp b/3rd/libzmq/src/ws_encoder.hpp new file mode 100644 index 00000000..299259a7 --- /dev/null +++ b/3rd/libzmq/src/ws_encoder.hpp @@ -0,0 +1,59 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WS_ENCODER_HPP_INCLUDED__ +#define __ZMQ_WS_ENCODER_HPP_INCLUDED__ + +#include "encoder.hpp" + +namespace zmq +{ +// Encoder for web socket framing protocol. Converts messages into data stream. + +class ws_encoder_t ZMQ_FINAL : public encoder_base_t +{ + public: + ws_encoder_t (size_t bufsize_, bool must_mask_); + ~ws_encoder_t (); + + private: + void size_ready (); + void message_ready (); + + unsigned char _tmp_buf[16]; + bool _must_mask; + unsigned char _mask[4]; + msg_t _masked_msg; + bool _is_binary; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ws_encoder_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/ws_engine.cpp b/3rd/libzmq/src/ws_engine.cpp new file mode 100644 index 00000000..a239330c --- /dev/null +++ b/3rd/libzmq/src/ws_engine.cpp @@ -0,0 +1,1077 @@ +/* +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 . +*/ + +#include "precompiled.hpp" + +#ifdef ZMQ_USE_NSS +#include +#include +#define SHA_DIGEST_LENGTH 20 +#elif defined ZMQ_USE_BUILTIN_SHA1 +#include "../external/sha1/sha1.h" +#elif defined ZMQ_USE_GNUTLS +#define SHA_DIGEST_LENGTH 20 +#include +#include +#endif + +#if !defined ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#endif + +#include + +#include "compat.hpp" +#include "tcp.hpp" +#include "ws_engine.hpp" +#include "session_base.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "random.hpp" +#include "ws_decoder.hpp" +#include "ws_encoder.hpp" +#include "null_mechanism.hpp" +#include "plain_server.hpp" +#include "plain_client.hpp" + +#ifdef ZMQ_HAVE_CURVE +#include "curve_client.hpp" +#include "curve_server.hpp" +#endif + +// OSX uses a different name for this socket option +#ifndef IPV6_ADD_MEMBERSHIP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif + +#ifdef __APPLE__ +#include +#endif + +static int +encode_base64 (const unsigned char *in_, int in_len_, char *out_, int out_len_); + +static void compute_accept_key (char *key_, + unsigned char hash_[SHA_DIGEST_LENGTH]); + +zmq::ws_engine_t::ws_engine_t (fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_, + const ws_address_t &address_, + bool client_) : + stream_engine_base_t (fd_, options_, endpoint_uri_pair_, true), + _client (client_), + _address (address_), + _client_handshake_state (client_handshake_initial), + _server_handshake_state (handshake_initial), + _header_name_position (0), + _header_value_position (0), + _header_upgrade_websocket (false), + _header_connection_upgrade (false), + _heartbeat_timeout (0) +{ + memset (_websocket_key, 0, MAX_HEADER_VALUE_LENGTH + 1); + memset (_websocket_accept, 0, MAX_HEADER_VALUE_LENGTH + 1); + memset (_websocket_protocol, 0, 256); + + _next_msg = &ws_engine_t::next_handshake_command; + _process_msg = &ws_engine_t::process_handshake_command; + _close_msg.init (); + + if (_options.heartbeat_interval > 0) { + _heartbeat_timeout = _options.heartbeat_timeout; + if (_heartbeat_timeout == -1) + _heartbeat_timeout = _options.heartbeat_interval; + } +} + +zmq::ws_engine_t::~ws_engine_t () +{ + _close_msg.close (); +} + +void zmq::ws_engine_t::start_ws_handshake () +{ + if (_client) { + const char *protocol; + if (_options.mechanism == ZMQ_NULL) + protocol = "ZWS2.0/NULL,ZWS2.0"; + else if (_options.mechanism == ZMQ_PLAIN) + protocol = "ZWS2.0/PLAIN"; +#ifdef ZMQ_HAVE_CURVE + else if (_options.mechanism == ZMQ_CURVE) + protocol = "ZWS2.0/CURVE"; +#endif + else { + // Avoid unitialized variable error breaking UWP build + protocol = ""; + assert (false); + } + + unsigned char nonce[16]; + int *p = reinterpret_cast (nonce); + + // The nonce doesn't have to be secure one, it is just use to avoid proxy cache + *p = zmq::generate_random (); + *(p + 1) = zmq::generate_random (); + *(p + 2) = zmq::generate_random (); + *(p + 3) = zmq::generate_random (); + + int size = + encode_base64 (nonce, 16, _websocket_key, MAX_HEADER_VALUE_LENGTH); + assert (size > 0); + + size = snprintf ( + reinterpret_cast (_write_buffer), WS_BUFFER_SIZE, + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: %s\r\n" + "Sec-WebSocket-Protocol: %s\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n", + _address.path (), _address.host (), _websocket_key, protocol); + assert (size > 0 && size < WS_BUFFER_SIZE); + _outpos = _write_buffer; + _outsize = size; + set_pollout (); + } +} + +void zmq::ws_engine_t::plug_internal () +{ + start_ws_handshake (); + set_pollin (); + in_event (); +} + +int zmq::ws_engine_t::routing_id_msg (msg_t *msg_) +{ + const int rc = msg_->init_size (_options.routing_id_size); + errno_assert (rc == 0); + 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; +} + +int zmq::ws_engine_t::process_routing_id_msg (msg_t *msg_) +{ + if (_options.recv_routing_id) { + msg_->set_flags (msg_t::routing_id); + const int rc = session ()->push_msg (msg_); + errno_assert (rc == 0); + } else { + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + } + + _process_msg = &ws_engine_t::push_msg_to_session; + + return 0; +} + +bool zmq::ws_engine_t::select_protocol (const char *protocol_) +{ + if (_options.mechanism == ZMQ_NULL && (strcmp ("ZWS2.0", protocol_) == 0)) { + _next_msg = static_cast ( + &ws_engine_t::routing_id_msg); + _process_msg = static_cast ( + &ws_engine_t::process_routing_id_msg); + + // No mechanism in place, enabling heartbeat + if (_options.heartbeat_interval > 0 && !_has_heartbeat_timer) { + add_timer (_options.heartbeat_interval, heartbeat_ivl_timer_id); + _has_heartbeat_timer = true; + } + + return true; + } + if (_options.mechanism == ZMQ_NULL + && strcmp ("ZWS2.0/NULL", protocol_) == 0) { + _mechanism = new (std::nothrow) + null_mechanism_t (session (), _peer_address, _options); + alloc_assert (_mechanism); + return true; + } else if (_options.mechanism == ZMQ_PLAIN + && strcmp ("ZWS2.0/PLAIN", protocol_) == 0) { + if (_options.as_server) + _mechanism = new (std::nothrow) + plain_server_t (session (), _peer_address, _options); + else + _mechanism = + new (std::nothrow) plain_client_t (session (), _options); + alloc_assert (_mechanism); + return true; + } +#ifdef ZMQ_HAVE_CURVE + else if (_options.mechanism == ZMQ_CURVE + && strcmp ("ZWS2.0/CURVE", protocol_) == 0) { + if (_options.as_server) + _mechanism = new (std::nothrow) + curve_server_t (session (), _peer_address, _options, false); + else + _mechanism = + new (std::nothrow) curve_client_t (session (), _options, false); + alloc_assert (_mechanism); + return true; + } +#endif + + return false; +} + +bool zmq::ws_engine_t::handshake () +{ + bool complete; + + if (_client) + complete = client_handshake (); + else + complete = server_handshake (); + + if (complete) { + _encoder = + new (std::nothrow) ws_encoder_t (_options.out_batch_size, _client); + alloc_assert (_encoder); + + _decoder = new (std::nothrow) + ws_decoder_t (_options.in_batch_size, _options.maxmsgsize, + _options.zero_copy, !_client); + alloc_assert (_decoder); + + socket ()->event_handshake_succeeded (_endpoint_uri_pair, 0); + + set_pollout (); + } + + return complete; +} + +bool zmq::ws_engine_t::server_handshake () +{ + const int nbytes = read (_read_buffer, WS_BUFFER_SIZE); + if (nbytes == -1) { + if (errno != EAGAIN) + error (zmq::i_engine::connection_error); + return false; + } + + _inpos = _read_buffer; + _insize = nbytes; + + while (_insize > 0) { + const char c = static_cast (*_inpos); + + switch (_server_handshake_state) { + case handshake_initial: + if (c == 'G') + _server_handshake_state = request_line_G; + else + _server_handshake_state = handshake_error; + break; + case request_line_G: + if (c == 'E') + _server_handshake_state = request_line_GE; + else + _server_handshake_state = handshake_error; + break; + case request_line_GE: + if (c == 'T') + _server_handshake_state = request_line_GET; + else + _server_handshake_state = handshake_error; + break; + case request_line_GET: + if (c == ' ') + _server_handshake_state = request_line_GET_space; + else + _server_handshake_state = handshake_error; + break; + case request_line_GET_space: + if (c == '\r' || c == '\n') + _server_handshake_state = handshake_error; + // TODO: instead of check what is not allowed check what is allowed + if (c != ' ') + _server_handshake_state = request_line_resource; + else + _server_handshake_state = request_line_GET_space; + break; + case request_line_resource: + if (c == '\r' || c == '\n') + _server_handshake_state = handshake_error; + else if (c == ' ') + _server_handshake_state = request_line_resource_space; + else + _server_handshake_state = request_line_resource; + break; + case request_line_resource_space: + if (c == 'H') + _server_handshake_state = request_line_H; + else + _server_handshake_state = handshake_error; + break; + case request_line_H: + if (c == 'T') + _server_handshake_state = request_line_HT; + else + _server_handshake_state = handshake_error; + break; + case request_line_HT: + if (c == 'T') + _server_handshake_state = request_line_HTT; + else + _server_handshake_state = handshake_error; + break; + case request_line_HTT: + if (c == 'P') + _server_handshake_state = request_line_HTTP; + else + _server_handshake_state = handshake_error; + break; + case request_line_HTTP: + if (c == '/') + _server_handshake_state = request_line_HTTP_slash; + else + _server_handshake_state = handshake_error; + break; + case request_line_HTTP_slash: + if (c == '1') + _server_handshake_state = request_line_HTTP_slash_1; + else + _server_handshake_state = handshake_error; + break; + case request_line_HTTP_slash_1: + if (c == '.') + _server_handshake_state = request_line_HTTP_slash_1_dot; + else + _server_handshake_state = handshake_error; + break; + case request_line_HTTP_slash_1_dot: + if (c == '1') + _server_handshake_state = request_line_HTTP_slash_1_dot_1; + else + _server_handshake_state = handshake_error; + break; + case request_line_HTTP_slash_1_dot_1: + if (c == '\r') + _server_handshake_state = request_line_cr; + else + _server_handshake_state = handshake_error; + break; + case request_line_cr: + if (c == '\n') + _server_handshake_state = header_field_begin_name; + else + _server_handshake_state = handshake_error; + break; + case header_field_begin_name: + switch (c) { + case '\r': + _server_handshake_state = handshake_end_line_cr; + break; + case '\n': + _server_handshake_state = handshake_error; + break; + default: + _header_name[0] = c; + _header_name_position = 1; + _server_handshake_state = header_field_name; + break; + } + break; + case header_field_name: + if (c == '\r' || c == '\n') + _server_handshake_state = handshake_error; + else if (c == ':') { + _header_name[_header_name_position] = '\0'; + _server_handshake_state = header_field_colon; + } else if (_header_name_position + 1 > MAX_HEADER_NAME_LENGTH) + _server_handshake_state = handshake_error; + else { + _header_name[_header_name_position] = c; + _header_name_position++; + _server_handshake_state = header_field_name; + } + break; + case header_field_colon: + case header_field_value_trailing_space: + if (c == '\n') + _server_handshake_state = handshake_error; + else if (c == '\r') + _server_handshake_state = header_field_cr; + else if (c == ' ') + _server_handshake_state = header_field_value_trailing_space; + else { + _header_value[0] = c; + _header_value_position = 1; + _server_handshake_state = header_field_value; + } + break; + case header_field_value: + if (c == '\n') + _server_handshake_state = handshake_error; + else if (c == '\r') { + _header_value[_header_value_position] = '\0'; + + if (strcasecmp ("upgrade", _header_name) == 0) + _header_upgrade_websocket = + strcasecmp ("websocket", _header_value) == 0; + else if (strcasecmp ("connection", _header_name) == 0) + _header_connection_upgrade = + strcasecmp ("upgrade", _header_value) == 0; + else if (strcasecmp ("Sec-WebSocket-Key", _header_name) + == 0) + strcpy_s (_websocket_key, _header_value); + else if (strcasecmp ("Sec-WebSocket-Protocol", _header_name) + == 0) { + // Currently only the ZWS2.0 is supported + // Sec-WebSocket-Protocol can appear multiple times or be a comma separated list + // if _websocket_protocol is already set we skip the check + if (_websocket_protocol[0] == '\0') { + char *rest = 0; + char *p = strtok_r (_header_value, ",", &rest); + while (p != NULL) { + if (*p == ' ') + p++; + + if (select_protocol (p)) { + strcpy_s (_websocket_protocol, p); + break; + } + + p = strtok_r (NULL, ",", &rest); + } + } + } + + _server_handshake_state = header_field_cr; + } else if (_header_value_position + 1 > MAX_HEADER_VALUE_LENGTH) + _server_handshake_state = handshake_error; + else { + _header_value[_header_value_position] = c; + _header_value_position++; + _server_handshake_state = header_field_value; + } + break; + case header_field_cr: + if (c == '\n') + _server_handshake_state = header_field_begin_name; + else + _server_handshake_state = handshake_error; + break; + case handshake_end_line_cr: + if (c == '\n') { + if (_header_connection_upgrade && _header_upgrade_websocket + && _websocket_protocol[0] != '\0' + && _websocket_key[0] != '\0') { + _server_handshake_state = handshake_complete; + + unsigned char hash[SHA_DIGEST_LENGTH]; + compute_accept_key (_websocket_key, hash); + + const int accept_key_len = encode_base64 ( + hash, SHA_DIGEST_LENGTH, _websocket_accept, + MAX_HEADER_VALUE_LENGTH); + assert (accept_key_len > 0); + _websocket_accept[accept_key_len] = '\0'; + + const int written = + snprintf (reinterpret_cast (_write_buffer), + WS_BUFFER_SIZE, + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "Sec-WebSocket-Protocol: %s\r\n" + "\r\n", + _websocket_accept, _websocket_protocol); + assert (written >= 0 && written < WS_BUFFER_SIZE); + _outpos = _write_buffer; + _outsize = written; + + _inpos++; + _insize--; + + return true; + } + _server_handshake_state = handshake_error; + } else + _server_handshake_state = handshake_error; + break; + default: + assert (false); + } + + _inpos++; + _insize--; + + if (_server_handshake_state == handshake_error) { + // TODO: send bad request + + socket ()->event_handshake_failed_protocol ( + _endpoint_uri_pair, ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED); + + error (zmq::i_engine::protocol_error); + return false; + } + } + return false; +} + +bool zmq::ws_engine_t::client_handshake () +{ + const int nbytes = read (_read_buffer, WS_BUFFER_SIZE); + if (nbytes == -1) { + if (errno != EAGAIN) + error (zmq::i_engine::connection_error); + return false; + } + + _inpos = _read_buffer; + _insize = nbytes; + + while (_insize > 0) { + const char c = static_cast (*_inpos); + + switch (_client_handshake_state) { + case client_handshake_initial: + if (c == 'H') + _client_handshake_state = response_line_H; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_H: + if (c == 'T') + _client_handshake_state = response_line_HT; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HT: + if (c == 'T') + _client_handshake_state = response_line_HTT; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HTT: + if (c == 'P') + _client_handshake_state = response_line_HTTP; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HTTP: + if (c == '/') + _client_handshake_state = response_line_HTTP_slash; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HTTP_slash: + if (c == '1') + _client_handshake_state = response_line_HTTP_slash_1; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HTTP_slash_1: + if (c == '.') + _client_handshake_state = response_line_HTTP_slash_1_dot; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HTTP_slash_1_dot: + if (c == '1') + _client_handshake_state = response_line_HTTP_slash_1_dot_1; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HTTP_slash_1_dot_1: + if (c == ' ') + _client_handshake_state = + response_line_HTTP_slash_1_dot_1_space; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_HTTP_slash_1_dot_1_space: + if (c == ' ') + _client_handshake_state = + response_line_HTTP_slash_1_dot_1_space; + else if (c == '1') + _client_handshake_state = response_line_status_1; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_status_1: + if (c == '0') + _client_handshake_state = response_line_status_10; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_status_10: + if (c == '1') + _client_handshake_state = response_line_status_101; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_status_101: + if (c == ' ') + _client_handshake_state = response_line_status_101_space; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_status_101_space: + if (c == ' ') + _client_handshake_state = response_line_status_101_space; + else if (c == 'S') + _client_handshake_state = response_line_s; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_s: + if (c == 'w') + _client_handshake_state = response_line_sw; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_sw: + if (c == 'i') + _client_handshake_state = response_line_swi; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_swi: + if (c == 't') + _client_handshake_state = response_line_swit; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_swit: + if (c == 'c') + _client_handshake_state = response_line_switc; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_switc: + if (c == 'h') + _client_handshake_state = response_line_switch; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_switch: + if (c == 'i') + _client_handshake_state = response_line_switchi; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_switchi: + if (c == 'n') + _client_handshake_state = response_line_switchin; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_switchin: + if (c == 'g') + _client_handshake_state = response_line_switching; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_switching: + if (c == ' ') + _client_handshake_state = response_line_switching_space; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_switching_space: + if (c == 'P') + _client_handshake_state = response_line_p; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_p: + if (c == 'r') + _client_handshake_state = response_line_pr; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_pr: + if (c == 'o') + _client_handshake_state = response_line_pro; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_pro: + if (c == 't') + _client_handshake_state = response_line_prot; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_prot: + if (c == 'o') + _client_handshake_state = response_line_proto; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_proto: + if (c == 'c') + _client_handshake_state = response_line_protoc; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_protoc: + if (c == 'o') + _client_handshake_state = response_line_protoco; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_protoco: + if (c == 'l') + _client_handshake_state = response_line_protocol; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_protocol: + if (c == 's') + _client_handshake_state = response_line_protocols; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_protocols: + if (c == '\r') + _client_handshake_state = response_line_cr; + else + _client_handshake_state = client_handshake_error; + break; + case response_line_cr: + if (c == '\n') + _client_handshake_state = client_header_field_begin_name; + else + _client_handshake_state = client_handshake_error; + break; + case client_header_field_begin_name: + switch (c) { + case '\r': + _client_handshake_state = client_handshake_end_line_cr; + break; + case '\n': + _client_handshake_state = client_handshake_error; + break; + default: + _header_name[0] = c; + _header_name_position = 1; + _client_handshake_state = client_header_field_name; + break; + } + break; + case client_header_field_name: + if (c == '\r' || c == '\n') + _client_handshake_state = client_handshake_error; + else if (c == ':') { + _header_name[_header_name_position] = '\0'; + _client_handshake_state = client_header_field_colon; + } else if (_header_name_position + 1 > MAX_HEADER_NAME_LENGTH) + _client_handshake_state = client_handshake_error; + else { + _header_name[_header_name_position] = c; + _header_name_position++; + _client_handshake_state = client_header_field_name; + } + break; + case client_header_field_colon: + case client_header_field_value_trailing_space: + if (c == '\n') + _client_handshake_state = client_handshake_error; + else if (c == '\r') + _client_handshake_state = client_header_field_cr; + else if (c == ' ') + _client_handshake_state = + client_header_field_value_trailing_space; + else { + _header_value[0] = c; + _header_value_position = 1; + _client_handshake_state = client_header_field_value; + } + break; + case client_header_field_value: + if (c == '\n') + _client_handshake_state = client_handshake_error; + else if (c == '\r') { + _header_value[_header_value_position] = '\0'; + + if (strcasecmp ("upgrade", _header_name) == 0) + _header_upgrade_websocket = + strcasecmp ("websocket", _header_value) == 0; + else if (strcasecmp ("connection", _header_name) == 0) + _header_connection_upgrade = + strcasecmp ("upgrade", _header_value) == 0; + else if (strcasecmp ("Sec-WebSocket-Accept", _header_name) + == 0) + strcpy_s (_websocket_accept, _header_value); + else if (strcasecmp ("Sec-WebSocket-Protocol", _header_name) + == 0) { + if (_mechanism) { + _client_handshake_state = client_handshake_error; + break; + } + if (select_protocol (_header_value)) + strcpy_s (_websocket_protocol, _header_value); + } + _client_handshake_state = client_header_field_cr; + } else if (_header_value_position + 1 > MAX_HEADER_VALUE_LENGTH) + _client_handshake_state = client_handshake_error; + else { + _header_value[_header_value_position] = c; + _header_value_position++; + _client_handshake_state = client_header_field_value; + } + break; + case client_header_field_cr: + if (c == '\n') + _client_handshake_state = client_header_field_begin_name; + else + _client_handshake_state = client_handshake_error; + break; + case client_handshake_end_line_cr: + if (c == '\n') { + if (_header_connection_upgrade && _header_upgrade_websocket + && _websocket_protocol[0] != '\0' + && _websocket_accept[0] != '\0') { + _client_handshake_state = client_handshake_complete; + + // TODO: validate accept key + + _inpos++; + _insize--; + + return true; + } + _client_handshake_state = client_handshake_error; + } else + _client_handshake_state = client_handshake_error; + break; + default: + assert (false); + } + + _inpos++; + _insize--; + + if (_client_handshake_state == client_handshake_error) { + socket ()->event_handshake_failed_protocol ( + _endpoint_uri_pair, ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED); + + error (zmq::i_engine::protocol_error); + return false; + } + } + + return false; +} + +int zmq::ws_engine_t::decode_and_push (msg_t *msg_) +{ + zmq_assert (_mechanism != NULL); + + // with WS engine, ping and pong commands are control messages and should not go through any mechanism + if (msg_->is_ping () || msg_->is_pong () || msg_->is_close_cmd ()) { + if (process_command_message (msg_) == -1) + return -1; + } else if (_mechanism->decode (msg_) == -1) + return -1; + + if (_has_timeout_timer) { + _has_timeout_timer = false; + cancel_timer (heartbeat_timeout_timer_id); + } + + if (msg_->flags () & msg_t::command && !msg_->is_ping () + && !msg_->is_pong () && !msg_->is_close_cmd ()) + process_command_message (msg_); + + if (_metadata) + msg_->set_metadata (_metadata); + if (session ()->push_msg (msg_) == -1) { + if (errno == EAGAIN) + _process_msg = &ws_engine_t::push_one_then_decode_and_push; + return -1; + } + return 0; +} + +int zmq::ws_engine_t::produce_close_message (msg_t *msg_) +{ + int rc = msg_->move (_close_msg); + errno_assert (rc == 0); + + _next_msg = static_cast ( + &ws_engine_t::produce_no_msg_after_close); + + return rc; +} + +int zmq::ws_engine_t::produce_no_msg_after_close (msg_t *msg_) +{ + LIBZMQ_UNUSED (msg_); + _next_msg = static_cast ( + &ws_engine_t::close_connection_after_close); + + errno = EAGAIN; + return -1; +} + +int zmq::ws_engine_t::close_connection_after_close (msg_t *msg_) +{ + LIBZMQ_UNUSED (msg_); + error (connection_error); + errno = ECONNRESET; + return -1; +} + +int zmq::ws_engine_t::produce_ping_message (msg_t *msg_) +{ + int rc = msg_->init (); + errno_assert (rc == 0); + msg_->set_flags (msg_t::command | msg_t::ping); + + _next_msg = &ws_engine_t::pull_and_encode; + if (!_has_timeout_timer && _heartbeat_timeout > 0) { + add_timer (_heartbeat_timeout, heartbeat_timeout_timer_id); + _has_timeout_timer = true; + } + + return rc; +} + + +int zmq::ws_engine_t::produce_pong_message (msg_t *msg_) +{ + int rc = msg_->init (); + errno_assert (rc == 0); + msg_->set_flags (msg_t::command | msg_t::pong); + + _next_msg = &ws_engine_t::pull_and_encode; + return rc; +} + + +int zmq::ws_engine_t::process_command_message (msg_t *msg_) +{ + if (msg_->is_ping ()) { + _next_msg = static_cast ( + &ws_engine_t::produce_pong_message); + out_event (); + } else if (msg_->is_close_cmd ()) { + int rc = _close_msg.copy (*msg_); + errno_assert (rc == 0); + _next_msg = static_cast ( + &ws_engine_t::produce_close_message); + out_event (); + } + + return 0; +} + +static int +encode_base64 (const unsigned char *in_, int in_len_, char *out_, int out_len_) +{ + static const unsigned char base64enc_tab[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + int io = 0; + uint32_t v = 0; + int rem = 0; + + for (int ii = 0; ii < in_len_; ii++) { + unsigned char ch; + ch = in_[ii]; + v = (v << 8) | ch; + rem += 8; + while (rem >= 6) { + rem -= 6; + if (io >= out_len_) + return -1; /* truncation is failure */ + out_[io++] = base64enc_tab[(v >> rem) & 63]; + } + } + if (rem) { + v <<= (6 - rem); + if (io >= out_len_) + return -1; /* truncation is failure */ + out_[io++] = base64enc_tab[v & 63]; + } + while (io & 3) { + if (io >= out_len_) + return -1; /* truncation is failure */ + out_[io++] = '='; + } + if (io >= out_len_) + return -1; /* no room for null terminator */ + out_[io] = 0; + return io; +} + +static void compute_accept_key (char *key_, unsigned char *hash_) +{ + const char *magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +#ifdef ZMQ_USE_NSS + unsigned int len; + HASH_HashType type = HASH_GetHashTypeByOidTag (SEC_OID_SHA1); + HASHContext *ctx = HASH_Create (type); + assert (ctx); + + HASH_Begin (ctx); + HASH_Update (ctx, (unsigned char *) key_, (unsigned int) strlen (key_)); + HASH_Update (ctx, (unsigned char *) magic_string, + (unsigned int) strlen (magic_string)); + HASH_End (ctx, hash_, &len, SHA_DIGEST_LENGTH); + HASH_Destroy (ctx); +#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 +} diff --git a/3rd/libzmq/src/ws_engine.hpp b/3rd/libzmq/src/ws_engine.hpp new file mode 100644 index 00000000..bdf145b8 --- /dev/null +++ b/3rd/libzmq/src/ws_engine.hpp @@ -0,0 +1,182 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WS_ENGINE_HPP_INCLUDED__ +#define __ZMQ_WS_ENGINE_HPP_INCLUDED__ + +#include "io_object.hpp" +#include "address.hpp" +#include "msg.hpp" +#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 + +namespace zmq +{ +class io_thread_t; +class session_base_t; + +typedef enum +{ + handshake_initial = 0, + request_line_G, + request_line_GE, + request_line_GET, + request_line_GET_space, + request_line_resource, + request_line_resource_space, + request_line_H, + request_line_HT, + request_line_HTT, + request_line_HTTP, + request_line_HTTP_slash, + request_line_HTTP_slash_1, + request_line_HTTP_slash_1_dot, + request_line_HTTP_slash_1_dot_1, + request_line_cr, + header_field_begin_name, + header_field_name, + header_field_colon, + header_field_value_trailing_space, + header_field_value, + header_field_cr, + handshake_end_line_cr, + handshake_complete, + + handshake_error = -1 +} ws_server_handshake_state_t; + + +typedef enum +{ + client_handshake_initial = 0, + response_line_H, + response_line_HT, + response_line_HTT, + response_line_HTTP, + response_line_HTTP_slash, + response_line_HTTP_slash_1, + response_line_HTTP_slash_1_dot, + response_line_HTTP_slash_1_dot_1, + response_line_HTTP_slash_1_dot_1_space, + response_line_status_1, + response_line_status_10, + response_line_status_101, + response_line_status_101_space, + response_line_s, + response_line_sw, + response_line_swi, + response_line_swit, + response_line_switc, + response_line_switch, + response_line_switchi, + response_line_switchin, + response_line_switching, + response_line_switching_space, + response_line_p, + response_line_pr, + response_line_pro, + response_line_prot, + response_line_proto, + response_line_protoc, + response_line_protoco, + response_line_protocol, + response_line_protocols, + response_line_cr, + client_header_field_begin_name, + client_header_field_name, + client_header_field_colon, + client_header_field_value_trailing_space, + client_header_field_value, + client_header_field_cr, + client_handshake_end_line_cr, + client_handshake_complete, + + client_handshake_error = -1 +} ws_client_handshake_state_t; + +class ws_engine_t : public stream_engine_base_t +{ + public: + ws_engine_t (fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_, + const ws_address_t &address_, + bool client_); + ~ws_engine_t (); + + protected: + int decode_and_push (msg_t *msg_); + int process_command_message (msg_t *msg_); + int produce_pong_message (msg_t *msg_); + int produce_ping_message (msg_t *msg_); + bool handshake (); + void plug_internal (); + void start_ws_handshake (); + + private: + int routing_id_msg (msg_t *msg_); + int process_routing_id_msg (msg_t *msg_); + int produce_close_message (msg_t *msg_); + int produce_no_msg_after_close (msg_t *msg_); + int close_connection_after_close (msg_t *msg_); + + bool select_protocol (const char *protocol); + + bool client_handshake (); + bool server_handshake (); + + bool _client; + ws_address_t _address; + + ws_client_handshake_state_t _client_handshake_state; + ws_server_handshake_state_t _server_handshake_state; + + unsigned char _read_buffer[WS_BUFFER_SIZE]; + unsigned char _write_buffer[WS_BUFFER_SIZE]; + char _header_name[MAX_HEADER_NAME_LENGTH + 1]; + int _header_name_position; + char _header_value[MAX_HEADER_VALUE_LENGTH + 1]; + int _header_value_position; + + bool _header_upgrade_websocket; + bool _header_connection_upgrade; + char _websocket_protocol[256]; + char _websocket_key[MAX_HEADER_VALUE_LENGTH + 1]; + char _websocket_accept[MAX_HEADER_VALUE_LENGTH + 1]; + + int _heartbeat_timeout; + msg_t _close_msg; +}; +} + +#endif diff --git a/3rd/libzmq/src/ws_listener.cpp b/3rd/libzmq/src/ws_listener.cpp new file mode 100644 index 00000000..2c7978ba --- /dev/null +++ b/3rd/libzmq/src/ws_listener.cpp @@ -0,0 +1,339 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include +#include + +#include "ws_listener.hpp" +#include "io_thread.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "tcp.hpp" +#include "socket_base.hpp" +#include "address.hpp" +#include "ws_engine.hpp" +#include "session_base.hpp" + +#ifdef ZMQ_HAVE_WSS +#include "wss_engine.hpp" +#include "wss_address.hpp" +#endif + +#ifndef ZMQ_HAVE_WINDOWS +#include +#include +#include +#include +#include +#include +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#endif + +#ifdef ZMQ_HAVE_OPENVMS +#include +#endif + +zmq::ws_listener_t::ws_listener_t (io_thread_t *io_thread_, + socket_base_t *socket_, + 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); + zmq_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); + zmq_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 () +{ + const fd_t fd = accept (); + + // If connection was reset by the peer in the meantime, just ignore it. + // TODO: Handle specific errors like ENFILE/EMFILE etc. + if (fd == retired_fd) { + _socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); + return; + } + + int rc = tune_tcp_socket (fd); + rc = rc | tune_tcp_maxrt (fd, options.tcp_maxrt); + if (rc != 0) { + _socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); + return; + } + + // Create the engine object for this connection. + create_engine (fd); +} + +std::string zmq::ws_listener_t::get_socket_name (zmq::fd_t fd_, + socket_end_t socket_end_) const +{ + std::string socket_name; + +#ifdef ZMQ_HAVE_WSS + if (_wss) + socket_name = zmq::get_socket_name (fd_, socket_end_); + else +#endif + socket_name = zmq::get_socket_name (fd_, socket_end_); + + return socket_name + _address.path (); +} + +int zmq::ws_listener_t::create_socket (const char *addr_) +{ + tcp_address_t address; + _s = tcp_open_socket (addr_, options, true, true, &address); + if (_s == retired_fd) { + return -1; + } + + // TODO why is this only done for the listener? + make_socket_noninheritable (_s); + + // Allow reusing of the address. + int flag = 1; + int rc; +#ifdef ZMQ_HAVE_WINDOWS + // TODO this was changed for Windows from SO_REUSEADDRE to + // SE_EXCLUSIVEADDRUSE by 0ab65324195ad70205514d465b03d851a6de051c, + // so the comment above is no longer correct; also, now the settings are + // different between listener and connecter with a src address. + // is this intentional? + rc = setsockopt (_s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast (&flag), sizeof (int)); + wsa_assert (rc != SOCKET_ERROR); +#elif defined ZMQ_HAVE_VXWORKS + rc = + setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof (int)); + errno_assert (rc == 0); +#else + rc = setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); + errno_assert (rc == 0); +#endif + + // Bind the socket to the network interface and port. +#if defined ZMQ_HAVE_VXWORKS + rc = bind (_s, (sockaddr *) _address.addr (), _address.addrlen ()); +#else + rc = bind (_s, address.addr (), address.addrlen ()); +#endif +#ifdef ZMQ_HAVE_WINDOWS + if (rc == SOCKET_ERROR) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error; + } +#else + if (rc != 0) + goto error; +#endif + + // Listen for incoming connections. + rc = listen (_s, options.backlog); +#ifdef ZMQ_HAVE_WINDOWS + if (rc == SOCKET_ERROR) { + errno = wsa_error_to_errno (WSAGetLastError ()); + goto error; + } +#else + if (rc != 0) + goto error; +#endif + + return 0; + +error: + const int err = errno; + close (); + errno = err; + return -1; +} + +int zmq::ws_listener_t::set_local_address (const char *addr_) +{ + if (options.use_fd != -1) { + // in this case, the addr_ passed is not used and ignored, since the + // socket was already created by the application + _s = options.use_fd; + } else { + const int rc = _address.resolve (addr_, true, options.ipv6); + if (rc != 0) + return -1; + + // remove the path, otherwise resolving the port will fail with wildcard + const char *delim = strrchr (addr_, '/'); + std::string host_address; + if (delim) { + host_address = std::string (addr_, delim - addr_); + } else { + host_address = addr_; + } + + if (create_socket (host_address.c_str ()) == -1) + return -1; + } + + _endpoint = get_socket_name (_s, socket_end_local); + + _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint), + _s); + return 0; +} + +zmq::fd_t zmq::ws_listener_t::accept () +{ + // The situation where connection cannot be accepted due to insufficient + // resources is considered valid and treated by ignoring the connection. + // Accept one connection and deal with different failure modes. + zmq_assert (_s != retired_fd); + + struct sockaddr_storage ss; + memset (&ss, 0, sizeof (ss)); +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int ss_len = sizeof (ss); +#else + socklen_t ss_len = sizeof (ss); +#endif +#if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4 + fd_t sock = ::accept4 (_s, reinterpret_cast (&ss), + &ss_len, SOCK_CLOEXEC); +#else + const fd_t sock = + ::accept (_s, reinterpret_cast (&ss), &ss_len); +#endif + + if (sock == retired_fd) { +#if defined ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET + || last_error == WSAEMFILE || last_error == WSAENOBUFS); +#elif defined ZMQ_HAVE_ANDROID + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR + || errno == ECONNABORTED || errno == EPROTO + || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE + || errno == ENFILE || errno == EINVAL); +#else + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR + || errno == ECONNABORTED || errno == EPROTO + || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE + || errno == ENFILE); +#endif + return retired_fd; + } + + make_socket_noninheritable (sock); + + if (zmq::set_nosigpipe (sock)) { +#ifdef ZMQ_HAVE_WINDOWS + const int rc = closesocket (sock); + wsa_assert (rc != SOCKET_ERROR); +#else + int rc = ::close (sock); + errno_assert (rc == 0); +#endif + return retired_fd; + } + + // Set the IP Type-Of-Service priority for this client socket + if (options.tos != 0) + set_ip_type_of_service (sock, options.tos); + + // Set the protocol-defined priority for this client socket + if (options.priority != 0) + set_socket_priority (sock, options.priority); + + return sock; +} + +void zmq::ws_listener_t::create_engine (fd_t fd_) +{ + const endpoint_uri_pair_t endpoint_pair ( + get_socket_name (fd_, socket_end_local), + get_socket_name (fd_, socket_end_remote), endpoint_type_bind); + + 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, + std::string ()); +#else + zmq_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 + // running in an I/O thread, there must be at least one available. + io_thread_t *io_thread = choose_io_thread (options.affinity); + zmq_assert (io_thread); + + // Create and launch a session object. + session_base_t *session = + session_base_t::create (io_thread, false, _socket, options, NULL); + errno_assert (session); + session->inc_seqnum (); + launch_child (session); + send_attach (session, engine, false); + + _socket->event_accepted (endpoint_pair, fd_); +} diff --git a/3rd/libzmq/src/ws_listener.hpp b/3rd/libzmq/src/ws_listener.hpp new file mode 100644 index 00000000..88f3b08b --- /dev/null +++ b/3rd/libzmq/src/ws_listener.hpp @@ -0,0 +1,84 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WS_LISTENER_HPP_INCLUDED__ +#define __ZMQ_WS_LISTENER_HPP_INCLUDED__ + +#include "fd.hpp" +#include "ws_address.hpp" +#include "stream_listener_base.hpp" + +#ifdef ZMQ_USE_GNUTLS +#include +#endif + +namespace zmq +{ +class ws_listener_t ZMQ_FINAL : public stream_listener_base_t +{ + public: + ws_listener_t (zmq::io_thread_t *io_thread_, + zmq::socket_base_t *socket_, + const options_t &options_, + bool wss_); + + ~ws_listener_t (); + + // Set address to listen on. + int set_local_address (const char *addr_); + + protected: + std::string get_socket_name (fd_t fd_, socket_end_t socket_end_) const; + void create_engine (fd_t fd); + + private: + // Handlers for I/O events. + void in_event (); + + // Accept the new connection. Returns the file descriptor of the + // newly created connection. The function may return retired_fd + // if the connection was dropped while waiting in the listen backlog + // or was denied because of accept filters. + fd_t accept (); + + int create_socket (const char *addr_); + + // Address to listen on. + ws_address_t _address; + + bool _wss; +#ifdef ZMQ_HAVE_WSS + gnutls_certificate_credentials_t _tls_cred; +#endif + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ws_listener_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/ws_protocol.hpp b/3rd/libzmq/src/ws_protocol.hpp new file mode 100644 index 00000000..b4b7e81f --- /dev/null +++ b/3rd/libzmq/src/ws_protocol.hpp @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WS_PROTOCOL_HPP_INCLUDED__ +#define __ZMQ_WS_PROTOCOL_HPP_INCLUDED__ + +namespace zmq +{ +// Definition of constants for WS transport protocol. +class ws_protocol_t +{ + public: + // Message flags. + enum opcode_t + { + opcode_continuation = 0, + opcode_text = 0x01, + opcode_binary = 0x02, + opcode_close = 0x08, + opcode_ping = 0x09, + opcode_pong = 0xA + }; + + enum + { + more_flag = 1, + command_flag = 2 + }; +}; +} + +#endif diff --git a/3rd/libzmq/src/wss_address.cpp b/3rd/libzmq/src/wss_address.cpp new file mode 100644 index 00000000..abfec9eb --- /dev/null +++ b/3rd/libzmq/src/wss_address.cpp @@ -0,0 +1,53 @@ +/* + Copyright (c) 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 . +*/ + +#include "precompiled.hpp" +#include +#include + +#include "wss_address.hpp" + +zmq::wss_address_t::wss_address_t () : ws_address_t () +{ +} + +zmq::wss_address_t::wss_address_t (const sockaddr *sa_, socklen_t sa_len_) : + ws_address_t (sa_, sa_len_) +{ +} + +int zmq::wss_address_t::to_string (std::string &addr_) const +{ + std::ostringstream os; + os << std::string ("wss://") << host () << std::string (":") + << _address.port () << path (); + addr_ = os.str (); + + return 0; +} diff --git a/3rd/libzmq/src/wss_address.hpp b/3rd/libzmq/src/wss_address.hpp new file mode 100644 index 00000000..ec6a9033 --- /dev/null +++ b/3rd/libzmq/src/wss_address.hpp @@ -0,0 +1,47 @@ +/* + Copyright (c) 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 . +*/ + +#ifndef __ZMQ_WSS_ADDRESS_HPP_INCLUDED__ +#define __ZMQ_WSS_ADDRESS_HPP_INCLUDED__ + +#include "ws_address.hpp" + +namespace zmq +{ +class wss_address_t : public ws_address_t +{ + public: + wss_address_t (); + wss_address_t (const sockaddr *sa_, socklen_t sa_len_); + // The opposite to resolve() + int to_string (std::string &addr_) const; +}; +} + +#endif diff --git a/3rd/libzmq/src/wss_engine.cpp b/3rd/libzmq/src/wss_engine.cpp new file mode 100644 index 00000000..986a6a4f --- /dev/null +++ b/3rd/libzmq/src/wss_engine.cpp @@ -0,0 +1,227 @@ +/* +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 . +*/ + +#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); + zmq_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 std::string &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); + zmq_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); + zmq_assert (rc >= 0); + } + + gnutls_certificate_set_verify_function (_tls_client_cred, + verify_certificate_callback); + + rc = gnutls_init (&_tls_session, GNUTLS_CLIENT | GNUTLS_NONBLOCK); + zmq_assert (rc == GNUTLS_E_SUCCESS); + + if (!hostname_.empty ()) + gnutls_server_name_set (_tls_session, GNUTLS_NAME_DNS, + hostname_.c_str (), hostname_.size ()); + + gnutls_session_set_ptr ( + _tls_session, + hostname_.empty () ? NULL : const_cast (hostname_.c_str ())); + + rc = gnutls_credentials_set (_tls_session, GNUTLS_CRD_CERTIFICATE, + _tls_client_cred); + zmq_assert (rc == GNUTLS_E_SUCCESS); + } else { + zmq_assert (tls_server_cred_); + + rc = gnutls_init (&_tls_session, GNUTLS_SERVER | GNUTLS_NONBLOCK); + zmq_assert (rc == GNUTLS_E_SUCCESS); + + rc = gnutls_credentials_set (_tls_session, GNUTLS_CRD_CERTIFICATE, + tls_server_cred_); + zmq_assert (rc == GNUTLS_E_SUCCESS); + } + + gnutls_set_default_priority (_tls_session); + gnutls_transport_set_int (_tls_session, fd_); +} + +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 (); +} + +void zmq::wss_engine_t::out_event () +{ + if (_established) + return ws_engine_t::out_event (); + + do_handshake (); +} + +bool zmq::wss_engine_t::do_handshake () +{ + int rc = gnutls_handshake (_tls_session); + + reset_pollout (); + + if (rc == GNUTLS_E_SUCCESS) { + start_ws_handshake (); + _established = true; + return false; + } else if (rc == GNUTLS_E_AGAIN) { + int direction = gnutls_record_get_direction (_tls_session); + if (direction == 1) + set_pollout (); + + return false; + } else if (rc == GNUTLS_E_INTERRUPTED + || rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { + return false; + } else { + error (zmq::i_engine::connection_error); + return false; + } + + return true; +} + +bool zmq::wss_engine_t::handshake () +{ + if (!_established) { + if (!do_handshake ()) { + 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; + } + + if (rc < 0) { + errno = EINVAL; + 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; + } + + if (rc < 0) { + errno = EINVAL; + return -1; + } + + // TODO: change return type to ssize_t (signed) + return rc; +} diff --git a/3rd/libzmq/src/wss_engine.hpp b/3rd/libzmq/src/wss_engine.hpp new file mode 100644 index 00000000..0280c38a --- /dev/null +++ b/3rd/libzmq/src/wss_engine.hpp @@ -0,0 +1,69 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_WSS_ENGINE_HPP_INCLUDED__ +#define __ZMQ_WSS_ENGINE_HPP_INCLUDED__ + +#include +#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 std::string &hostname_); + ~wss_engine_t (); + + void out_event (); + + protected: + bool handshake (); + void plug_internal (); + int read (void *data, size_t size_); + int write (const void *data_, size_t size_); + + private: + bool do_handshake (); + + bool _established; + gnutls_certificate_credentials_t _tls_client_cred; + gnutls_session_t _tls_session; +}; +} + +#endif diff --git a/3rd/libzmq/src/xpub.cpp b/3rd/libzmq/src/xpub.cpp new file mode 100644 index 00000000..e806b8e4 --- /dev/null +++ b/3rd/libzmq/src/xpub.cpp @@ -0,0 +1,399 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include "xpub.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "msg.hpp" +#include "macros.hpp" +#include "generic_mtrie_impl.hpp" + +zmq::xpub_t::xpub_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_), + _verbose_subs (false), + _verbose_unsubs (false), + _more_send (false), + _more_recv (false), + _process_subscribe (false), + _only_first_subscribe (false), + _lossy (true), + _manual (false), + _send_last_pipe (false), + _pending_pipes (), + _welcome_msg () +{ + _last_pipe = NULL; + options.type = ZMQ_XPUB; + _welcome_msg.init (); +} + +zmq::xpub_t::~xpub_t () +{ + _welcome_msg.close (); + for (std::deque::iterator it = _pending_metadata.begin (), + end = _pending_metadata.end (); + it != end; ++it) + if (*it && (*it)->drop_ref ()) + LIBZMQ_DELETE (*it); +} + +void zmq::xpub_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + _dist.attach (pipe_); + + // If subscribe_to_all_ is specified, the caller would like to subscribe + // to all data on this pipe, implicitly. + if (subscribe_to_all_) + _subscriptions.add (NULL, 0, pipe_); + + // if welcome message exists, send a copy of it + if (_welcome_msg.size () > 0) { + msg_t copy; + copy.init (); + const int rc = copy.copy (_welcome_msg); + errno_assert (rc == 0); + const bool ok = pipe_->write (©); + zmq_assert (ok); + pipe_->flush (); + } + + // The pipe is active when attached. Let's read the subscriptions from + // it, if any. + xread_activated (pipe_); +} + +void zmq::xpub_t::xread_activated (pipe_t *pipe_) +{ + // There are some subscriptions waiting. Let's process them. + msg_t msg; + while (pipe_->read (&msg)) { + metadata_t *metadata = msg.metadata (); + unsigned char *msg_data = static_cast (msg.data ()), + *data = NULL; + size_t size = 0; + bool subscribe = false; + bool is_subscribe_or_cancel = false; + bool notify = false; + + const bool first_part = !_more_recv; + _more_recv = (msg.flags () & msg_t::more) != 0; + + if (first_part || _process_subscribe) { + // Apply the subscription to the trie + if (msg.is_subscribe () || msg.is_cancel ()) { + data = static_cast (msg.command_body ()); + size = msg.command_body_size (); + subscribe = msg.is_subscribe (); + is_subscribe_or_cancel = true; + } else if (msg.size () > 0 && (*msg_data == 0 || *msg_data == 1)) { + data = msg_data + 1; + size = msg.size () - 1; + subscribe = *msg_data == 1; + is_subscribe_or_cancel = true; + } + } + + if (first_part) + _process_subscribe = + !_only_first_subscribe || is_subscribe_or_cancel; + + if (is_subscribe_or_cancel) { + if (_manual) { + // Store manual subscription to use on termination + if (!subscribe) + _manual_subscriptions.rm (data, size, pipe_); + else + _manual_subscriptions.add (data, size, pipe_); + + _pending_pipes.push_back (pipe_); + } else { + if (!subscribe) { + const mtrie_t::rm_result rm_result = + _subscriptions.rm (data, size, pipe_); + // TODO reconsider what to do if rm_result == mtrie_t::not_found + notify = + rm_result != mtrie_t::values_remain || _verbose_unsubs; + } else { + const bool first_added = + _subscriptions.add (data, size, pipe_); + notify = first_added || _verbose_subs; + } + } + + // If the request was a new subscription, or the subscription + // was removed, or verbose mode or manual mode are enabled, store it + // so that it can be passed to the user on next recv call. + if (_manual || (options.type == ZMQ_XPUB && notify)) { + // ZMTP 3.1 hack: we need to support sub/cancel commands, but + // we can't give them back to userspace as it would be an API + // breakage since the payload of the message is completely + // different. Manually craft an old-style message instead. + // Although with other transports it would be possible to simply + // reuse the same buffer and prefix a 0/1 byte to the topic, with + // inproc the subscribe/cancel command string is not present in + // the message, so this optimization is not possible. + // The pushback makes a copy of the data array anyway, so the + // number of buffer copies does not change. + blob_t notification (size + 1); + if (subscribe) + *notification.data () = 1; + else + *notification.data () = 0; + memcpy (notification.data () + 1, data, size); + + _pending_data.push_back (ZMQ_MOVE (notification)); + if (metadata) + metadata->add_ref (); + _pending_metadata.push_back (metadata); + _pending_flags.push_back (0); + } + } else if (options.type != ZMQ_PUB) { + // Process user message coming upstream from xsub socket, + // but not if the type is PUB, which never processes user + // messages + _pending_data.push_back (blob_t (msg_data, msg.size ())); + if (metadata) + metadata->add_ref (); + _pending_metadata.push_back (metadata); + _pending_flags.push_back (msg.flags ()); + } + + msg.close (); + } +} + +void zmq::xpub_t::xwrite_activated (pipe_t *pipe_) +{ + _dist.activated (pipe_); +} + +int zmq::xpub_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + if (option_ == ZMQ_XPUB_VERBOSE || option_ == ZMQ_XPUB_VERBOSER + || option_ == ZMQ_XPUB_MANUAL_LAST_VALUE || option_ == ZMQ_XPUB_NODROP + || option_ == ZMQ_XPUB_MANUAL || option_ == ZMQ_ONLY_FIRST_SUBSCRIBE) { + if (optvallen_ != sizeof (int) + || *static_cast (optval_) < 0) { + errno = EINVAL; + return -1; + } + if (option_ == ZMQ_XPUB_VERBOSE) { + _verbose_subs = (*static_cast (optval_) != 0); + _verbose_unsubs = false; + } else if (option_ == ZMQ_XPUB_VERBOSER) { + _verbose_subs = (*static_cast (optval_) != 0); + _verbose_unsubs = _verbose_subs; + } else if (option_ == ZMQ_XPUB_MANUAL_LAST_VALUE) { + _manual = (*static_cast (optval_) != 0); + _send_last_pipe = _manual; + } else if (option_ == ZMQ_XPUB_NODROP) + _lossy = (*static_cast (optval_) == 0); + else if (option_ == ZMQ_XPUB_MANUAL) + _manual = (*static_cast (optval_) != 0); + else if (option_ == ZMQ_ONLY_FIRST_SUBSCRIBE) + _only_first_subscribe = (*static_cast (optval_) != 0); + } else if (option_ == ZMQ_SUBSCRIBE && _manual) { + if (_last_pipe != NULL) + _subscriptions.add ((unsigned char *) optval_, optvallen_, + _last_pipe); + } else if (option_ == ZMQ_UNSUBSCRIBE && _manual) { + if (_last_pipe != NULL) + _subscriptions.rm ((unsigned char *) optval_, optvallen_, + _last_pipe); + } else if (option_ == ZMQ_XPUB_WELCOME_MSG) { + _welcome_msg.close (); + + if (optvallen_ > 0) { + const int rc = _welcome_msg.init_size (optvallen_); + errno_assert (rc == 0); + + unsigned char *data = + static_cast (_welcome_msg.data ()); + memcpy (data, optval_, optvallen_); + } else + _welcome_msg.init (); + } else { + errno = EINVAL; + return -1; + } + return 0; +} + +static void stub (zmq::mtrie_t::prefix_t data_, size_t size_, void *arg_) +{ + LIBZMQ_UNUSED (data_); + LIBZMQ_UNUSED (size_); + LIBZMQ_UNUSED (arg_); +} + +void zmq::xpub_t::xpipe_terminated (pipe_t *pipe_) +{ + if (_manual) { + // Remove the pipe from the trie and send corresponding manual + // unsubscriptions upstream. + _manual_subscriptions.rm (pipe_, send_unsubscription, this, false); + // Remove pipe without actually sending the message as it was taken + // care of by the manual call above. subscriptions is the real mtrie, + // so the pipe must be removed from there or it will be left over. + _subscriptions.rm (pipe_, stub, static_cast (NULL), false); + } else { + // Remove the pipe from the trie. If there are topics that nobody + // is interested in anymore, send corresponding unsubscriptions + // upstream. + _subscriptions.rm (pipe_, send_unsubscription, this, !_verbose_unsubs); + } + + _dist.pipe_terminated (pipe_); +} + +void zmq::xpub_t::mark_as_matching (pipe_t *pipe_, xpub_t *self_) +{ + self_->_dist.match (pipe_); +} + +void zmq::xpub_t::mark_last_pipe_as_matching (pipe_t *pipe_, xpub_t *self_) +{ + if (self_->_last_pipe == pipe_) + self_->_dist.match (pipe_); +} + +int zmq::xpub_t::xsend (msg_t *msg_) +{ + const bool msg_more = (msg_->flags () & msg_t::more) != 0; + + // For the first part of multi-part message, find the matching pipes. + if (!_more_send) { + // Ensure nothing from previous failed attempt to send is left matched + _dist.unmatch (); + + if (unlikely (_manual && _last_pipe && _send_last_pipe)) { + _subscriptions.match (static_cast (msg_->data ()), + msg_->size (), mark_last_pipe_as_matching, + this); + _last_pipe = NULL; + } else + _subscriptions.match (static_cast (msg_->data ()), + msg_->size (), mark_as_matching, this); + // If inverted matching is used, reverse the selection now + if (options.invert_matching) { + _dist.reverse_match (); + } + } + + int rc = -1; // Assume we fail + if (_lossy || _dist.check_hwm ()) { + if (_dist.send_to_matching (msg_) == 0) { + // If we are at the end of multi-part message we can mark + // all the pipes as non-matching. + if (!msg_more) + _dist.unmatch (); + _more_send = msg_more; + rc = 0; // Yay, sent successfully + } + } else + errno = EAGAIN; + return rc; +} + +bool zmq::xpub_t::xhas_out () +{ + return _dist.has_out (); +} + +int zmq::xpub_t::xrecv (msg_t *msg_) +{ + // If there is at least one + if (_pending_data.empty ()) { + errno = EAGAIN; + return -1; + } + + // User is reading a message, set last_pipe and remove it from the deque + if (_manual && !_pending_pipes.empty ()) { + _last_pipe = _pending_pipes.front (); + _pending_pipes.pop_front (); + } + + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init_size (_pending_data.front ().size ()); + errno_assert (rc == 0); + memcpy (msg_->data (), _pending_data.front ().data (), + _pending_data.front ().size ()); + + // set metadata only if there is some + if (metadata_t *metadata = _pending_metadata.front ()) { + msg_->set_metadata (metadata); + // Remove ref corresponding to vector placement + metadata->drop_ref (); + } + + msg_->set_flags (_pending_flags.front ()); + _pending_data.pop_front (); + _pending_metadata.pop_front (); + _pending_flags.pop_front (); + return 0; +} + +bool zmq::xpub_t::xhas_in () +{ + return !_pending_data.empty (); +} + +void zmq::xpub_t::send_unsubscription (zmq::mtrie_t::prefix_t data_, + size_t size_, + xpub_t *self_) +{ + if (self_->options.type != ZMQ_PUB) { + // Place the unsubscription to the queue of pending (un)subscriptions + // to be retrieved by the user later on. + blob_t unsub (size_ + 1); + *unsub.data () = 0; + if (size_ > 0) + memcpy (unsub.data () + 1, data_, size_); + self_->_pending_data.ZMQ_PUSH_OR_EMPLACE_BACK (ZMQ_MOVE (unsub)); + self_->_pending_metadata.push_back (NULL); + self_->_pending_flags.push_back (0); + + if (self_->_manual) { + self_->_last_pipe = NULL; + self_->_pending_pipes.push_back (NULL); + } + } +} diff --git a/3rd/libzmq/src/xpub.hpp b/3rd/libzmq/src/xpub.hpp new file mode 100644 index 00000000..82504b0e --- /dev/null +++ b/3rd/libzmq/src/xpub.hpp @@ -0,0 +1,140 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_XPUB_HPP_INCLUDED__ +#define __ZMQ_XPUB_HPP_INCLUDED__ + +#include + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "mtrie.hpp" +#include "dist.hpp" + +namespace zmq +{ +class ctx_t; +class msg_t; +class pipe_t; +class io_thread_t; + +class xpub_t : public socket_base_t +{ + public: + xpub_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~xpub_t () ZMQ_OVERRIDE; + + // Implementations of virtual functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_ = false, + bool locally_initiated_ = false) ZMQ_OVERRIDE; + int xsend (zmq::msg_t *msg_) ZMQ_FINAL; + bool xhas_out () ZMQ_FINAL; + int xrecv (zmq::msg_t *msg_) ZMQ_OVERRIDE; + bool xhas_in () ZMQ_OVERRIDE; + void xread_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void xwrite_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + int + xsetsockopt (int option_, const void *optval_, size_t optvallen_) ZMQ_FINAL; + void xpipe_terminated (zmq::pipe_t *pipe_) ZMQ_FINAL; + + private: + // Function to be applied to the trie to send all the subscriptions + // upstream. + static void send_unsubscription (zmq::mtrie_t::prefix_t data_, + size_t size_, + xpub_t *self_); + + // Function to be applied to each matching pipes. + static void mark_as_matching (zmq::pipe_t *pipe_, xpub_t *self_); + + // List of all subscriptions mapped to corresponding pipes. + mtrie_t _subscriptions; + + // List of manual subscriptions mapped to corresponding pipes. + mtrie_t _manual_subscriptions; + + // Distributor of messages holding the list of outbound pipes. + dist_t _dist; + + // If true, send all subscription messages upstream, not just + // unique ones + bool _verbose_subs; + + // If true, send all unsubscription messages upstream, not just + // unique ones + bool _verbose_unsubs; + + // True if we are in the middle of sending a multi-part message. + bool _more_send; + + // True if we are in the middle of receiving a multi-part message. + bool _more_recv; + + // If true, subscribe and cancel messages are processed for the rest + // of multipart message. + bool _process_subscribe; + + // This option is enabled with ZMQ_ONLY_FIRST_SUBSCRIBE. + // If true, messages following subscribe/unsubscribe in a multipart + // message are treated as user data regardless of the first byte. + bool _only_first_subscribe; + + // Drop messages if HWM reached, otherwise return with EAGAIN + bool _lossy; + + // Subscriptions will not bed added automatically, only after calling set option with ZMQ_SUBSCRIBE or ZMQ_UNSUBSCRIBE + bool _manual; + + // Send message to the last pipe, only used if xpub is on manual and after calling set option with ZMQ_SUBSCRIBE + bool _send_last_pipe; + + // Function to be applied to match the last pipe. + static void mark_last_pipe_as_matching (zmq::pipe_t *pipe_, xpub_t *self_); + + // Last pipe that sent subscription message, only used if xpub is on manual + pipe_t *_last_pipe; + + // Pipes that sent subscriptions messages that have not yet been processed, only used if xpub is on manual + std::deque _pending_pipes; + + // Welcome message to send to pipe when attached + msg_t _welcome_msg; + + // List of pending (un)subscriptions, ie. those that were already + // applied to the trie, but not yet received by the user. + std::deque _pending_data; + std::deque _pending_metadata; + std::deque _pending_flags; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (xpub_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/xsub.cpp b/3rd/libzmq/src/xsub.cpp new file mode 100644 index 00000000..66c4d16b --- /dev/null +++ b/3rd/libzmq/src/xsub.cpp @@ -0,0 +1,279 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include + +#include "macros.hpp" +#include "xsub.hpp" +#include "err.hpp" + +zmq::xsub_t::xsub_t (class ctx_t *parent_, uint32_t tid_, int sid_) : + socket_base_t (parent_, tid_, sid_), + _has_message (false), + _more_send (false), + _more_recv (false), + _process_subscribe (false), + _only_first_subscribe (false) +{ + options.type = ZMQ_XSUB; + + // When socket is being closed down we don't want to wait till pending + // subscription commands are sent to the wire. + options.linger.store (0); + + const int rc = _message.init (); + errno_assert (rc == 0); +} + +zmq::xsub_t::~xsub_t () +{ + const int rc = _message.close (); + errno_assert (rc == 0); +} + +void zmq::xsub_t::xattach_pipe (pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) +{ + LIBZMQ_UNUSED (subscribe_to_all_); + LIBZMQ_UNUSED (locally_initiated_); + + zmq_assert (pipe_); + _fq.attach (pipe_); + _dist.attach (pipe_); + + // Send all the cached subscriptions to the new upstream peer. + _subscriptions.apply (send_subscription, pipe_); + pipe_->flush (); +} + +void zmq::xsub_t::xread_activated (pipe_t *pipe_) +{ + _fq.activated (pipe_); +} + +void zmq::xsub_t::xwrite_activated (pipe_t *pipe_) +{ + _dist.activated (pipe_); +} + +void zmq::xsub_t::xpipe_terminated (pipe_t *pipe_) +{ + _fq.pipe_terminated (pipe_); + _dist.pipe_terminated (pipe_); +} + +void zmq::xsub_t::xhiccuped (pipe_t *pipe_) +{ + // Send all the cached subscriptions to the hiccuped pipe. + _subscriptions.apply (send_subscription, pipe_); + pipe_->flush (); +} + +int zmq::xsub_t::xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) +{ + if (option_ == ZMQ_ONLY_FIRST_SUBSCRIBE) { + if (optvallen_ != sizeof (int) + || *static_cast (optval_) < 0) { + errno = EINVAL; + return -1; + } + _only_first_subscribe = (*static_cast (optval_) != 0); + return 0; + } + errno = EINVAL; + return -1; +} + +int zmq::xsub_t::xsend (msg_t *msg_) +{ + size_t size = msg_->size (); + unsigned char *data = static_cast (msg_->data ()); + + const bool first_part = !_more_send; + _more_send = (msg_->flags () & msg_t::more) != 0; + + if (first_part) { + _process_subscribe = !_only_first_subscribe; + } else if (!_process_subscribe) { + // User message sent upstream to XPUB socket + return _dist.send_to_all (msg_); + } + + if (msg_->is_subscribe () || (size > 0 && *data == 1)) { + // Process subscribe message + // This used to filter out duplicate subscriptions, + // however this is alread done on the XPUB side and + // doing it here as well breaks ZMQ_XPUB_VERBOSE + // when there are forwarding devices involved. + if (!msg_->is_subscribe ()) { + data = data + 1; + size = size - 1; + } + _subscriptions.add (data, size); + _process_subscribe = true; + return _dist.send_to_all (msg_); + } + if (msg_->is_cancel () || (size > 0 && *data == 0)) { + // Process unsubscribe message + if (!msg_->is_cancel ()) { + data = data + 1; + size = size - 1; + } + _process_subscribe = true; + if (_subscriptions.rm (data, size)) + return _dist.send_to_all (msg_); + } else + // User message sent upstream to XPUB socket + return _dist.send_to_all (msg_); + + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + + return 0; +} + +bool zmq::xsub_t::xhas_out () +{ + // Subscription can be added/removed anytime. + return true; +} + +int zmq::xsub_t::xrecv (msg_t *msg_) +{ + // If there's already a message prepared by a previous call to zmq_poll, + // return it straight ahead. + if (_has_message) { + const int rc = msg_->move (_message); + errno_assert (rc == 0); + _has_message = false; + _more_recv = (msg_->flags () & msg_t::more) != 0; + return 0; + } + + // TODO: This can result in infinite loop in the case of continuous + // stream of non-matching messages which breaks the non-blocking recv + // semantics. + while (true) { + // Get a message using fair queueing algorithm. + int rc = _fq.recv (msg_); + + // If there's no message available, return immediately. + // The same when error occurs. + if (rc != 0) + return -1; + + // Check whether the message matches at least one subscription. + // Non-initial parts of the message are passed + if (_more_recv || !options.filter || match (msg_)) { + _more_recv = (msg_->flags () & msg_t::more) != 0; + return 0; + } + + // Message doesn't match. Pop any remaining parts of the message + // from the pipe. + while (msg_->flags () & msg_t::more) { + rc = _fq.recv (msg_); + errno_assert (rc == 0); + } + } +} + +bool zmq::xsub_t::xhas_in () +{ + // There are subsequent parts of the partly-read message available. + if (_more_recv) + return true; + + // If there's already a message prepared by a previous call to zmq_poll, + // return straight ahead. + if (_has_message) + return true; + + // TODO: This can result in infinite loop in the case of continuous + // stream of non-matching messages. + while (true) { + // Get a message using fair queueing algorithm. + int rc = _fq.recv (&_message); + + // If there's no message available, return immediately. + // The same when error occurs. + if (rc != 0) { + errno_assert (errno == EAGAIN); + return false; + } + + // Check whether the message matches at least one subscription. + if (!options.filter || match (&_message)) { + _has_message = true; + return true; + } + + // Message doesn't match. Pop any remaining parts of the message + // from the pipe. + while (_message.flags () & msg_t::more) { + rc = _fq.recv (&_message); + errno_assert (rc == 0); + } + } +} + +bool zmq::xsub_t::match (msg_t *msg_) +{ + const bool matching = _subscriptions.check ( + static_cast (msg_->data ()), msg_->size ()); + + return matching ^ options.invert_matching; +} + +void zmq::xsub_t::send_subscription (unsigned char *data_, + size_t size_, + void *arg_) +{ + pipe_t *pipe = static_cast (arg_); + + // Create the subscription message. + msg_t msg; + const int rc = msg.init_subscribe (size_, data_); + errno_assert (rc == 0); + + // Send it to the pipe. + const bool sent = pipe->write (&msg); + // If we reached the SNDHWM, and thus cannot send the subscription, drop + // the subscription message instead. This matches the behaviour of + // zmq_setsockopt(ZMQ_SUBSCRIBE, ...), which also drops subscriptions + // when the SNDHWM is reached. + if (!sent) + msg.close (); +} diff --git a/3rd/libzmq/src/xsub.hpp b/3rd/libzmq/src/xsub.hpp new file mode 100644 index 00000000..6bbbce07 --- /dev/null +++ b/3rd/libzmq/src/xsub.hpp @@ -0,0 +1,120 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_XSUB_HPP_INCLUDED__ +#define __ZMQ_XSUB_HPP_INCLUDED__ + +#include "socket_base.hpp" +#include "session_base.hpp" +#include "dist.hpp" +#include "fq.hpp" +#ifdef ZMQ_USE_RADIX_TREE +#include "radix_tree.hpp" +#else +#include "trie.hpp" +#endif + +namespace zmq +{ +class ctx_t; +class pipe_t; +class io_thread_t; + +class xsub_t : public socket_base_t +{ + public: + xsub_t (zmq::ctx_t *parent_, uint32_t tid_, int sid_); + ~xsub_t () ZMQ_OVERRIDE; + + protected: + // Overrides of functions from socket_base_t. + void xattach_pipe (zmq::pipe_t *pipe_, + bool subscribe_to_all_, + bool locally_initiated_) ZMQ_FINAL; + int xsetsockopt (int option_, + const void *optval_, + size_t optvallen_) ZMQ_OVERRIDE; + int xsend (zmq::msg_t *msg_) ZMQ_OVERRIDE; + bool xhas_out () ZMQ_OVERRIDE; + int xrecv (zmq::msg_t *msg_) ZMQ_FINAL; + bool xhas_in () ZMQ_FINAL; + void xread_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void xwrite_activated (zmq::pipe_t *pipe_) ZMQ_FINAL; + void xhiccuped (pipe_t *pipe_) ZMQ_FINAL; + void xpipe_terminated (zmq::pipe_t *pipe_) ZMQ_FINAL; + + private: + // Check whether the message matches at least one subscription. + bool match (zmq::msg_t *msg_); + + // Function to be applied to the trie to send all the subsciptions + // upstream. + static void + send_subscription (unsigned char *data_, size_t size_, void *arg_); + + // Fair queueing object for inbound pipes. + fq_t _fq; + + // Object for distributing the subscriptions upstream. + dist_t _dist; + + // The repository of subscriptions. +#ifdef ZMQ_USE_RADIX_TREE + radix_tree_t _subscriptions; +#else + trie_t _subscriptions; +#endif + + // If true, 'message' contains a matching message to return on the + // next recv call. + bool _has_message; + msg_t _message; + + // If true, part of a multipart message was already sent, but + // there are following parts still waiting. + bool _more_send; + + // If true, part of a multipart message was already received, but + // there are following parts still waiting. + bool _more_recv; + + // If true, subscribe and cancel messages are processed for the rest + // of multipart message. + bool _process_subscribe; + + // This option is enabled with ZMQ_ONLY_FIRST_SUBSCRIBE. + // If true, messages following subscribe/unsubscribe in a multipart + // message are treated as user data regardless of the first byte. + bool _only_first_subscribe; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (xsub_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/ypipe.hpp b/3rd/libzmq/src/ypipe.hpp new file mode 100644 index 00000000..e30878f9 --- /dev/null +++ b/3rd/libzmq/src/ypipe.hpp @@ -0,0 +1,205 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_YPIPE_HPP_INCLUDED__ +#define __ZMQ_YPIPE_HPP_INCLUDED__ + +#include "atomic_ptr.hpp" +#include "yqueue.hpp" +#include "ypipe_base.hpp" + +namespace zmq +{ +// Lock-free queue implementation. +// Only a single thread can read from the pipe at any specific moment. +// Only a single thread can write to the pipe at any specific moment. +// T is the type of the object in the queue. +// N is granularity of the pipe, i.e. how many items are needed to +// perform next memory allocation. + +template class ypipe_t ZMQ_FINAL : public ypipe_base_t +{ + public: + // Initialises the pipe. + ypipe_t () + { + // Insert terminator element into the queue. + _queue.push (); + + // Let all the pointers to point to the terminator. + // (unless pipe is dead, in which case c is set to NULL). + _r = _w = _f = &_queue.back (); + _c.set (&_queue.back ()); + } + + // Following function (write) deliberately copies uninitialised data + // when used with zmq_msg. Initialising the VSM body for + // non-VSM messages won't be good for performance. + +#ifdef ZMQ_HAVE_OPENVMS +#pragma message save +#pragma message disable(UNINIT) +#endif + + // Write an item to the pipe. Don't flush it yet. If incomplete is + // set to true the item is assumed to be continued by items + // subsequently written to the pipe. Incomplete items are never + // flushed down the stream. + void write (const T &value_, bool incomplete_) + { + // Place the value to the queue, add new terminator element. + _queue.back () = value_; + _queue.push (); + + // Move the "flush up to here" poiter. + if (!incomplete_) + _f = &_queue.back (); + } + +#ifdef ZMQ_HAVE_OPENVMS +#pragma message restore +#endif + + // Pop an incomplete item from the pipe. Returns true if such + // item exists, false otherwise. + bool unwrite (T *value_) + { + if (_f == &_queue.back ()) + return false; + _queue.unpush (); + *value_ = _queue.back (); + return true; + } + + // Flush all the completed items into the pipe. Returns false if + // the reader thread is sleeping. In that case, caller is obliged to + // wake the reader up before using the pipe again. + bool flush () + { + // If there are no un-flushed items, do nothing. + if (_w == _f) + return true; + + // Try to set 'c' to 'f'. + if (_c.cas (_w, _f) != _w) { + // Compare-and-swap was unseccessful because 'c' is NULL. + // This means that the reader is asleep. Therefore we don't + // care about thread-safeness and update c in non-atomic + // manner. We'll return false to let the caller know + // that reader is sleeping. + _c.set (_f); + _w = _f; + return false; + } + + // Reader is alive. Nothing special to do now. Just move + // the 'first un-flushed item' pointer to 'f'. + _w = _f; + return true; + } + + // Check whether item is available for reading. + bool check_read () + { + // Was the value prefetched already? If so, return. + if (&_queue.front () != _r && _r) + return true; + + // There's no prefetched value, so let us prefetch more values. + // Prefetching is to simply retrieve the + // pointer from c in atomic fashion. If there are no + // items to prefetch, set c to NULL (using compare-and-swap). + _r = _c.cas (&_queue.front (), NULL); + + // If there are no elements prefetched, exit. + // During pipe's lifetime r should never be NULL, however, + // it can happen during pipe shutdown when items + // are being deallocated. + if (&_queue.front () == _r || !_r) + return false; + + // There was at least one value prefetched. + return true; + } + + // Reads an item from the pipe. Returns false if there is no value. + // available. + bool read (T *value_) + { + // Try to prefetch a value. + if (!check_read ()) + return false; + + // There was at least one value prefetched. + // Return it to the caller. + *value_ = _queue.front (); + _queue.pop (); + return true; + } + + // Applies the function fn to the first elemenent in the pipe + // and returns the value returned by the fn. + // The pipe mustn't be empty or the function crashes. + bool probe (bool (*fn_) (const T &)) + { + const bool rc = check_read (); + zmq_assert (rc); + + return (*fn_) (_queue.front ()); + } + + protected: + // Allocation-efficient queue to store pipe items. + // Front of the queue points to the first prefetched item, back of + // the pipe points to last un-flushed item. Front is used only by + // reader thread, while back is used only by writer thread. + yqueue_t _queue; + + // Points to the first un-flushed item. This variable is used + // exclusively by writer thread. + T *_w; + + // Points to the first un-prefetched item. This variable is used + // exclusively by reader thread. + T *_r; + + // Points to the first item to be flushed in the future. + T *_f; + + // The single point of contention between writer and reader thread. + // Points past the last flushed item. If it is NULL, + // reader is asleep. This pointer should be always accessed using + // atomic operations. + atomic_ptr_t _c; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ypipe_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/ypipe_base.hpp b/3rd/libzmq/src/ypipe_base.hpp new file mode 100644 index 00000000..50a534f8 --- /dev/null +++ b/3rd/libzmq/src/ypipe_base.hpp @@ -0,0 +1,55 @@ + +/* + 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 . +*/ + +#ifndef __ZMQ_YPIPE_BASE_HPP_INCLUDED__ +#define __ZMQ_YPIPE_BASE_HPP_INCLUDED__ + +#include "macros.hpp" + +namespace zmq +{ +// ypipe_base abstracts ypipe and ypipe_conflate specific +// classes, one is selected according to a the conflate +// socket option + +template class ypipe_base_t +{ + public: + virtual ~ypipe_base_t () ZMQ_DEFAULT; + virtual void write (const T &value_, bool incomplete_) = 0; + virtual bool unwrite (T *value_) = 0; + virtual bool flush () = 0; + virtual bool check_read () = 0; + virtual bool read (T *value_) = 0; + virtual bool probe (bool (*fn_) (const T &)) = 0; +}; +} + +#endif diff --git a/3rd/libzmq/src/ypipe_conflate.hpp b/3rd/libzmq/src/ypipe_conflate.hpp new file mode 100644 index 00000000..aa0731d1 --- /dev/null +++ b/3rd/libzmq/src/ypipe_conflate.hpp @@ -0,0 +1,113 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_YPIPE_CONFLATE_HPP_INCLUDED__ +#define __ZMQ_YPIPE_CONFLATE_HPP_INCLUDED__ + +#include "platform.hpp" +#include "dbuffer.hpp" +#include "ypipe_base.hpp" + +namespace zmq +{ +// Adapter for dbuffer, to plug it in instead of a queue for the sake +// of implementing the conflate socket option, which, if set, makes +// the receiving side to discard all incoming messages but the last one. +// +// reader_awake flag is needed here to mimic ypipe delicate behaviour +// around the reader being asleep (see 'c' pointer being NULL in ypipe.hpp) + +template class ypipe_conflate_t ZMQ_FINAL : public ypipe_base_t +{ + public: + // Initialises the pipe. + ypipe_conflate_t () : reader_awake (false) {} + + // Following function (write) deliberately copies uninitialised data + // when used with zmq_msg. Initialising the VSM body for + // non-VSM messages won't be good for performance. + +#ifdef ZMQ_HAVE_OPENVMS +#pragma message save +#pragma message disable(UNINIT) +#endif + void write (const T &value_, bool incomplete_) + { + (void) incomplete_; + + dbuffer.write (value_); + } + +#ifdef ZMQ_HAVE_OPENVMS +#pragma message restore +#endif + + // There are no incomplete items for conflate ypipe + bool unwrite (T *) { return false; } + + // Flush is no-op for conflate ypipe. Reader asleep behaviour + // is as of the usual ypipe. + // Returns false if the reader thread is sleeping. In that case, + // caller is obliged to wake the reader up before using the pipe again. + bool flush () { return reader_awake; } + + // Check whether item is available for reading. + bool check_read () + { + const bool res = dbuffer.check_read (); + if (!res) + reader_awake = false; + + return res; + } + + // Reads an item from the pipe. Returns false if there is no value. + // available. + bool read (T *value_) + { + if (!check_read ()) + return false; + + return dbuffer.read (value_); + } + + // Applies the function fn to the first elemenent in the pipe + // and returns the value returned by the fn. + // The pipe mustn't be empty or the function crashes. + bool probe (bool (*fn_) (const T &)) { return dbuffer.probe (fn_); } + + protected: + dbuffer_t dbuffer; + bool reader_awake; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (ypipe_conflate_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/yqueue.hpp b/3rd/libzmq/src/yqueue.hpp new file mode 100644 index 00000000..6ba634a3 --- /dev/null +++ b/3rd/libzmq/src/yqueue.hpp @@ -0,0 +1,214 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_YQUEUE_HPP_INCLUDED__ +#define __ZMQ_YQUEUE_HPP_INCLUDED__ + +#include +#include + +#include "err.hpp" +#include "atomic_ptr.hpp" + +namespace zmq +{ +// yqueue is an efficient queue implementation. The main goal is +// to minimise number of allocations/deallocations needed. Thus yqueue +// allocates/deallocates elements in batches of N. +// +// yqueue allows one thread to use push/back function and another one +// to use pop/front functions. However, user must ensure that there's no +// pop on the empty queue and that both threads don't access the same +// element in unsynchronised manner. +// +// T is the type of the object in the queue. +// N is granularity of the queue (how many pushes have to be done till +// actual memory allocation is required). +#ifdef HAVE_POSIX_MEMALIGN +// ALIGN is the memory alignment size to use in the case where we have +// posix_memalign available. Default value is 64, this alignment will +// prevent two queue chunks from occupying the same CPU cache line on +// architectures where cache lines are <= 64 bytes (e.g. most things +// except POWER). It is detected at build time to try to account for other +// platforms like POWER and s390x. +template class yqueue_t +#else +template class yqueue_t +#endif +{ + public: + // Create the queue. + inline yqueue_t () + { + _begin_chunk = allocate_chunk (); + alloc_assert (_begin_chunk); + _begin_pos = 0; + _back_chunk = NULL; + _back_pos = 0; + _end_chunk = _begin_chunk; + _end_pos = 0; + } + + // Destroy the queue. + inline ~yqueue_t () + { + while (true) { + if (_begin_chunk == _end_chunk) { + free (_begin_chunk); + break; + } + chunk_t *o = _begin_chunk; + _begin_chunk = _begin_chunk->next; + free (o); + } + + chunk_t *sc = _spare_chunk.xchg (NULL); + free (sc); + } + + // Returns reference to the front element of the queue. + // If the queue is empty, behaviour is undefined. + inline T &front () { return _begin_chunk->values[_begin_pos]; } + + // Returns reference to the back element of the queue. + // If the queue is empty, behaviour is undefined. + inline T &back () { return _back_chunk->values[_back_pos]; } + + // Adds an element to the back end of the queue. + inline void push () + { + _back_chunk = _end_chunk; + _back_pos = _end_pos; + + if (++_end_pos != N) + return; + + chunk_t *sc = _spare_chunk.xchg (NULL); + if (sc) { + _end_chunk->next = sc; + sc->prev = _end_chunk; + } else { + _end_chunk->next = allocate_chunk (); + alloc_assert (_end_chunk->next); + _end_chunk->next->prev = _end_chunk; + } + _end_chunk = _end_chunk->next; + _end_pos = 0; + } + + // Removes element from the back end of the queue. In other words + // it rollbacks last push to the queue. Take care: Caller is + // responsible for destroying the object being unpushed. + // The caller must also guarantee that the queue isn't empty when + // unpush is called. It cannot be done automatically as the read + // side of the queue can be managed by different, completely + // unsynchronised thread. + inline void unpush () + { + // First, move 'back' one position backwards. + if (_back_pos) + --_back_pos; + else { + _back_pos = N - 1; + _back_chunk = _back_chunk->prev; + } + + // Now, move 'end' position backwards. Note that obsolete end chunk + // is not used as a spare chunk. The analysis shows that doing so + // would require free and atomic operation per chunk deallocated + // instead of a simple free. + if (_end_pos) + --_end_pos; + else { + _end_pos = N - 1; + _end_chunk = _end_chunk->prev; + free (_end_chunk->next); + _end_chunk->next = NULL; + } + } + + // Removes an element from the front end of the queue. + inline void pop () + { + if (++_begin_pos == N) { + chunk_t *o = _begin_chunk; + _begin_chunk = _begin_chunk->next; + _begin_chunk->prev = NULL; + _begin_pos = 0; + + // 'o' has been more recently used than _spare_chunk, + // so for cache reasons we'll get rid of the spare and + // use 'o' as the spare. + chunk_t *cs = _spare_chunk.xchg (o); + free (cs); + } + } + + private: + // Individual memory chunk to hold N elements. + struct chunk_t + { + T values[N]; + chunk_t *prev; + chunk_t *next; + }; + + static inline chunk_t *allocate_chunk () + { +#ifdef HAVE_POSIX_MEMALIGN + void *pv; + if (posix_memalign (&pv, ALIGN, sizeof (chunk_t)) == 0) + return (chunk_t *) pv; + return NULL; +#else + return static_cast (malloc (sizeof (chunk_t))); +#endif + } + + // Back position may point to invalid memory if the queue is empty, + // while begin & end positions are always valid. Begin position is + // accessed exclusively be queue reader (front/pop), while back and + // end positions are accessed exclusively by queue writer (back/push). + chunk_t *_begin_chunk; + int _begin_pos; + chunk_t *_back_chunk; + int _back_pos; + chunk_t *_end_chunk; + int _end_pos; + + // People are likely to produce and consume at similar rates. In + // this scenario holding onto the most recently freed chunk saves + // us from having to call malloc/free. + atomic_ptr_t _spare_chunk; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (yqueue_t) +}; +} + +#endif diff --git a/3rd/libzmq/src/zap_client.cpp b/3rd/libzmq/src/zap_client.cpp new file mode 100644 index 00000000..7ba28c91 --- /dev/null +++ b/3rd/libzmq/src/zap_client.cpp @@ -0,0 +1,314 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" + +#include "zap_client.hpp" +#include "msg.hpp" +#include "session_base.hpp" + +namespace zmq +{ +const char zap_version[] = "1.0"; +const size_t zap_version_len = sizeof (zap_version) - 1; + +const char id[] = "1"; +const size_t id_len = sizeof (id) - 1; + +zap_client_t::zap_client_t (session_base_t *const session_, + const std::string &peer_address_, + const options_t &options_) : + mechanism_base_t (session_, options_), + peer_address (peer_address_) +{ +} + +void zap_client_t::send_zap_request (const char *mechanism_, + size_t mechanism_length_, + const uint8_t *credentials_, + size_t credentials_size_) +{ + send_zap_request (mechanism_, mechanism_length_, &credentials_, + &credentials_size_, 1); +} + +void zap_client_t::send_zap_request (const char *mechanism_, + size_t mechanism_length_, + const uint8_t **credentials_, + size_t *credentials_sizes_, + size_t credentials_count_) +{ + // write_zap_msg cannot fail. It could only fail if the HWM was exceeded, + // but on the ZAP socket, the HWM is disabled. + + int rc; + msg_t msg; + + // Address delimiter frame + rc = msg.init (); + errno_assert (rc == 0); + msg.set_flags (msg_t::more); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + + // Version frame + rc = msg.init_size (zap_version_len); + errno_assert (rc == 0); + memcpy (msg.data (), zap_version, zap_version_len); + msg.set_flags (msg_t::more); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + + // Request ID frame + rc = msg.init_size (id_len); + errno_assert (rc == 0); + memcpy (msg.data (), id, id_len); + msg.set_flags (msg_t::more); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + + // Domain frame + rc = msg.init_size (options.zap_domain.length ()); + errno_assert (rc == 0); + memcpy (msg.data (), options.zap_domain.c_str (), + options.zap_domain.length ()); + msg.set_flags (msg_t::more); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + + // Address frame + rc = msg.init_size (peer_address.length ()); + errno_assert (rc == 0); + memcpy (msg.data (), peer_address.c_str (), peer_address.length ()); + msg.set_flags (msg_t::more); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + + // Routing id frame + rc = msg.init_size (options.routing_id_size); + errno_assert (rc == 0); + memcpy (msg.data (), options.routing_id, options.routing_id_size); + msg.set_flags (msg_t::more); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + + // Mechanism frame + rc = msg.init_size (mechanism_length_); + errno_assert (rc == 0); + memcpy (msg.data (), mechanism_, mechanism_length_); + if (credentials_count_) + msg.set_flags (msg_t::more); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + + // Credentials frames + for (size_t i = 0; i < credentials_count_; ++i) { + rc = msg.init_size (credentials_sizes_[i]); + errno_assert (rc == 0); + if (i < credentials_count_ - 1) + msg.set_flags (msg_t::more); + memcpy (msg.data (), credentials_[i], credentials_sizes_[i]); + rc = session->write_zap_msg (&msg); + errno_assert (rc == 0); + } +} + +int zap_client_t::receive_and_process_zap_reply () +{ + int rc = 0; + const size_t zap_reply_frame_count = 7; + msg_t msg[zap_reply_frame_count]; + + // Initialize all reply frames + for (size_t i = 0; i < zap_reply_frame_count; i++) { + rc = msg[i].init (); + errno_assert (rc == 0); + } + + for (size_t i = 0; i < zap_reply_frame_count; i++) { + rc = session->read_zap_msg (&msg[i]); + if (rc == -1) { + if (errno == EAGAIN) { + return 1; + } + return close_and_return (msg, -1); + } + if ((msg[i].flags () & msg_t::more) + == (i < zap_reply_frame_count - 1 ? 0 : msg_t::more)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY); + errno = EPROTO; + return close_and_return (msg, -1); + } + } + + // Address delimiter frame + if (msg[0].size () > 0) { + // TODO can a ZAP handler produce such a message at all? + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED); + errno = EPROTO; + return close_and_return (msg, -1); + } + + // Version frame + if (msg[1].size () != zap_version_len + || memcmp (msg[1].data (), zap_version, zap_version_len)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION); + errno = EPROTO; + return close_and_return (msg, -1); + } + + // Request id frame + if (msg[2].size () != id_len || memcmp (msg[2].data (), id, id_len)) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID); + errno = EPROTO; + return close_and_return (msg, -1); + } + + // Status code frame, only 200, 300, 400 and 500 are valid status codes + const char *status_code_data = static_cast (msg[3].data ()); + if (msg[3].size () != 3 || status_code_data[0] < '2' + || status_code_data[0] > '5' || status_code_data[1] != '0' + || status_code_data[2] != '0') { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE); + errno = EPROTO; + return close_and_return (msg, -1); + } + + // Save status code + status_code.assign (static_cast (msg[3].data ()), 3); + + // Save user id + set_user_id (msg[5].data (), msg[5].size ()); + + // Process metadata frame + rc = parse_metadata (static_cast (msg[6].data ()), + msg[6].size (), true); + + if (rc != 0) { + session->get_socket ()->event_handshake_failed_protocol ( + session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA); + errno = EPROTO; + return close_and_return (msg, -1); + } + + // Close all reply frames + for (size_t i = 0; i < zap_reply_frame_count; i++) { + const int rc2 = msg[i].close (); + errno_assert (rc2 == 0); + } + + handle_zap_status_code (); + + return 0; +} + +void zap_client_t::handle_zap_status_code () +{ + // we can assume here that status_code is a valid ZAP status code, + // i.e. 200, 300, 400 or 500 + int status_code_numeric = 0; + switch (status_code[0]) { + case '2': + return; + case '3': + status_code_numeric = 300; + break; + case '4': + status_code_numeric = 400; + break; + case '5': + status_code_numeric = 500; + break; + } + + session->get_socket ()->event_handshake_failed_auth ( + session->get_endpoint (), status_code_numeric); +} + +zap_client_common_handshake_t::zap_client_common_handshake_t ( + session_base_t *const session_, + const std::string &peer_address_, + const options_t &options_, + state_t zap_reply_ok_state_) : + mechanism_base_t (session_, options_), + zap_client_t (session_, peer_address_, options_), + state (waiting_for_hello), + _zap_reply_ok_state (zap_reply_ok_state_) +{ +} + +zmq::mechanism_t::status_t zap_client_common_handshake_t::status () const +{ + if (state == ready) + return mechanism_t::ready; + if (state == error_sent) + return mechanism_t::error; + + return mechanism_t::handshaking; +} + +int zap_client_common_handshake_t::zap_msg_available () +{ + zmq_assert (state == waiting_for_zap_reply); + return receive_and_process_zap_reply () == -1 ? -1 : 0; +} + +void zap_client_common_handshake_t::handle_zap_status_code () +{ + zap_client_t::handle_zap_status_code (); + + // we can assume here that status_code is a valid ZAP status code, + // i.e. 200, 300, 400 or 500 + switch (status_code[0]) { + case '2': + state = _zap_reply_ok_state; + break; + case '3': + // a 300 error code (temporary failure) + // should NOT result in an ERROR message, but instead the + // client should be silently disconnected (see CURVEZMQ RFC) + // therefore, go immediately to state error_sent + state = error_sent; + break; + default: + state = sending_error; + } +} + +int zap_client_common_handshake_t::receive_and_process_zap_reply () +{ + zmq_assert (state == waiting_for_zap_reply); + return zap_client_t::receive_and_process_zap_reply (); +} +} diff --git a/3rd/libzmq/src/zap_client.hpp b/3rd/libzmq/src/zap_client.hpp new file mode 100644 index 00000000..970a0379 --- /dev/null +++ b/3rd/libzmq/src/zap_client.hpp @@ -0,0 +1,101 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ZAP_CLIENT_HPP_INCLUDED__ +#define __ZMQ_ZAP_CLIENT_HPP_INCLUDED__ + +#include "mechanism_base.hpp" + +namespace zmq +{ +class zap_client_t : public virtual mechanism_base_t +{ + public: + zap_client_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_); + + void send_zap_request (const char *mechanism_, + size_t mechanism_length_, + const uint8_t *credentials_, + size_t credentials_size_); + + void send_zap_request (const char *mechanism_, + size_t mechanism_length_, + const uint8_t **credentials_, + size_t *credentials_sizes_, + size_t credentials_count_); + + virtual int receive_and_process_zap_reply (); + virtual void handle_zap_status_code (); + + protected: + const std::string peer_address; + + // Status code as received from ZAP handler + std::string status_code; +}; + +class zap_client_common_handshake_t : public zap_client_t +{ + protected: + enum state_t + { + waiting_for_hello, + sending_welcome, + waiting_for_initiate, + waiting_for_zap_reply, + sending_ready, + sending_error, + error_sent, + ready + }; + + zap_client_common_handshake_t (session_base_t *session_, + const std::string &peer_address_, + const options_t &options_, + state_t zap_reply_ok_state_); + + // methods from mechanism_t + status_t status () const ZMQ_FINAL; + int zap_msg_available () ZMQ_FINAL; + + // zap_client_t methods + int receive_and_process_zap_reply () ZMQ_FINAL; + void handle_zap_status_code () ZMQ_FINAL; + + // Current FSM state + state_t state; + + private: + const state_t _zap_reply_ok_state; +}; +} + +#endif diff --git a/3rd/libzmq/src/zmq.cpp b/3rd/libzmq/src/zmq.cpp new file mode 100644 index 00000000..2c2e3d52 --- /dev/null +++ b/3rd/libzmq/src/zmq.cpp @@ -0,0 +1,1538 @@ +/* + 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 . +*/ + +// "Tell them I was a writer. +// A maker of software. +// A humanist. A father. +// And many things. +// But above all, a writer. +// Thank You. :)" +// - Pieter Hintjens + +#include "precompiled.hpp" +#define ZMQ_TYPE_UNSAFE + +#include "macros.hpp" +#include "poller.hpp" +#include "peer.hpp" + +#if !defined ZMQ_HAVE_POLLER +// On AIX platform, poll.h has to be included first to get consistent +// definition of pollfd structure (AIX uses 'reqevents' and 'retnevents' +// instead of 'events' and 'revents' and defines macros to map from POSIX-y +// names to AIX-specific names). +#if defined ZMQ_POLL_BASED_ON_POLL && !defined ZMQ_HAVE_WINDOWS +#include +#endif + +#include "polling_util.hpp" +#endif + +// TODO: determine if this is an issue, since zmq.h is being loaded from pch. +// zmq.h must be included *after* poll.h for AIX to build properly +//#include "../include/zmq.h" + +#if !defined ZMQ_HAVE_WINDOWS +#include +#ifdef ZMQ_HAVE_VXWORKS +#include +#endif +#endif + +// XSI vector I/O +#if defined ZMQ_HAVE_UIO +#include +#else +struct iovec +{ + void *iov_base; + size_t iov_len; +}; +#endif + +#include +#include +#include +#include + +#include "proxy.hpp" +#include "socket_base.hpp" +#include "stdint.hpp" +#include "config.hpp" +#include "likely.hpp" +#include "clock.hpp" +#include "ctx.hpp" +#include "err.hpp" +#include "msg.hpp" +#include "fd.hpp" +#include "metadata.hpp" +#include "socket_poller.hpp" +#include "timers.hpp" +#include "ip.hpp" +#include "address.hpp" + +#if defined ZMQ_HAVE_OPENPGM +#define __PGM_WININT_H__ +#include +#endif + +// Compile time check whether msg_t fits into zmq_msg_t. +typedef char + check_msg_t_size[sizeof (zmq::msg_t) == sizeof (zmq_msg_t) ? 1 : -1]; + + +void zmq_version (int *major_, int *minor_, int *patch_) +{ + *major_ = ZMQ_VERSION_MAJOR; + *minor_ = ZMQ_VERSION_MINOR; + *patch_ = ZMQ_VERSION_PATCH; +} + + +const char *zmq_strerror (int errnum_) +{ + return zmq::errno_to_string (errnum_); +} + +int zmq_errno (void) +{ + return errno; +} + + +// New context API + +void *zmq_ctx_new (void) +{ + // We do this before the ctx constructor since its embedded mailbox_t + // object needs the network to be up and running (at least on Windows). + if (!zmq::initialize_network ()) { + return NULL; + } + + // Create 0MQ context. + zmq::ctx_t *ctx = new (std::nothrow) zmq::ctx_t; + if (ctx) { + if (!ctx->valid ()) { + delete ctx; + return NULL; + } + } + return ctx; +} + +int zmq_ctx_term (void *ctx_) +{ + if (!ctx_ || !(static_cast (ctx_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + const int rc = (static_cast (ctx_))->terminate (); + const int en = errno; + + // Shut down only if termination was not interrupted by a signal. + if (!rc || en != EINTR) { + zmq::shutdown_network (); + } + + errno = en; + return rc; +} + +int zmq_ctx_shutdown (void *ctx_) +{ + if (!ctx_ || !(static_cast (ctx_))->check_tag ()) { + errno = EFAULT; + return -1; + } + return (static_cast (ctx_))->shutdown (); +} + +int zmq_ctx_set (void *ctx_, int option_, int optval_) +{ + return zmq_ctx_set_ext (ctx_, option_, &optval_, sizeof (int)); +} + +int zmq_ctx_set_ext (void *ctx_, + int option_, + const void *optval_, + size_t optvallen_) +{ + if (!ctx_ || !(static_cast (ctx_))->check_tag ()) { + errno = EFAULT; + return -1; + } + return (static_cast (ctx_)) + ->set (option_, optval_, optvallen_); +} + +int zmq_ctx_get (void *ctx_, int option_) +{ + if (!ctx_ || !(static_cast (ctx_))->check_tag ()) { + errno = EFAULT; + return -1; + } + return (static_cast (ctx_))->get (option_); +} + +int zmq_ctx_get_ext (void *ctx_, int option_, void *optval_, size_t *optvallen_) +{ + if (!ctx_ || !(static_cast (ctx_))->check_tag ()) { + errno = EFAULT; + return -1; + } + return (static_cast (ctx_)) + ->get (option_, optval_, optvallen_); +} + + +// Stable/legacy context API + +void *zmq_init (int io_threads_) +{ + if (io_threads_ >= 0) { + void *ctx = zmq_ctx_new (); + zmq_ctx_set (ctx, ZMQ_IO_THREADS, io_threads_); + return ctx; + } + errno = EINVAL; + return NULL; +} + +int zmq_term (void *ctx_) +{ + return zmq_ctx_term (ctx_); +} + +int zmq_ctx_destroy (void *ctx_) +{ + return zmq_ctx_term (ctx_); +} + + +// Sockets + +static zmq::socket_base_t *as_socket_base_t (void *s_) +{ + zmq::socket_base_t *s = static_cast (s_); + if (!s_ || !s->check_tag ()) { + errno = ENOTSOCK; + return NULL; + } + return s; +} + +void *zmq_socket (void *ctx_, int type_) +{ + if (!ctx_ || !(static_cast (ctx_))->check_tag ()) { + errno = EFAULT; + return NULL; + } + zmq::ctx_t *ctx = static_cast (ctx_); + zmq::socket_base_t *s = ctx->create_socket (type_); + return static_cast (s); +} + +int zmq_close (void *s_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + s->close (); + return 0; +} + +int zmq_setsockopt (void *s_, + int option_, + const void *optval_, + size_t optvallen_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->setsockopt (option_, optval_, optvallen_); +} + +int zmq_getsockopt (void *s_, int option_, void *optval_, size_t *optvallen_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->getsockopt (option_, optval_, optvallen_); +} + +int zmq_socket_monitor_versioned ( + void *s_, const char *addr_, uint64_t events_, int event_version_, int type_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->monitor (addr_, events_, event_version_, type_); +} + +int zmq_socket_monitor (void *s_, const char *addr_, int events_) +{ + return zmq_socket_monitor_versioned (s_, addr_, events_, 1, ZMQ_PAIR); +} + +int zmq_join (void *s_, const char *group_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->join (group_); +} + +int zmq_leave (void *s_, const char *group_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->leave (group_); +} + +int zmq_bind (void *s_, const char *addr_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->bind (addr_); +} + +int zmq_connect (void *s_, const char *addr_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->connect (addr_); +} + +uint32_t zmq_connect_peer (void *s_, const char *addr_) +{ + zmq::peer_t *s = static_cast (s_); + if (!s_ || !s->check_tag ()) { + errno = ENOTSOCK; + return 0; + } + + int socket_type; + size_t socket_type_size = sizeof (socket_type); + if (s->getsockopt (ZMQ_TYPE, &socket_type, &socket_type_size) != 0) + return 0; + + if (socket_type != ZMQ_PEER) { + errno = ENOTSUP; + return 0; + } + + return s->connect_peer (addr_); +} + + +int zmq_unbind (void *s_, const char *addr_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->term_endpoint (addr_); +} + +int zmq_disconnect (void *s_, const char *addr_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->term_endpoint (addr_); +} + +// Sending functions. + +static inline int +s_sendmsg (zmq::socket_base_t *s_, zmq_msg_t *msg_, int flags_) +{ + size_t sz = zmq_msg_size (msg_); + const int rc = s_->send (reinterpret_cast (msg_), flags_); + if (unlikely (rc < 0)) + return -1; + + // This is what I'd like to do, my C++ fu is too weak -- PH 2016/02/09 + // int max_msgsz = s_->parent->get (ZMQ_MAX_MSGSZ); + size_t max_msgsz = INT_MAX; + + // Truncate returned size to INT_MAX to avoid overflow to negative values + return static_cast (sz < max_msgsz ? sz : max_msgsz); +} + +/* To be deprecated once zmq_msg_send() is stable */ +int zmq_sendmsg (void *s_, zmq_msg_t *msg_, int flags_) +{ + return zmq_msg_send (msg_, s_, flags_); +} + +int zmq_send (void *s_, const void *buf_, size_t len_, int flags_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + zmq_msg_t msg; + int rc = zmq_msg_init_buffer (&msg, buf_, len_); + if (unlikely (rc < 0)) + return -1; + + rc = s_sendmsg (s, &msg, flags_); + if (unlikely (rc < 0)) { + const int err = errno; + const int rc2 = zmq_msg_close (&msg); + errno_assert (rc2 == 0); + errno = err; + return -1; + } + // Note the optimisation here. We don't close the msg object as it is + // empty anyway. This may change when implementation of zmq_msg_t changes. + return rc; +} + +int zmq_send_const (void *s_, const void *buf_, size_t len_, int flags_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + zmq_msg_t msg; + int rc = + zmq_msg_init_data (&msg, const_cast (buf_), len_, NULL, NULL); + if (rc != 0) + return -1; + + rc = s_sendmsg (s, &msg, flags_); + if (unlikely (rc < 0)) { + const int err = errno; + const int rc2 = zmq_msg_close (&msg); + errno_assert (rc2 == 0); + errno = err; + return -1; + } + // Note the optimisation here. We don't close the msg object as it is + // empty anyway. This may change when implementation of zmq_msg_t changes. + return rc; +} + + +// Send multiple messages. +// TODO: this function has no man page +// +// If flag bit ZMQ_SNDMORE is set the vector is treated as +// a single multi-part message, i.e. the last message has +// ZMQ_SNDMORE bit switched off. +// +int zmq_sendiov (void *s_, iovec *a_, size_t count_, int flags_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + if (unlikely (count_ <= 0 || !a_)) { + errno = EINVAL; + return -1; + } + + int rc = 0; + zmq_msg_t msg; + + for (size_t i = 0; i < count_; ++i) { + rc = zmq_msg_init_size (&msg, a_[i].iov_len); + if (rc != 0) { + rc = -1; + break; + } + memcpy (zmq_msg_data (&msg), a_[i].iov_base, a_[i].iov_len); + if (i == count_ - 1) + flags_ = flags_ & ~ZMQ_SNDMORE; + rc = s_sendmsg (s, &msg, flags_); + if (unlikely (rc < 0)) { + const int err = errno; + const int rc2 = zmq_msg_close (&msg); + errno_assert (rc2 == 0); + errno = err; + rc = -1; + break; + } + } + return rc; +} + +// Receiving functions. + +static int s_recvmsg (zmq::socket_base_t *s_, zmq_msg_t *msg_, int flags_) +{ + const int rc = s_->recv (reinterpret_cast (msg_), flags_); + if (unlikely (rc < 0)) + return -1; + + // Truncate returned size to INT_MAX to avoid overflow to negative values + const size_t sz = zmq_msg_size (msg_); + return static_cast (sz < INT_MAX ? sz : INT_MAX); +} + +/* To be deprecated once zmq_msg_recv() is stable */ +int zmq_recvmsg (void *s_, zmq_msg_t *msg_, int flags_) +{ + return zmq_msg_recv (msg_, s_, flags_); +} + + +int zmq_recv (void *s_, void *buf_, size_t len_, int flags_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + zmq_msg_t msg; + int rc = zmq_msg_init (&msg); + errno_assert (rc == 0); + + const int nbytes = s_recvmsg (s, &msg, flags_); + if (unlikely (nbytes < 0)) { + const int err = errno; + rc = zmq_msg_close (&msg); + errno_assert (rc == 0); + errno = err; + return -1; + } + + // An oversized message is silently truncated. + const size_t to_copy = size_t (nbytes) < len_ ? size_t (nbytes) : len_; + + // We explicitly allow a null buffer argument if len is zero + if (to_copy) { + assert (buf_); + memcpy (buf_, zmq_msg_data (&msg), to_copy); + } + rc = zmq_msg_close (&msg); + errno_assert (rc == 0); + + return nbytes; +} + +// Receive a multi-part message +// +// Receives up to *count_ parts of a multi-part message. +// Sets *count_ to the actual number of parts read. +// ZMQ_RCVMORE is set to indicate if a complete multi-part message was read. +// Returns number of message parts read, or -1 on error. +// +// Note: even if -1 is returned, some parts of the message +// may have been read. Therefore the client must consult +// *count_ to retrieve message parts successfully read, +// even if -1 is returned. +// +// The iov_base* buffers of each iovec *a_ filled in by this +// function may be freed using free(). +// TODO: this function has no man page +// +int zmq_recviov (void *s_, iovec *a_, size_t *count_, int flags_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + if (unlikely (!count_ || *count_ <= 0 || !a_)) { + errno = EINVAL; + return -1; + } + + const size_t count = *count_; + int nread = 0; + bool recvmore = true; + + *count_ = 0; + + for (size_t i = 0; recvmore && i < count; ++i) { + zmq_msg_t msg; + int rc = zmq_msg_init (&msg); + errno_assert (rc == 0); + + const int nbytes = s_recvmsg (s, &msg, flags_); + if (unlikely (nbytes < 0)) { + const int err = errno; + rc = zmq_msg_close (&msg); + errno_assert (rc == 0); + errno = err; + nread = -1; + break; + } + + a_[i].iov_len = zmq_msg_size (&msg); + a_[i].iov_base = static_cast (malloc (a_[i].iov_len)); + if (unlikely (!a_[i].iov_base)) { + errno = ENOMEM; + return -1; + } + memcpy (a_[i].iov_base, static_cast (zmq_msg_data (&msg)), + a_[i].iov_len); + // Assume zmq_socket ZMQ_RVCMORE is properly set. + const zmq::msg_t *p_msg = reinterpret_cast (&msg); + recvmore = p_msg->flags () & zmq::msg_t::more; + rc = zmq_msg_close (&msg); + errno_assert (rc == 0); + ++*count_; + ++nread; + } + return nread; +} + +// Message manipulators. + +int zmq_msg_init (zmq_msg_t *msg_) +{ + return (reinterpret_cast (msg_))->init (); +} + +int zmq_msg_init_size (zmq_msg_t *msg_, size_t size_) +{ + return (reinterpret_cast (msg_))->init_size (size_); +} + +int zmq_msg_init_buffer (zmq_msg_t *msg_, const void *buf_, size_t size_) +{ + return (reinterpret_cast (msg_))->init_buffer (buf_, size_); +} + +int zmq_msg_init_data ( + zmq_msg_t *msg_, void *data_, size_t size_, zmq_free_fn *ffn_, void *hint_) +{ + return (reinterpret_cast (msg_)) + ->init_data (data_, size_, ffn_, hint_); +} + +int zmq_msg_send (zmq_msg_t *msg_, void *s_, int flags_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s_sendmsg (s, msg_, flags_); +} + +int zmq_msg_recv (zmq_msg_t *msg_, void *s_, int flags_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s_recvmsg (s, msg_, flags_); +} + +int zmq_msg_close (zmq_msg_t *msg_) +{ + return (reinterpret_cast (msg_))->close (); +} + +int zmq_msg_move (zmq_msg_t *dest_, zmq_msg_t *src_) +{ + return (reinterpret_cast (dest_)) + ->move (*reinterpret_cast (src_)); +} + +int zmq_msg_copy (zmq_msg_t *dest_, zmq_msg_t *src_) +{ + return (reinterpret_cast (dest_)) + ->copy (*reinterpret_cast (src_)); +} + +void *zmq_msg_data (zmq_msg_t *msg_) +{ + return (reinterpret_cast (msg_))->data (); +} + +size_t zmq_msg_size (const zmq_msg_t *msg_) +{ + return ((zmq::msg_t *) msg_)->size (); +} + +int zmq_msg_more (const zmq_msg_t *msg_) +{ + return zmq_msg_get (msg_, ZMQ_MORE); +} + +int zmq_msg_get (const zmq_msg_t *msg_, int property_) +{ + const char *fd_string; + + switch (property_) { + case ZMQ_MORE: + return (((zmq::msg_t *) msg_)->flags () & zmq::msg_t::more) ? 1 : 0; + case ZMQ_SRCFD: + fd_string = zmq_msg_gets (msg_, "__fd"); + if (fd_string == NULL) + return -1; + + return atoi (fd_string); + case ZMQ_SHARED: + return (((zmq::msg_t *) msg_)->is_cmsg ()) + || (((zmq::msg_t *) msg_)->flags () & zmq::msg_t::shared) + ? 1 + : 0; + default: + errno = EINVAL; + return -1; + } +} + +int zmq_msg_set (zmq_msg_t *, int, int) +{ + // No properties supported at present + errno = EINVAL; + return -1; +} + +int zmq_msg_set_routing_id (zmq_msg_t *msg_, uint32_t routing_id_) +{ + return (reinterpret_cast (msg_)) + ->set_routing_id (routing_id_); +} + +uint32_t zmq_msg_routing_id (zmq_msg_t *msg_) +{ + return (reinterpret_cast (msg_))->get_routing_id (); +} + +int zmq_msg_set_group (zmq_msg_t *msg_, const char *group_) +{ + return (reinterpret_cast (msg_))->set_group (group_); +} + +const char *zmq_msg_group (zmq_msg_t *msg_) +{ + return (reinterpret_cast (msg_))->group (); +} + +// Get message metadata string + +const char *zmq_msg_gets (const zmq_msg_t *msg_, const char *property_) +{ + const zmq::metadata_t *metadata = + reinterpret_cast (msg_)->metadata (); + const char *value = NULL; + if (metadata) + value = metadata->get (std::string (property_)); + if (value) + return value; + + errno = EINVAL; + return NULL; +} + +// Polling. + +#if defined ZMQ_HAVE_POLLER +static int zmq_poller_poll (zmq_pollitem_t *items_, int nitems_, long timeout_) +{ + // implement zmq_poll on top of zmq_poller + int rc; + zmq_poller_event_t *events; + zmq::socket_poller_t poller; + events = new (std::nothrow) zmq_poller_event_t[nitems_]; + alloc_assert (events); + + bool repeat_items = false; + // Register sockets with poller + for (int i = 0; i < nitems_; i++) { + items_[i].revents = 0; + + bool modify = false; + short e = items_[i].events; + if (items_[i].socket) { + // Poll item is a 0MQ socket. + for (int j = 0; j < i; ++j) { + // Check for repeat entries + if (items_[j].socket == items_[i].socket) { + repeat_items = true; + modify = true; + e |= items_[j].events; + } + } + if (modify) { + rc = zmq_poller_modify (&poller, items_[i].socket, e); + } else { + rc = zmq_poller_add (&poller, items_[i].socket, NULL, e); + } + if (rc < 0) { + delete[] events; + return rc; + } + } else { + // Poll item is a raw file descriptor. + for (int j = 0; j < i; ++j) { + // Check for repeat entries + if (!items_[j].socket && items_[j].fd == items_[i].fd) { + repeat_items = true; + modify = true; + e |= items_[j].events; + } + } + if (modify) { + rc = zmq_poller_modify_fd (&poller, items_[i].fd, e); + } else { + rc = zmq_poller_add_fd (&poller, items_[i].fd, NULL, e); + } + if (rc < 0) { + delete[] events; + return rc; + } + } + } + + // Wait for events + rc = zmq_poller_wait_all (&poller, events, nitems_, timeout_); + if (rc < 0) { + delete[] events; + if (zmq_errno () == EAGAIN) { + return 0; + } + return rc; + } + + // Transform poller events into zmq_pollitem events. + // items_ contains all items, while events only contains fired events. + // If no sockets are repeated (likely), the two are still co-ordered, so step through the items + // checking for matches only on the first event. + // If there are repeat items, they cannot be assumed to be co-ordered, + // so each pollitem must check fired events from the beginning. + int j_start = 0, found_events = rc; + for (int i = 0; i < nitems_; i++) { + for (int j = j_start; j < found_events; ++j) { + if ((items_[i].socket && items_[i].socket == events[j].socket) + || (!(items_[i].socket || events[j].socket) + && items_[i].fd == events[j].fd)) { + items_[i].revents = events[j].events & items_[i].events; + if (!repeat_items) { + // no repeats, we can ignore events we've already seen + j_start++; + } + break; + } + if (!repeat_items) { + // no repeats, never have to look at j > j_start + break; + } + } + } + + // Cleanup + delete[] events; + return rc; +} +#endif // ZMQ_HAVE_POLLER + +int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_) +{ +#if defined ZMQ_HAVE_POLLER + // if poller is present, use that if there is at least 1 thread-safe socket, + // otherwise fall back to the previous implementation as it's faster. + for (int i = 0; i != nitems_; i++) { + if (items_[i].socket) { + zmq::socket_base_t *s = as_socket_base_t (items_[i].socket); + if (s) { + if (s->is_thread_safe ()) + return zmq_poller_poll (items_, nitems_, timeout_); + } else { + //as_socket_base_t returned NULL : socket is invalid + return -1; + } + } + } +#endif // ZMQ_HAVE_POLLER +#if defined ZMQ_POLL_BASED_ON_POLL || defined ZMQ_POLL_BASED_ON_SELECT + if (unlikely (nitems_ < 0)) { + errno = EINVAL; + return -1; + } + if (unlikely (nitems_ == 0)) { + if (timeout_ == 0) + return 0; +#if defined ZMQ_HAVE_WINDOWS + Sleep (timeout_ > 0 ? timeout_ : INFINITE); + return 0; +#elif defined ZMQ_HAVE_VXWORKS + struct timespec ns_; + ns_.tv_sec = timeout_ / 1000; + ns_.tv_nsec = timeout_ % 1000 * 1000000; + return nanosleep (&ns_, 0); +#else + return usleep (timeout_ * 1000); +#endif + } + if (!items_) { + errno = EFAULT; + return -1; + } + + zmq::clock_t clock; + uint64_t now = 0; + uint64_t end = 0; +#if defined ZMQ_POLL_BASED_ON_POLL + zmq::fast_vector_t pollfds (nitems_); + + // Build pollset for poll () system call. + for (int i = 0; i != nitems_; i++) { + // If the poll item is a 0MQ socket, we poll on the file descriptor + // retrieved by the ZMQ_FD socket option. + if (items_[i].socket) { + size_t zmq_fd_size = sizeof (zmq::fd_t); + if (zmq_getsockopt (items_[i].socket, ZMQ_FD, &pollfds[i].fd, + &zmq_fd_size) + == -1) { + return -1; + } + pollfds[i].events = items_[i].events ? POLLIN : 0; + } + // Else, the poll item is a raw file descriptor. Just convert the + // events to normal POLLIN/POLLOUT for poll (). + else { + pollfds[i].fd = items_[i].fd; + pollfds[i].events = + (items_[i].events & ZMQ_POLLIN ? POLLIN : 0) + | (items_[i].events & ZMQ_POLLOUT ? POLLOUT : 0) + | (items_[i].events & ZMQ_POLLPRI ? POLLPRI : 0); + } + } +#else + // Ensure we do not attempt to select () on more than FD_SETSIZE + // file descriptors. + // TODO since this function is called by a client, we could return errno EINVAL/ENOMEM/... here + zmq_assert (nitems_ <= FD_SETSIZE); + + zmq::optimized_fd_set_t pollset_in (nitems_); + FD_ZERO (pollset_in.get ()); + zmq::optimized_fd_set_t pollset_out (nitems_); + FD_ZERO (pollset_out.get ()); + zmq::optimized_fd_set_t pollset_err (nitems_); + FD_ZERO (pollset_err.get ()); + + zmq::fd_t maxfd = 0; + + // Build the fd_sets for passing to select (). + for (int i = 0; i != nitems_; i++) { + // If the poll item is a 0MQ socket we are interested in input on the + // notification file descriptor retrieved by the ZMQ_FD socket option. + if (items_[i].socket) { + size_t zmq_fd_size = sizeof (zmq::fd_t); + zmq::fd_t notify_fd; + if (zmq_getsockopt (items_[i].socket, ZMQ_FD, ¬ify_fd, + &zmq_fd_size) + == -1) + return -1; + if (items_[i].events) { + FD_SET (notify_fd, pollset_in.get ()); + if (maxfd < notify_fd) + maxfd = notify_fd; + } + } + // Else, the poll item is a raw file descriptor. Convert the poll item + // events to the appropriate fd_sets. + else { + if (items_[i].events & ZMQ_POLLIN) + FD_SET (items_[i].fd, pollset_in.get ()); + if (items_[i].events & ZMQ_POLLOUT) + FD_SET (items_[i].fd, pollset_out.get ()); + if (items_[i].events & ZMQ_POLLERR) + FD_SET (items_[i].fd, pollset_err.get ()); + if (maxfd < items_[i].fd) + maxfd = items_[i].fd; + } + } + + zmq::optimized_fd_set_t inset (nitems_); + zmq::optimized_fd_set_t outset (nitems_); + zmq::optimized_fd_set_t errset (nitems_); +#endif + + bool first_pass = true; + int nevents = 0; + + while (true) { +#if defined ZMQ_POLL_BASED_ON_POLL + + // Compute the timeout for the subsequent poll. + const zmq::timeout_t timeout = + zmq::compute_timeout (first_pass, timeout_, now, end); + + // Wait for events. + { + const int rc = poll (&pollfds[0], nitems_, timeout); + if (rc == -1 && errno == EINTR) { + return -1; + } + errno_assert (rc >= 0); + } + // Check for the events. + for (int i = 0; i != nitems_; i++) { + items_[i].revents = 0; + + // The poll item is a 0MQ socket. Retrieve pending events + // using the ZMQ_EVENTS socket option. + if (items_[i].socket) { + size_t zmq_events_size = sizeof (uint32_t); + uint32_t zmq_events; + if (zmq_getsockopt (items_[i].socket, ZMQ_EVENTS, &zmq_events, + &zmq_events_size) + == -1) { + return -1; + } + if ((items_[i].events & ZMQ_POLLOUT) + && (zmq_events & ZMQ_POLLOUT)) + items_[i].revents |= ZMQ_POLLOUT; + if ((items_[i].events & ZMQ_POLLIN) + && (zmq_events & ZMQ_POLLIN)) + items_[i].revents |= ZMQ_POLLIN; + } + // Else, the poll item is a raw file descriptor, simply convert + // the events to zmq_pollitem_t-style format. + else { + if (pollfds[i].revents & POLLIN) + items_[i].revents |= ZMQ_POLLIN; + if (pollfds[i].revents & POLLOUT) + items_[i].revents |= ZMQ_POLLOUT; + if (pollfds[i].revents & POLLPRI) + items_[i].revents |= ZMQ_POLLPRI; + if (pollfds[i].revents & ~(POLLIN | POLLOUT | POLLPRI)) + items_[i].revents |= ZMQ_POLLERR; + } + + if (items_[i].revents) + nevents++; + } + +#else + + // Compute the timeout for the subsequent poll. + timeval timeout; + timeval *ptimeout; + if (first_pass) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + ptimeout = &timeout; + } else if (timeout_ < 0) + ptimeout = NULL; + else { + timeout.tv_sec = static_cast ((end - now) / 1000); + timeout.tv_usec = static_cast ((end - now) % 1000 * 1000); + ptimeout = &timeout; + } + + // Wait for events. Ignore interrupts if there's infinite timeout. + while (true) { + memcpy (inset.get (), pollset_in.get (), + zmq::valid_pollset_bytes (*pollset_in.get ())); + memcpy (outset.get (), pollset_out.get (), + zmq::valid_pollset_bytes (*pollset_out.get ())); + memcpy (errset.get (), pollset_err.get (), + zmq::valid_pollset_bytes (*pollset_err.get ())); +#if defined ZMQ_HAVE_WINDOWS + int rc = + select (0, inset.get (), outset.get (), errset.get (), ptimeout); + if (unlikely (rc == SOCKET_ERROR)) { + errno = zmq::wsa_error_to_errno (WSAGetLastError ()); + wsa_assert (errno == ENOTSOCK); + return -1; + } +#else + int rc = select (maxfd + 1, inset.get (), outset.get (), + errset.get (), ptimeout); + if (unlikely (rc == -1)) { + errno_assert (errno == EINTR || errno == EBADF); + return -1; + } +#endif + break; + } + + // Check for the events. + for (int i = 0; i != nitems_; i++) { + items_[i].revents = 0; + + // The poll item is a 0MQ socket. Retrieve pending events + // using the ZMQ_EVENTS socket option. + if (items_[i].socket) { + size_t zmq_events_size = sizeof (uint32_t); + uint32_t zmq_events; + if (zmq_getsockopt (items_[i].socket, ZMQ_EVENTS, &zmq_events, + &zmq_events_size) + == -1) + return -1; + if ((items_[i].events & ZMQ_POLLOUT) + && (zmq_events & ZMQ_POLLOUT)) + items_[i].revents |= ZMQ_POLLOUT; + if ((items_[i].events & ZMQ_POLLIN) + && (zmq_events & ZMQ_POLLIN)) + items_[i].revents |= ZMQ_POLLIN; + } + // Else, the poll item is a raw file descriptor, simply convert + // the events to zmq_pollitem_t-style format. + else { + if (FD_ISSET (items_[i].fd, inset.get ())) + items_[i].revents |= ZMQ_POLLIN; + if (FD_ISSET (items_[i].fd, outset.get ())) + items_[i].revents |= ZMQ_POLLOUT; + if (FD_ISSET (items_[i].fd, errset.get ())) + items_[i].revents |= ZMQ_POLLERR; + } + + if (items_[i].revents) + nevents++; + } +#endif + + // If timeout is zero, exit immediately whether there are events or not. + if (timeout_ == 0) + break; + + // If there are events to return, we can exit immediately. + if (nevents) + break; + + // At this point we are meant to wait for events but there are none. + // If timeout is infinite we can just loop until we get some events. + if (timeout_ < 0) { + if (first_pass) + first_pass = false; + continue; + } + + // The timeout is finite and there are no events. In the first pass + // we get a timestamp of when the polling have begun. (We assume that + // first pass have taken negligible time). We also compute the time + // when the polling should time out. + if (first_pass) { + now = clock.now_ms (); + end = now + timeout_; + if (now == end) + break; + first_pass = false; + continue; + } + + // Find out whether timeout have expired. + now = clock.now_ms (); + if (now >= end) + break; + } + + return nevents; +#else + // Exotic platforms that support neither poll() nor select(). + errno = ENOTSUP; + return -1; +#endif +} + +// The poller functionality + +void *zmq_poller_new (void) +{ + zmq::socket_poller_t *poller = new (std::nothrow) zmq::socket_poller_t; + if (!poller) { + errno = ENOMEM; + } + return poller; +} + +int zmq_poller_destroy (void **poller_p_) +{ + if (poller_p_) { + const zmq::socket_poller_t *const poller = + static_cast (*poller_p_); + if (poller && poller->check_tag ()) { + delete poller; + *poller_p_ = NULL; + return 0; + } + } + errno = EFAULT; + return -1; +} + + +static int check_poller (void *const poller_) +{ + if (!poller_ + || !(static_cast (poller_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + return 0; +} + +static int check_events (const short events_) +{ + if (events_ & ~(ZMQ_POLLIN | ZMQ_POLLOUT | ZMQ_POLLERR | ZMQ_POLLPRI)) { + errno = EINVAL; + return -1; + } + return 0; +} + +static int check_poller_registration_args (void *const poller_, void *const s_) +{ + if (-1 == check_poller (poller_)) + return -1; + + if (!s_ || !(static_cast (s_))->check_tag ()) { + errno = ENOTSOCK; + return -1; + } + + return 0; +} + +static int check_poller_fd_registration_args (void *const poller_, + const zmq::fd_t fd_) +{ + if (-1 == check_poller (poller_)) + return -1; + + if (fd_ == zmq::retired_fd) { + errno = EBADF; + return -1; + } + + return 0; +} + +int zmq_poller_size (void *poller_) +{ + if (-1 == check_poller (poller_)) + return -1; + + return (static_cast (poller_))->size (); +} + +int zmq_poller_add (void *poller_, void *s_, void *user_data_, short events_) +{ + if (-1 == check_poller_registration_args (poller_, s_) + || -1 == check_events (events_)) + return -1; + + zmq::socket_base_t *socket = static_cast (s_); + + return (static_cast (poller_)) + ->add (socket, user_data_, events_); +} + +int zmq_poller_add_fd (void *poller_, + zmq::fd_t fd_, + void *user_data_, + short events_) +{ + if (-1 == check_poller_fd_registration_args (poller_, fd_) + || -1 == check_events (events_)) + return -1; + + return (static_cast (poller_)) + ->add_fd (fd_, user_data_, events_); +} + + +int zmq_poller_modify (void *poller_, void *s_, short events_) +{ + if (-1 == check_poller_registration_args (poller_, s_) + || -1 == check_events (events_)) + return -1; + + const zmq::socket_base_t *const socket = + static_cast (s_); + + return (static_cast (poller_)) + ->modify (socket, events_); +} + +int zmq_poller_modify_fd (void *poller_, zmq::fd_t fd_, short events_) +{ + if (-1 == check_poller_fd_registration_args (poller_, fd_) + || -1 == check_events (events_)) + return -1; + + return (static_cast (poller_)) + ->modify_fd (fd_, events_); +} + +int zmq_poller_remove (void *poller_, void *s_) +{ + if (-1 == check_poller_registration_args (poller_, s_)) + return -1; + + zmq::socket_base_t *socket = static_cast (s_); + + return (static_cast (poller_))->remove (socket); +} + +int zmq_poller_remove_fd (void *poller_, zmq::fd_t fd_) +{ + if (-1 == check_poller_fd_registration_args (poller_, fd_)) + return -1; + + return (static_cast (poller_))->remove_fd (fd_); +} + +int zmq_poller_wait (void *poller_, zmq_poller_event_t *event_, long timeout_) +{ + const int rc = zmq_poller_wait_all (poller_, event_, 1, timeout_); + + if (rc < 0 && event_) { + event_->socket = NULL; + event_->fd = zmq::retired_fd; + event_->user_data = NULL; + event_->events = 0; + } + // wait_all returns number of events, but we return 0 for any success + return rc >= 0 ? 0 : rc; +} + +int zmq_poller_wait_all (void *poller_, + zmq_poller_event_t *events_, + int n_events_, + long timeout_) +{ + if (-1 == check_poller (poller_)) + return -1; + + if (!events_) { + errno = EFAULT; + return -1; + } + if (n_events_ < 0) { + errno = EINVAL; + return -1; + } + + const int rc = + (static_cast (poller_)) + ->wait (reinterpret_cast (events_), + n_events_, timeout_); + + return rc; +} + +int zmq_poller_fd (void *poller_, zmq_fd_t *fd_) +{ + if (!poller_ + || !(static_cast (poller_)->check_tag ())) { + errno = EFAULT; + return -1; + } + return static_cast (poller_)->signaler_fd (fd_); +} + +// Peer-specific state + +int zmq_socket_get_peer_state (void *s_, + const void *routing_id_, + size_t routing_id_size_) +{ + const zmq::socket_base_t *const s = as_socket_base_t (s_); + if (!s) + return -1; + + return s->get_peer_state (routing_id_, routing_id_size_); +} + +// Timers + +void *zmq_timers_new (void) +{ + zmq::timers_t *timers = new (std::nothrow) zmq::timers_t; + alloc_assert (timers); + return timers; +} + +int zmq_timers_destroy (void **timers_p_) +{ + void *timers = *timers_p_; + if (!timers || !(static_cast (timers))->check_tag ()) { + errno = EFAULT; + return -1; + } + delete (static_cast (timers)); + *timers_p_ = NULL; + return 0; +} + +int zmq_timers_add (void *timers_, + size_t interval_, + zmq_timer_fn handler_, + void *arg_) +{ + if (!timers_ || !(static_cast (timers_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + return (static_cast (timers_)) + ->add (interval_, handler_, arg_); +} + +int zmq_timers_cancel (void *timers_, int timer_id_) +{ + if (!timers_ || !(static_cast (timers_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + return (static_cast (timers_))->cancel (timer_id_); +} + +int zmq_timers_set_interval (void *timers_, int timer_id_, size_t interval_) +{ + if (!timers_ || !(static_cast (timers_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + return (static_cast (timers_)) + ->set_interval (timer_id_, interval_); +} + +int zmq_timers_reset (void *timers_, int timer_id_) +{ + if (!timers_ || !(static_cast (timers_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + return (static_cast (timers_))->reset (timer_id_); +} + +long zmq_timers_timeout (void *timers_) +{ + if (!timers_ || !(static_cast (timers_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + return (static_cast (timers_))->timeout (); +} + +int zmq_timers_execute (void *timers_) +{ + if (!timers_ || !(static_cast (timers_))->check_tag ()) { + errno = EFAULT; + return -1; + } + + return (static_cast (timers_))->execute (); +} + +// The proxy functionality + +int zmq_proxy (void *frontend_, void *backend_, void *capture_) +{ + if (!frontend_ || !backend_) { + errno = EFAULT; + return -1; + } + return zmq::proxy (static_cast (frontend_), + static_cast (backend_), + static_cast (capture_)); +} + +int zmq_proxy_steerable (void *frontend_, + void *backend_, + void *capture_, + void *control_) +{ + if (!frontend_ || !backend_) { + errno = EFAULT; + return -1; + } + return zmq::proxy (static_cast (frontend_), + static_cast (backend_), + static_cast (capture_), + static_cast (control_)); +} + +// The deprecated device functionality + +int zmq_device (int /* type */, void *frontend_, void *backend_) +{ + return zmq::proxy (static_cast (frontend_), + static_cast (backend_), NULL); +} + +// Probe library capabilities; for now, reports on transport and security + +int zmq_has (const char *capability_) +{ +#if defined(ZMQ_HAVE_IPC) + if (strcmp (capability_, zmq::protocol_name::ipc) == 0) + return true; +#endif +#if defined(ZMQ_HAVE_OPENPGM) + if (strcmp (capability_, zmq::protocol_name::pgm) == 0) + return true; +#endif +#if defined(ZMQ_HAVE_TIPC) + if (strcmp (capability_, zmq::protocol_name::tipc) == 0) + return true; +#endif +#if defined(ZMQ_HAVE_NORM) + if (strcmp (capability_, zmq::protocol_name::norm) == 0) + return true; +#endif +#if defined(ZMQ_HAVE_CURVE) + if (strcmp (capability_, "curve") == 0) + return true; +#endif +#if defined(HAVE_LIBGSSAPI_KRB5) + if (strcmp (capability_, "gssapi") == 0) + return true; +#endif +#if defined(ZMQ_HAVE_VMCI) + if (strcmp (capability_, zmq::protocol_name::vmci) == 0) + return true; +#endif +#if defined(ZMQ_BUILD_DRAFT_API) + if (strcmp (capability_, "draft") == 0) + return true; +#endif +#if defined(ZMQ_HAVE_WS) + if (strcmp (capability_, "WS") == 0) + return true; +#endif +#if defined(ZMQ_HAVE_WSS) + if (strcmp (capability_, "WSS") == 0) + return true; +#endif + // Whatever the application asked for, we don't have + return false; +} + +int zmq_socket_monitor_pipes_stats (void *s_) +{ + zmq::socket_base_t *s = as_socket_base_t (s_); + if (!s) + return -1; + return s->query_pipes_stats (); +} diff --git a/3rd/libzmq/src/zmq_draft.h b/3rd/libzmq/src/zmq_draft.h new file mode 100644 index 00000000..b384d035 --- /dev/null +++ b/3rd/libzmq/src/zmq_draft.h @@ -0,0 +1,172 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_DRAFT_H_INCLUDED__ +#define __ZMQ_DRAFT_H_INCLUDED__ + +/******************************************************************************/ +/* These functions are DRAFT and disabled in stable releases, and subject to */ +/* change at ANY time until declared stable. */ +/******************************************************************************/ + +#ifndef ZMQ_BUILD_DRAFT_API + +/* DRAFT Socket types. */ +#define ZMQ_SERVER 12 +#define ZMQ_CLIENT 13 +#define ZMQ_RADIO 14 +#define ZMQ_DISH 15 +#define ZMQ_GATHER 16 +#define ZMQ_SCATTER 17 +#define ZMQ_DGRAM 18 +#define ZMQ_PEER 19 +#define ZMQ_CHANNEL 20 + +/* DRAFT Socket options. */ +#define ZMQ_ZAP_ENFORCE_DOMAIN 93 +#define ZMQ_LOOPBACK_FASTPATH 94 +#define ZMQ_METADATA 95 +#define ZMQ_MULTICAST_LOOP 96 +#define ZMQ_ROUTER_NOTIFY 97 +#define ZMQ_XPUB_MANUAL_LAST_VALUE 98 +#define ZMQ_SOCKS_USERNAME 99 +#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 +#define ZMQ_ONLY_FIRST_SUBSCRIBE 108 +#define ZMQ_RECONNECT_STOP 109 +#define ZMQ_HELLO_MSG 110 +#define ZMQ_DISCONNECT_MSG 111 +#define ZMQ_PRIORITY 112 + +/* DRAFT ZMQ_RECONNECT_STOP options */ +#define ZMQ_RECONNECT_STOP_CONN_REFUSED 0x1 +#define ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED 0x2 +#define ZMQ_RECONNECT_STOP_AFTER_DISCONNECT 0x3 + +/* DRAFT Context options */ +#define ZMQ_ZERO_COPY_RECV 10 + +/* DRAFT Context methods. */ +int zmq_ctx_set_ext (void *context_, + int option_, + const void *optval_, + size_t optvallen_); +int zmq_ctx_get_ext (void *context_, + int option_, + void *optval_, + size_t *optvallen_); + +/* DRAFT Socket methods. */ +int zmq_join (void *s_, const char *group_); +int zmq_leave (void *s_, const char *group_); + +/* DRAFT Msg methods. */ +int zmq_msg_set_routing_id (zmq_msg_t *msg_, uint32_t routing_id_); +uint32_t zmq_msg_routing_id (zmq_msg_t *msg_); +int zmq_msg_set_group (zmq_msg_t *msg_, const char *group_); +const char *zmq_msg_group (zmq_msg_t *msg_); +int zmq_msg_init_buffer (zmq_msg_t *msg_, const void *buf_, size_t size_); + +/* DRAFT Msg property names. */ +#define ZMQ_MSG_PROPERTY_ROUTING_ID "Routing-Id" +#define ZMQ_MSG_PROPERTY_SOCKET_TYPE "Socket-Type" +#define ZMQ_MSG_PROPERTY_USER_ID "User-Id" +#define ZMQ_MSG_PROPERTY_PEER_ADDRESS "Peer-Address" + +/* Router notify options */ +#define ZMQ_NOTIFY_CONNECT 1 +#define ZMQ_NOTIFY_DISCONNECT 2 + +/******************************************************************************/ +/* Poller polling on sockets,fd and thread-safe sockets */ +/******************************************************************************/ + +#if defined _WIN32 +typedef SOCKET zmq_fd_t; +#else +typedef int zmq_fd_t; +#endif + +typedef struct zmq_poller_event_t +{ + void *socket; + zmq_fd_t fd; + void *user_data; + short events; +} zmq_poller_event_t; + +void *zmq_poller_new (void); +int zmq_poller_destroy (void **poller_p_); +int zmq_poller_size (void *poller_); +int zmq_poller_add (void *poller_, + void *socket_, + void *user_data_, + short events_); +int zmq_poller_modify (void *poller_, void *socket_, short events_); +int zmq_poller_remove (void *poller_, void *socket_); +int zmq_poller_wait (void *poller_, zmq_poller_event_t *event_, long timeout_); +int zmq_poller_wait_all (void *poller_, + zmq_poller_event_t *events_, + int n_events_, + long timeout_); +zmq_fd_t zmq_poller_fd (void *poller_); + +int zmq_poller_add_fd (void *poller_, + zmq_fd_t fd_, + void *user_data_, + short events_); +int zmq_poller_modify_fd (void *poller_, zmq_fd_t fd_, short events_); +int zmq_poller_remove_fd (void *poller_, zmq_fd_t fd_); + +int zmq_socket_get_peer_state (void *socket_, + const void *routing_id_, + size_t routing_id_size_); + +/* DRAFT Socket monitoring events */ +#define ZMQ_EVENT_PIPES_STATS 0x10000 + +#define ZMQ_CURRENT_EVENT_VERSION 1 +#define ZMQ_CURRENT_EVENT_VERSION_DRAFT 2 + +#define ZMQ_EVENT_ALL_V1 ZMQ_EVENT_ALL +#define ZMQ_EVENT_ALL_V2 ZMQ_EVENT_ALL_V1 | ZMQ_EVENT_PIPES_STATS + +int zmq_socket_monitor_versioned ( + void *s_, const char *addr_, uint64_t events_, int event_version_, int type_); +int zmq_socket_monitor_pipes_stats (void *s_); + +#endif // ZMQ_BUILD_DRAFT_API + +#endif //ifndef __ZMQ_DRAFT_H_INCLUDED__ diff --git a/3rd/libzmq/src/zmq_utils.cpp b/3rd/libzmq/src/zmq_utils.cpp new file mode 100644 index 00000000..e83fc8f1 --- /dev/null +++ b/3rd/libzmq/src/zmq_utils.cpp @@ -0,0 +1,326 @@ +/* + Copyright (c) 2007-2017 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 . +*/ + +#include "precompiled.hpp" + +#include "macros.hpp" +#include "clock.hpp" +#include "err.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" +#include "atomic_ptr.hpp" +#include "random.hpp" +#include +#include + +#if !defined ZMQ_HAVE_WINDOWS +#include +#endif + +#if defined(ZMQ_USE_TWEETNACL) +#include "tweetnacl.h" +#elif defined(ZMQ_USE_LIBSODIUM) +#include "sodium.h" +#endif + +void zmq_sleep (int seconds_) +{ +#if defined ZMQ_HAVE_WINDOWS + Sleep (seconds_ * 1000); +#else + sleep (seconds_); +#endif +} + +void *zmq_stopwatch_start () +{ + uint64_t *watch = static_cast (malloc (sizeof (uint64_t))); + alloc_assert (watch); + *watch = zmq::clock_t::now_us (); + return static_cast (watch); +} + +unsigned long zmq_stopwatch_intermediate (void *watch_) +{ + const uint64_t end = zmq::clock_t::now_us (); + const uint64_t start = *static_cast (watch_); + return static_cast (end - start); +} + +unsigned long zmq_stopwatch_stop (void *watch_) +{ + const unsigned long res = zmq_stopwatch_intermediate (watch_); + free (watch_); + return res; +} + +void *zmq_threadstart (zmq_thread_fn *func_, void *arg_) +{ + zmq::thread_t *thread = new (std::nothrow) zmq::thread_t; + alloc_assert (thread); + thread->start (func_, arg_, "ZMQapp"); + return thread; +} + +void zmq_threadclose (void *thread_) +{ + zmq::thread_t *p_thread = static_cast (thread_); + p_thread->stop (); + LIBZMQ_DELETE (p_thread); +} + +// Z85 codec, taken from 0MQ RFC project, implements RFC32 Z85 encoding + +// Maps base 256 to base 85 +static char encoder[85 + 1] = {"0123456789" + "abcdefghij" + "klmnopqrst" + "uvwxyzABCD" + "EFGHIJKLMN" + "OPQRSTUVWX" + "YZ.-:+=^!/" + "*?&<>()[]{" + "}@%$#"}; + +// Maps base 85 to base 256 +// We chop off lower 32 and higher 128 ranges +// 0xFF denotes invalid characters within this range +static uint8_t decoder[96] = { + 0xFF, 0x44, 0xFF, 0x54, 0x53, 0x52, 0x48, 0xFF, 0x4B, 0x4C, 0x46, 0x41, + 0xFF, 0x3F, 0x3E, 0x45, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x40, 0xFF, 0x49, 0x42, 0x4A, 0x47, 0x51, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x4D, + 0xFF, 0x4E, 0x43, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, + 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x4F, 0xFF, 0x50, 0xFF, 0xFF}; + +// -------------------------------------------------------------------------- +// Encode a binary frame as a string; destination string MUST be at least +// size * 5 / 4 bytes long plus 1 byte for the null terminator. Returns +// dest. Size must be a multiple of 4. +// Returns NULL and sets errno = EINVAL for invalid input. + +char *zmq_z85_encode (char *dest_, const uint8_t *data_, size_t size_) +{ + if (size_ % 4 != 0) { + errno = EINVAL; + return NULL; + } + unsigned int char_nbr = 0; + unsigned int byte_nbr = 0; + uint32_t value = 0; + while (byte_nbr < size_) { + // Accumulate value in base 256 (binary) + value = value * 256 + data_[byte_nbr++]; + if (byte_nbr % 4 == 0) { + // Output value in base 85 + unsigned int divisor = 85 * 85 * 85 * 85; + while (divisor) { + dest_[char_nbr++] = encoder[value / divisor % 85]; + divisor /= 85; + } + value = 0; + } + } + assert (char_nbr == size_ * 5 / 4); + dest_[char_nbr] = 0; + return dest_; +} + + +// -------------------------------------------------------------------------- +// Decode an encoded string into a binary frame; dest must be at least +// strlen (string) * 4 / 5 bytes long. Returns dest. strlen (string) +// must be a multiple of 5. +// Returns NULL and sets errno = EINVAL for invalid input. + +uint8_t *zmq_z85_decode (uint8_t *dest_, const char *string_) +{ + unsigned int byte_nbr = 0; + unsigned int char_nbr = 0; + uint32_t value = 0; + size_t src_len = strlen (string_); + + if (src_len < 5 || src_len % 5 != 0) + goto error_inval; + + while (string_[char_nbr]) { + // Accumulate value in base 85 + if (UINT32_MAX / 85 < value) { + // Invalid z85 encoding, represented value exceeds 0xffffffff + goto error_inval; + } + value *= 85; + const uint8_t index = string_[char_nbr++] - 32; + if (index >= sizeof (decoder)) { + // Invalid z85 encoding, character outside range + goto error_inval; + } + const uint32_t summand = decoder[index]; + if (summand == 0xFF || summand > (UINT32_MAX - value)) { + // Invalid z85 encoding, invalid character or represented value exceeds 0xffffffff + goto error_inval; + } + value += summand; + if (char_nbr % 5 == 0) { + // Output value in base 256 + unsigned int divisor = 256 * 256 * 256; + while (divisor) { + dest_[byte_nbr++] = value / divisor % 256; + divisor /= 256; + } + value = 0; + } + } + if (char_nbr % 5 != 0) { + goto error_inval; + } + assert (byte_nbr == strlen (string_) * 4 / 5); + return dest_; + +error_inval: + errno = EINVAL; + return NULL; +} + +// -------------------------------------------------------------------------- +// Generate a public/private keypair with tweetnacl or libsodium. +// Generated keys will be 40 byte z85-encoded strings. +// Returns 0 on success, -1 on failure, setting errno. +// Sets errno = ENOTSUP in the absence of a CURVE library. + +int zmq_curve_keypair (char *z85_public_key_, char *z85_secret_key_) +{ +#if defined(ZMQ_HAVE_CURVE) +#if crypto_box_PUBLICKEYBYTES != 32 || crypto_box_SECRETKEYBYTES != 32 +#error "CURVE encryption library not built correctly" +#endif + + uint8_t public_key[32]; + uint8_t secret_key[32]; + + zmq::random_open (); + + const int res = crypto_box_keypair (public_key, secret_key); + zmq_z85_encode (z85_public_key_, public_key, 32); + zmq_z85_encode (z85_secret_key_, secret_key, 32); + + zmq::random_close (); + + return res; +#else + (void) z85_public_key_, (void) z85_secret_key_; + errno = ENOTSUP; + return -1; +#endif +} + +// -------------------------------------------------------------------------- +// Derive the public key from a private key using tweetnacl or libsodium. +// Derived key will be 40 byte z85-encoded string. +// Returns 0 on success, -1 on failure, setting errno. +// Sets errno = ENOTSUP in the absence of a CURVE library. + +int zmq_curve_public (char *z85_public_key_, const char *z85_secret_key_) +{ +#if defined(ZMQ_HAVE_CURVE) +#if crypto_box_PUBLICKEYBYTES != 32 || crypto_box_SECRETKEYBYTES != 32 +#error "CURVE encryption library not built correctly" +#endif + + uint8_t public_key[32]; + uint8_t secret_key[32]; + + zmq::random_open (); + + if (zmq_z85_decode (secret_key, z85_secret_key_) == NULL) + return -1; + + // Return codes are suppressed as none of these can actually fail. + crypto_scalarmult_base (public_key, secret_key); + zmq_z85_encode (z85_public_key_, public_key, 32); + + zmq::random_close (); + + return 0; +#else + (void) z85_public_key_, (void) z85_secret_key_; + errno = ENOTSUP; + return -1; +#endif +} + + +// -------------------------------------------------------------------------- +// Initialize a new atomic counter, which is set to zero + +void *zmq_atomic_counter_new (void) +{ + zmq::atomic_counter_t *counter = new (std::nothrow) zmq::atomic_counter_t; + alloc_assert (counter); + return counter; +} + +// Se the value of the atomic counter + +void zmq_atomic_counter_set (void *counter_, int value_) +{ + (static_cast (counter_))->set (value_); +} + +// Increment the atomic counter, and return the old value + +int zmq_atomic_counter_inc (void *counter_) +{ + return (static_cast (counter_))->add (1); +} + +// Decrement the atomic counter and return 1 (if counter >= 1), or +// 0 if counter hit zero. + +int zmq_atomic_counter_dec (void *counter_) +{ + return (static_cast (counter_))->sub (1) ? 1 : 0; +} + +// Return actual value of atomic counter + +int zmq_atomic_counter_value (void *counter_) +{ + return (static_cast (counter_))->get (); +} + +// Destroy atomic counter, and set reference to NULL + +void zmq_atomic_counter_destroy (void **counter_p_) +{ + delete (static_cast (*counter_p_)); + *counter_p_ = NULL; +} diff --git a/3rd/libzmq/src/zmtp_engine.cpp b/3rd/libzmq/src/zmtp_engine.cpp new file mode 100644 index 00000000..0bde8660 --- /dev/null +++ b/3rd/libzmq/src/zmtp_engine.cpp @@ -0,0 +1,587 @@ +/* + 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 . +*/ + +#include "precompiled.hpp" +#include "macros.hpp" + +#include +#include + +#ifndef ZMQ_HAVE_WINDOWS +#include +#endif + +#include +#include + +#include "zmtp_engine.hpp" +#include "io_thread.hpp" +#include "session_base.hpp" +#include "v1_encoder.hpp" +#include "v1_decoder.hpp" +#include "v2_encoder.hpp" +#include "v2_decoder.hpp" +#include "v3_1_encoder.hpp" +#include "null_mechanism.hpp" +#include "plain_client.hpp" +#include "plain_server.hpp" +#include "gssapi_client.hpp" +#include "gssapi_server.hpp" +#include "curve_client.hpp" +#include "curve_server.hpp" +#include "raw_decoder.hpp" +#include "raw_encoder.hpp" +#include "config.hpp" +#include "err.hpp" +#include "ip.hpp" +#include "likely.hpp" +#include "wire.hpp" + +zmq::zmtp_engine_t::zmtp_engine_t ( + fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_) : + stream_engine_base_t (fd_, options_, endpoint_uri_pair_, true), + _greeting_size (v2_greeting_size), + _greeting_bytes_read (0), + _subscription_required (false), + _heartbeat_timeout (0) +{ + _next_msg = static_cast ( + &zmtp_engine_t::routing_id_msg); + _process_msg = static_cast ( + &zmtp_engine_t::process_routing_id_msg); + + int rc = _pong_msg.init (); + errno_assert (rc == 0); + + rc = _routing_id_msg.init (); + errno_assert (rc == 0); + + if (_options.heartbeat_interval > 0) { + _heartbeat_timeout = _options.heartbeat_timeout; + if (_heartbeat_timeout == -1) + _heartbeat_timeout = _options.heartbeat_interval; + } +} + +zmq::zmtp_engine_t::~zmtp_engine_t () +{ + const int rc = _routing_id_msg.close (); + errno_assert (rc == 0); +} + +void zmq::zmtp_engine_t::plug_internal () +{ + // start optional timer, to prevent handshake hanging on no input + set_handshake_timer (); + + // Send the 'length' and 'flags' fields of the routing id message. + // The 'length' field is encoded in the long format. + _outpos = _greeting_send; + _outpos[_outsize++] = UCHAR_MAX; + put_uint64 (&_outpos[_outsize], _options.routing_id_size + 1); + _outsize += 8; + _outpos[_outsize++] = 0x7f; + + set_pollin (); + set_pollout (); + // Flush all the data that may have been already received downstream. + in_event (); +} + +// Position of the revision and minor fields in the greeting. +const size_t revision_pos = 10; +const size_t minor_pos = 11; + +bool zmq::zmtp_engine_t::handshake () +{ + zmq_assert (_greeting_bytes_read < _greeting_size); + // Receive the greeting. + const int rc = receive_greeting (); + if (rc == -1) + return false; + const bool unversioned = rc != 0; + + if (!(this + ->*select_handshake_fun (unversioned, _greeting_recv[revision_pos], + _greeting_recv[minor_pos])) ()) + return false; + + // Start polling for output if necessary. + if (_outsize == 0) + set_pollout (); + + return true; +} + +int zmq::zmtp_engine_t::receive_greeting () +{ + bool unversioned = false; + while (_greeting_bytes_read < _greeting_size) { + const int n = read (_greeting_recv + _greeting_bytes_read, + _greeting_size - _greeting_bytes_read); + if (n == -1) { + if (errno != EAGAIN) + error (connection_error); + return -1; + } + + _greeting_bytes_read += n; + + // We have received at least one byte from the peer. + // If the first byte is not 0xff, we know that the + // peer is using unversioned protocol. + if (_greeting_recv[0] != 0xff) { + unversioned = true; + break; + } + + if (_greeting_bytes_read < signature_size) + continue; + + // Inspect the right-most bit of the 10th byte (which coincides + // with the 'flags' field if a regular message was sent). + // Zero indicates this is a header of a routing id message + // (i.e. the peer is using the unversioned protocol). + if (!(_greeting_recv[9] & 0x01)) { + unversioned = true; + break; + } + + // The peer is using versioned protocol. + receive_greeting_versioned (); + } + return unversioned ? 1 : 0; +} + +void zmq::zmtp_engine_t::receive_greeting_versioned () +{ + // Send the major version number. + if (_outpos + _outsize == _greeting_send + signature_size) { + if (_outsize == 0) + set_pollout (); + _outpos[_outsize++] = 3; // Major version number + } + + if (_greeting_bytes_read > signature_size) { + if (_outpos + _outsize == _greeting_send + signature_size + 1) { + if (_outsize == 0) + set_pollout (); + + // Use ZMTP/2.0 to talk to older peers. + if (_greeting_recv[revision_pos] == ZMTP_1_0 + || _greeting_recv[revision_pos] == ZMTP_2_0) + _outpos[_outsize++] = _options.type; + else { + _outpos[_outsize++] = 1; // Minor version number + memset (_outpos + _outsize, 0, 20); + + zmq_assert (_options.mechanism == ZMQ_NULL + || _options.mechanism == ZMQ_PLAIN + || _options.mechanism == ZMQ_CURVE + || _options.mechanism == ZMQ_GSSAPI); + + if (_options.mechanism == ZMQ_NULL) + memcpy (_outpos + _outsize, "NULL", 4); + else if (_options.mechanism == ZMQ_PLAIN) + memcpy (_outpos + _outsize, "PLAIN", 5); + else if (_options.mechanism == ZMQ_GSSAPI) + memcpy (_outpos + _outsize, "GSSAPI", 6); + else if (_options.mechanism == ZMQ_CURVE) + memcpy (_outpos + _outsize, "CURVE", 5); + _outsize += 20; + memset (_outpos + _outsize, 0, 32); + _outsize += 32; + _greeting_size = v3_greeting_size; + } + } + } +} + +zmq::zmtp_engine_t::handshake_fun_t zmq::zmtp_engine_t::select_handshake_fun ( + bool unversioned_, unsigned char revision_, unsigned char minor_) +{ + // Is the peer using ZMTP/1.0 with no revision number? + if (unversioned_) { + return &zmtp_engine_t::handshake_v1_0_unversioned; + } + switch (revision_) { + case ZMTP_1_0: + return &zmtp_engine_t::handshake_v1_0; + case ZMTP_2_0: + return &zmtp_engine_t::handshake_v2_0; + case ZMTP_3_x: + switch (minor_) { + case 0: + return &zmtp_engine_t::handshake_v3_0; + default: + return &zmtp_engine_t::handshake_v3_1; + } + default: + return &zmtp_engine_t::handshake_v3_1; + } +} + +bool zmq::zmtp_engine_t::handshake_v1_0_unversioned () +{ + // We send and receive rest of routing id message + if (session ()->zap_enabled ()) { + // reject ZMTP 1.0 connections if ZAP is enabled + error (protocol_error); + return false; + } + + _encoder = new (std::nothrow) v1_encoder_t (_options.out_batch_size); + alloc_assert (_encoder); + + _decoder = new (std::nothrow) + v1_decoder_t (_options.in_batch_size, _options.maxmsgsize); + alloc_assert (_decoder); + + // We have already sent the message header. + // Since there is no way to tell the encoder to + // skip the message header, we simply throw that + // header data away. + const size_t header_size = + _options.routing_id_size + 1 >= UCHAR_MAX ? 10 : 2; + unsigned char tmp[10], *bufferp = tmp; + + // Prepare the routing id message and load it into encoder. + // Then consume bytes we have already sent to the peer. + int rc = _routing_id_msg.close (); + zmq_assert (rc == 0); + rc = _routing_id_msg.init_size (_options.routing_id_size); + zmq_assert (rc == 0); + memcpy (_routing_id_msg.data (), _options.routing_id, + _options.routing_id_size); + _encoder->load_msg (&_routing_id_msg); + const size_t buffer_size = _encoder->encode (&bufferp, header_size); + zmq_assert (buffer_size == header_size); + + // Make sure the decoder sees the data we have already received. + _inpos = _greeting_recv; + _insize = _greeting_bytes_read; + + // To allow for interoperability with peers that do not forward + // their subscriptions, we inject a phantom subscription message + // message into the incoming message stream. + if (_options.type == ZMQ_PUB || _options.type == ZMQ_XPUB) + _subscription_required = true; + + // We are sending our routing id now and the next message + // will come from the socket. + _next_msg = &zmtp_engine_t::pull_msg_from_session; + + // We are expecting routing id message. + _process_msg = static_cast ( + &zmtp_engine_t::process_routing_id_msg); + + return true; +} + +bool zmq::zmtp_engine_t::handshake_v1_0 () +{ + if (session ()->zap_enabled ()) { + // reject ZMTP 1.0 connections if ZAP is enabled + error (protocol_error); + return false; + } + + _encoder = new (std::nothrow) v1_encoder_t (_options.out_batch_size); + alloc_assert (_encoder); + + _decoder = new (std::nothrow) + v1_decoder_t (_options.in_batch_size, _options.maxmsgsize); + alloc_assert (_decoder); + + return true; +} + +bool zmq::zmtp_engine_t::handshake_v2_0 () +{ + if (session ()->zap_enabled ()) { + // reject ZMTP 2.0 connections if ZAP is enabled + error (protocol_error); + return false; + } + + _encoder = new (std::nothrow) v2_encoder_t (_options.out_batch_size); + alloc_assert (_encoder); + + _decoder = new (std::nothrow) v2_decoder_t ( + _options.in_batch_size, _options.maxmsgsize, _options.zero_copy); + alloc_assert (_decoder); + + return true; +} + +bool zmq::zmtp_engine_t::handshake_v3_x (const bool downgrade_sub_) +{ + if (_options.mechanism == ZMQ_NULL + && memcmp (_greeting_recv + 12, "NULL\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", + 20) + == 0) { + _mechanism = new (std::nothrow) + null_mechanism_t (session (), _peer_address, _options); + alloc_assert (_mechanism); + } else if (_options.mechanism == ZMQ_PLAIN + && memcmp (_greeting_recv + 12, + "PLAIN\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) + == 0) { + if (_options.as_server) + _mechanism = new (std::nothrow) + plain_server_t (session (), _peer_address, _options); + else + _mechanism = + new (std::nothrow) plain_client_t (session (), _options); + alloc_assert (_mechanism); + } +#ifdef ZMQ_HAVE_CURVE + else if (_options.mechanism == ZMQ_CURVE + && memcmp (_greeting_recv + 12, + "CURVE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) + == 0) { + if (_options.as_server) + _mechanism = new (std::nothrow) curve_server_t ( + session (), _peer_address, _options, downgrade_sub_); + else + _mechanism = new (std::nothrow) + curve_client_t (session (), _options, downgrade_sub_); + alloc_assert (_mechanism); + } +#endif +#ifdef HAVE_LIBGSSAPI_KRB5 + else if (_options.mechanism == ZMQ_GSSAPI + && memcmp (_greeting_recv + 12, + "GSSAPI\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) + == 0) { + if (_options.as_server) + _mechanism = new (std::nothrow) + gssapi_server_t (session (), _peer_address, _options); + else + _mechanism = + new (std::nothrow) gssapi_client_t (session (), _options); + alloc_assert (_mechanism); + } +#endif + else { + socket ()->event_handshake_failed_protocol ( + session ()->get_endpoint (), + ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH); + error (protocol_error); + return false; + } + _next_msg = &zmtp_engine_t::next_handshake_command; + _process_msg = &zmtp_engine_t::process_handshake_command; + + return true; +} + +bool zmq::zmtp_engine_t::handshake_v3_0 () +{ + _encoder = new (std::nothrow) v2_encoder_t (_options.out_batch_size); + alloc_assert (_encoder); + + _decoder = new (std::nothrow) v2_decoder_t ( + _options.in_batch_size, _options.maxmsgsize, _options.zero_copy); + alloc_assert (_decoder); + + return zmq::zmtp_engine_t::handshake_v3_x (true); +} + +bool zmq::zmtp_engine_t::handshake_v3_1 () +{ + _encoder = new (std::nothrow) v3_1_encoder_t (_options.out_batch_size); + alloc_assert (_encoder); + + _decoder = new (std::nothrow) v2_decoder_t ( + _options.in_batch_size, _options.maxmsgsize, _options.zero_copy); + alloc_assert (_decoder); + + return zmq::zmtp_engine_t::handshake_v3_x (false); +} + +int zmq::zmtp_engine_t::routing_id_msg (msg_t *msg_) +{ + const int rc = msg_->init_size (_options.routing_id_size); + errno_assert (rc == 0); + if (_options.routing_id_size > 0) + memcpy (msg_->data (), _options.routing_id, _options.routing_id_size); + _next_msg = &zmtp_engine_t::pull_msg_from_session; + return 0; +} + +int zmq::zmtp_engine_t::process_routing_id_msg (msg_t *msg_) +{ + if (_options.recv_routing_id) { + msg_->set_flags (msg_t::routing_id); + const int rc = session ()->push_msg (msg_); + errno_assert (rc == 0); + } else { + int rc = msg_->close (); + errno_assert (rc == 0); + rc = msg_->init (); + errno_assert (rc == 0); + } + + if (_subscription_required) { + msg_t subscription; + + // Inject the subscription message, so that also + // ZMQ 2.x peers receive published messages. + int rc = subscription.init_size (1); + errno_assert (rc == 0); + *static_cast (subscription.data ()) = 1; + rc = session ()->push_msg (&subscription); + errno_assert (rc == 0); + } + + _process_msg = &zmtp_engine_t::push_msg_to_session; + + return 0; +} + +int zmq::zmtp_engine_t::produce_ping_message (msg_t *msg_) +{ + // 16-bit TTL + \4PING == 7 + const size_t ping_ttl_len = msg_t::ping_cmd_name_size + 2; + zmq_assert (_mechanism != NULL); + + int rc = msg_->init_size (ping_ttl_len); + errno_assert (rc == 0); + msg_->set_flags (msg_t::command); + // Copy in the command message + memcpy (msg_->data (), "\4PING", msg_t::ping_cmd_name_size); + + uint16_t ttl_val = htons (_options.heartbeat_ttl); + memcpy (static_cast (msg_->data ()) + msg_t::ping_cmd_name_size, + &ttl_val, sizeof (ttl_val)); + + rc = _mechanism->encode (msg_); + _next_msg = &zmtp_engine_t::pull_and_encode; + if (!_has_timeout_timer && _heartbeat_timeout > 0) { + add_timer (_heartbeat_timeout, heartbeat_timeout_timer_id); + _has_timeout_timer = true; + } + return rc; +} + +int zmq::zmtp_engine_t::produce_pong_message (msg_t *msg_) +{ + zmq_assert (_mechanism != NULL); + + int rc = msg_->move (_pong_msg); + errno_assert (rc == 0); + + rc = _mechanism->encode (msg_); + _next_msg = &zmtp_engine_t::pull_and_encode; + return rc; +} + +int zmq::zmtp_engine_t::process_heartbeat_message (msg_t *msg_) +{ + if (msg_->is_ping ()) { + // 16-bit TTL + \4PING == 7 + const size_t ping_ttl_len = msg_t::ping_cmd_name_size + 2; + const size_t ping_max_ctx_len = 16; + uint16_t remote_heartbeat_ttl; + + // Get the remote heartbeat TTL to setup the timer + memcpy (&remote_heartbeat_ttl, + static_cast (msg_->data ()) + + msg_t::ping_cmd_name_size, + ping_ttl_len - msg_t::ping_cmd_name_size); + remote_heartbeat_ttl = ntohs (remote_heartbeat_ttl); + // The remote heartbeat is in 10ths of a second + // so we multiply it by 100 to get the timer interval in ms. + remote_heartbeat_ttl *= 100; + + if (!_has_ttl_timer && remote_heartbeat_ttl > 0) { + add_timer (remote_heartbeat_ttl, heartbeat_ttl_timer_id); + _has_ttl_timer = true; + } + + // As per ZMTP 3.1 the PING command might contain an up to 16 bytes + // context which needs to be PONGed back, so build the pong message + // here and store it. Truncate it if it's too long. + // Given the engine goes straight to out_event, sequential PINGs will + // not be a problem. + const size_t context_len = + std::min (msg_->size () - ping_ttl_len, ping_max_ctx_len); + const int rc = + _pong_msg.init_size (msg_t::ping_cmd_name_size + context_len); + errno_assert (rc == 0); + _pong_msg.set_flags (msg_t::command); + memcpy (_pong_msg.data (), "\4PONG", msg_t::ping_cmd_name_size); + if (context_len > 0) + memcpy (static_cast (_pong_msg.data ()) + + msg_t::ping_cmd_name_size, + static_cast (msg_->data ()) + ping_ttl_len, + context_len); + + _next_msg = static_cast ( + &zmtp_engine_t::produce_pong_message); + out_event (); + } + + return 0; +} + +int zmq::zmtp_engine_t::process_command_message (msg_t *msg_) +{ + const uint8_t cmd_name_size = + *(static_cast (msg_->data ())); + const size_t ping_name_size = msg_t::ping_cmd_name_size - 1; + const size_t sub_name_size = msg_t::sub_cmd_name_size - 1; + const size_t cancel_name_size = msg_t::cancel_cmd_name_size - 1; + // Malformed command + if (unlikely (msg_->size () < cmd_name_size + sizeof (cmd_name_size))) + return -1; + + const uint8_t *const cmd_name = + static_cast (msg_->data ()) + 1; + if (cmd_name_size == ping_name_size + && memcmp (cmd_name, "PING", cmd_name_size) == 0) + msg_->set_flags (zmq::msg_t::ping); + if (cmd_name_size == ping_name_size + && memcmp (cmd_name, "PONG", cmd_name_size) == 0) + msg_->set_flags (zmq::msg_t::pong); + if (cmd_name_size == sub_name_size + && memcmp (cmd_name, "SUBSCRIBE", cmd_name_size) == 0) + msg_->set_flags (zmq::msg_t::subscribe); + if (cmd_name_size == cancel_name_size + && memcmp (cmd_name, "CANCEL", cmd_name_size) == 0) + msg_->set_flags (zmq::msg_t::cancel); + + if (msg_->is_ping () || msg_->is_pong ()) + return process_heartbeat_message (msg_); + + return 0; +} diff --git a/3rd/libzmq/src/zmtp_engine.hpp b/3rd/libzmq/src/zmtp_engine.hpp new file mode 100644 index 00000000..10f4134c --- /dev/null +++ b/3rd/libzmq/src/zmtp_engine.hpp @@ -0,0 +1,136 @@ +/* + 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 . +*/ + +#ifndef __ZMQ_ZMTP_ENGINE_HPP_INCLUDED__ +#define __ZMQ_ZMTP_ENGINE_HPP_INCLUDED__ + +#include + +#include "fd.hpp" +#include "i_engine.hpp" +#include "io_object.hpp" +#include "i_encoder.hpp" +#include "i_decoder.hpp" +#include "options.hpp" +#include "socket_base.hpp" +#include "metadata.hpp" +#include "msg.hpp" +#include "stream_engine_base.hpp" + +namespace zmq +{ +// Protocol revisions +enum +{ + ZMTP_1_0 = 0, + ZMTP_2_0 = 1, + ZMTP_3_x = 3 +}; + +class io_thread_t; +class session_base_t; +class mechanism_t; + +// This engine handles any socket with SOCK_STREAM semantics, +// e.g. TCP socket or an UNIX domain socket. + +class zmtp_engine_t ZMQ_FINAL : public stream_engine_base_t +{ + public: + zmtp_engine_t (fd_t fd_, + const options_t &options_, + const endpoint_uri_pair_t &endpoint_uri_pair_); + ~zmtp_engine_t (); + + protected: + // Detects the protocol used by the peer. + bool handshake (); + + void plug_internal (); + + int process_command_message (msg_t *msg_); + int produce_ping_message (msg_t *msg_); + int process_heartbeat_message (msg_t *msg_); + int produce_pong_message (msg_t *msg_); + + private: + // Receive the greeting from the peer. + int receive_greeting (); + void receive_greeting_versioned (); + + typedef bool (zmtp_engine_t::*handshake_fun_t) (); + static handshake_fun_t select_handshake_fun (bool unversioned, + unsigned char revision, + unsigned char minor); + + bool handshake_v1_0_unversioned (); + bool handshake_v1_0 (); + bool handshake_v2_0 (); + bool handshake_v3_x (bool downgrade_sub); + bool handshake_v3_0 (); + bool handshake_v3_1 (); + + int routing_id_msg (msg_t *msg_); + int process_routing_id_msg (msg_t *msg_); + + msg_t _routing_id_msg; + + // Need to store PING payload for PONG + msg_t _pong_msg; + + static const size_t signature_size = 10; + + // Size of ZMTP/1.0 and ZMTP/2.0 greeting message + static const size_t v2_greeting_size = 12; + + // Size of ZMTP/3.0 greeting message + static const size_t v3_greeting_size = 64; + + // Expected greeting size. + size_t _greeting_size; + + // Greeting received from, and sent to peer + unsigned char _greeting_recv[v3_greeting_size]; + unsigned char _greeting_send[v3_greeting_size]; + + // Size of greeting received so far + unsigned int _greeting_bytes_read; + + // Indicates whether the engine is to inject a phantom + // subscription message into the incoming stream. + // Needed to support old peers. + bool _subscription_required; + + int _heartbeat_timeout; + + ZMQ_NON_COPYABLE_NOR_MOVABLE (zmtp_engine_t) +}; +} + +#endif diff --git a/3rd/libzmq/version.sh b/3rd/libzmq/version.sh new file mode 100644 index 00000000..b4425d09 --- /dev/null +++ b/3rd/libzmq/version.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# This script extracts the 0MQ version from include/zmq.h, which is the master +# location for this information. +# +if [ ! -f include/zmq.h ]; then + echo "version.sh: error: include/zmq.h does not exist" 1>&2 + exit 1 +fi +MAJOR=`egrep '^#define +ZMQ_VERSION_MAJOR +[0-9]+$' include/zmq.h` +MINOR=`egrep '^#define +ZMQ_VERSION_MINOR +[0-9]+$' include/zmq.h` +PATCH=`egrep '^#define +ZMQ_VERSION_PATCH +[0-9]+$' include/zmq.h` +if [ -z "$MAJOR" -o -z "$MINOR" -o -z "$PATCH" ]; then + echo "version.sh: error: could not extract version from include/zmq.h" 1>&2 + exit 1 +fi +MAJOR=`echo $MAJOR | awk '{ print $3 }'` +MINOR=`echo $MINOR | awk '{ print $3 }'` +PATCH=`echo $PATCH | awk '{ print $3 }'` +echo $MAJOR.$MINOR.$PATCH | tr -d '\n' + diff --git a/CMakeLists.txt b/CMakeLists.txt index 0844598c..25c2c5ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -361,6 +361,13 @@ if (NOT CROSSTOOLS) pip_find_lib(sodium) if(sodium_FOUND) + set(ENABLE_CPACK OFF CACHE BOOL "ENABLE_CPACK for zmq" FORCE) + set(BUILD_TESTS OFF CACHE BOOL "BUILD_TESTS for zmq" FORCE) + set(WITH_DOCS OFF CACHE BOOL "WITH_DOCS for zmq" FORCE) + set(WITH_DOC OFF CACHE BOOL "WITH_DOC for zmq" FORCE) + set(ENABLE_PRECOMPILED OFF CACHE BOOL "ENABLE_PRECOMPILED for zmq" FORCE) + set(SODIUM_INCLUDE_DIRS ${INCLUDE_DIRECTORIES} PARENT_SCOPE) + add_subdirectory(3rd/libzmq) pip_module(crypt "sodium" "PIP crypt support" "" "" "") pip_module(cloud "pip_io_utils" "PIP cloud support" "" "" "") endif()