diff options
author | Lutz Bichler <Lutz.Bichler@bmw.de> | 2020-11-30 09:44:44 +0100 |
---|---|---|
committer | Lutz Bichler <Lutz.Bichler@bmw.de> | 2020-11-30 09:44:44 +0100 |
commit | 710a8613ee5bd9eb490addecd7f2ee8049c4fd0c (patch) | |
tree | 49215269c551ac7eba482b3d9f6f084f56bf2fd3 | |
parent | 46e6856a1a61f4b24e29c1f2d3ed0b4ee7da0ede (diff) | |
download | vSomeIP-710a8613ee5bd9eb490addecd7f2ee8049c4fd0c.tar.gz |
vsomeip 3.1.20.13.1.20.1
105 files changed, 18035 insertions, 1730 deletions
@@ -1,6 +1,35 @@ Changes ======= +v3.1.20.1 +- CMakeLists.txt fixes + (by Martin Haase) +- Mark all services unavailable when the routing manager goes down + (by Philip Werner & dannyrhubarb) + +v3.1.20 +- SomeIP/TP optimization +- Fix for expired subscriptions + +v3.1.19 +- Log statistics for high frequent received remote events +- Avoid unintended deletion of all service instances in release_service() +- Prevent deletion of server endpoint on SubscribeEventGroupACK with multicast endpoint +- Do not lock the multicast mutex twice + +v3.1.18 +- Support boost 1.74 +- Ignore remote offers without referenced endpoint options +- Fixed race condition when removing security policies +- Ensure composite send operations have finished before resetting TCP server endpoint + +v3.1.17 +- Support AutoSAR E2E Profile 4 +- Support dynamic policies for offered services +- Fixed race condition between service shutdown and subscription +- Fixed race condition between service instances offered on the + same endpoint(s). + v3.1.16.1 - Restore IPv6 within UDP server endpoint - AOSP build adaptation to vsomeip3 libraries @@ -15,6 +44,23 @@ v3.1.15 - Speedup security policy handling - Enable building with boost v1.73.0 +v3.1.14.1 +- Merged extended support for static routing (versioning) + (by Jean-Patrice Laude jean-patrice.laude@renault.com) +- Merged simplification of build process for hello_world example + (by Nikolay Khilyuk nkh@ua.fm) +- Updated Android.bp to use boost 1.70 or higher +- Merged Android support for hello_world example + (by Nikolay Khilyuk nkh@ua.fm) +- Align response sample to documentation (do not specify application name) + (by JayHou houjie@lixiang.com) +- Call dlerror before calling dlsym to clear previous error + (by Oleg Kharitonov Oleg.Kharitonov@elektrobit.com) +- Get base path from environment variable for Android NDK + (by Nikolay Khilyuk nkh@ua.fm) +- Fixed wrong library naming + (by Nikolay Khilyuk nkh@ua.fm) + v3.1.14 - Fixed race conditions (application registration, subscription) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2ccc46..9fca08f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ set (VSOMEIP_COMPAT_NAME vsomeip) set (VSOMEIP_MAJOR_VERSION 3) set (VSOMEIP_MINOR_VERSION 1) -set (VSOMEIP_PATCH_VERSION 16) +set (VSOMEIP_PATCH_VERSION 20) set (VSOMEIP_HOTFIX_VERSION 1) set (VSOMEIP_VERSION ${VSOMEIP_MAJOR_VERSION}.${VSOMEIP_MINOR_VERSION}.${VSOMEIP_PATCH_VERSION}) @@ -31,9 +31,9 @@ endif() ################################################################################################### # Offer the user the choice of overriding the installation directories -set (INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") -set (INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") -set (INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") +set (INSTALL_LIB_DIR lib CACHE STRING "Installation directory for libraries") +set (INSTALL_BIN_DIR bin CACHE STRING "Installation directory for executables") +set (INSTALL_INCLUDE_DIR include CACHE STRING "Installation directory for header files") if (WIN32 AND NOT CYGWIN) set (DEF_INSTALL_CMAKE_DIR CMake) @@ -41,15 +41,7 @@ else () set (DEF_INSTALL_CMAKE_DIR lib/cmake/${VSOMEIP_NAME}) endif () -set (INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") - -# Make relative paths absolute (needed later on) -foreach (p LIB BIN INCLUDE CMAKE) - set (var INSTALL_${p}_DIR) - if (NOT IS_ABSOLUTE "${${var}}") - set (ABSOLUTE_${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") # Add all targets to the build-tree export set - endif () -endforeach () +set (INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE STRING "Installation directory for CMake files") ################################################################################################### # Set a default build type if none was specified @@ -135,6 +127,11 @@ else () set (VSOMEIP_ENABLE_MULTIPLE_ROUTING_MANAGERS 0) endif () +# Session handling configuration +if (ENABLE_SESSION_HANDLING_CONFIG) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVSOMEIP_HAS_SESSION_HANDLING_CONFIG") +endif () + ################################################################################ # Dependencies ################################################################################ @@ -173,8 +170,10 @@ else() endif() message( STATUS "Using boost version: ${VSOMEIP_BOOST_VERSION}" ) -if (${VSOMEIP_BOOST_VERSION} GREATER 107300) -message( ERROR "boost version ${VSOMEIP_BOOST_VERSION} is not (yet) supported. Latest supported version is 1.72.0" ) +if (${VSOMEIP_BOOST_VERSION} GREATER 107400) +message( ERROR "boost version ${VSOMEIP_BOOST_VERSION} is not (yet) supported. Latest supported version is 1.74.0" ) +elseif(${VSOMEIP_BOOST_VERSION} GREATER 107300) +set(VSOMEIP_BOOST_HELPER implementation/helper/1.74) elseif(${VSOMEIP_BOOST_VERSION} GREATER 106999) set(VSOMEIP_BOOST_HELPER implementation/helper/1.70) elseif(${VSOMEIP_BOOST_VERSION} GREATER 106599) @@ -214,6 +213,9 @@ endif () include_directories( interface +) + +include_directories(SYSTEM ${VSOMEIP_BOOST_HELPER} ${DLT_INCLUDE_DIRS} ) @@ -235,7 +237,6 @@ if (MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_WINSOCK_DEPRECATED_NO_WARNINGS -D_WIN32_WINNT=${BOOST_WINDOWS_VERSION} -DWIN32 -DBOOST_ASIO_DISABLE_IOCP /EHsc") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS -D_WIN32_WINNT=${BOOST_WINDOWS_VERSION} -DWIN32 -DBOOST_ASIO_DISABLE_IOCP /EHsc") set(USE_RT "") - set(Boost_LIBRARIES "") link_directories(${Boost_LIBRARY_DIR_DEBUG}) ADD_DEFINITIONS( -DBOOST_ALL_DYN_LINK ) else() @@ -676,6 +677,16 @@ else() set(TEST_SECOND_ADDRESS "ON") endif() +set(TEST_E2E_PROFILE_04 "OFF" CACHE BOOL + "Controls whether E2E Profile 04 tests should run or not") +if (ENABLE_SESSION_HANDLING_CONFIG) + set(TEST_E2E_PROFILE_04 "ON") +else () + message(WARNING "ENABLE_SESSION_HANDLING_CONFIG isn't set. " + "Test of E2E Profile 04 is not enabled.") +endif () + + SET(TEST_UID_DEFAULT_VALUE "123456789") SET(TEST_UID "${TEST_UID_DEFAULT_VALUE}" CACHE STRING "The User ID of the user running the test: Needed for security") diff --git a/implementation/configuration/include/configuration.hpp b/implementation/configuration/include/configuration.hpp index f20d040..a962298 100644 --- a/implementation/configuration/include/configuration.hpp +++ b/implementation/configuration/include/configuration.hpp @@ -130,6 +130,9 @@ public: virtual std::size_t get_io_thread_count(const std::string &_name) const = 0; virtual int get_io_thread_nice_level(const std::string &_name) const = 0; virtual std::size_t get_request_debouncing(const std::string &_name) const = 0; +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + virtual bool has_session_handling(const std::string &_name) const = 0; +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG virtual std::uint32_t get_max_message_size_local() const = 0; virtual std::uint32_t get_max_message_size_reliable(const std::string& _address, @@ -265,6 +268,11 @@ public: // routing shutdown timeout virtual std::uint32_t get_shutdown_timeout() const = 0; + + virtual bool log_statistics() const = 0; + virtual uint32_t get_statistics_interval() const = 0; + virtual uint32_t get_statistics_min_freq() const = 0; + virtual uint32_t get_statistics_max_messages() const = 0; }; } // namespace vsomeip_v3 diff --git a/implementation/configuration/include/configuration_impl.hpp b/implementation/configuration/include/configuration_impl.hpp index 5b77c18..66cfc84 100644 --- a/implementation/configuration/include/configuration_impl.hpp +++ b/implementation/configuration/include/configuration_impl.hpp @@ -115,6 +115,7 @@ public: VSOMEIP_EXPORT std::size_t get_io_thread_count(const std::string &_name) const; VSOMEIP_EXPORT int get_io_thread_nice_level(const std::string &_name) const; VSOMEIP_EXPORT std::size_t get_request_debouncing(const std::string &_name) const; + VSOMEIP_EXPORT bool has_session_handling(const std::string &_name) const; VSOMEIP_EXPORT std::set<std::pair<service_t, instance_t> > get_remote_services() const; @@ -232,6 +233,12 @@ public: std::uint16_t _port_service, method_t _method) const; VSOMEIP_EXPORT std::uint32_t get_shutdown_timeout() const; + + VSOMEIP_EXPORT bool log_statistics() const; + VSOMEIP_EXPORT uint32_t get_statistics_interval() const; + VSOMEIP_EXPORT uint32_t get_statistics_min_freq() const; + VSOMEIP_EXPORT uint32_t get_statistics_max_messages() const; + private: void read_data(const std::set<std::string> &_input, std::vector<configuration_element> &_elements, @@ -399,16 +406,19 @@ protected: std::map<std::string, std::tuple< client_t, - std::size_t, - std::size_t, - std::size_t, - std::size_t, + std::size_t, // max dispatchers + std::size_t, // max dispatch time + std::size_t, // thread count + std::size_t, // request debouncing std::map< plugin_type_e, std::set<std::string> - >, - int, - std::string + >, // plugins + int, // nice level + std::string // overlay +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + , bool // has session handling? +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG > > applications_; std::set<client_t> client_identifiers_; @@ -553,6 +563,11 @@ protected: mutable std::mutex secure_services_mutex_; std::map<service_t, std::set<instance_t> > secure_services_; + + bool log_statistics_; + uint32_t statistics_interval_; + uint32_t statistics_min_freq_; + uint32_t statistics_max_messages_; }; } // namespace cfg diff --git a/implementation/configuration/include/e2e.hpp b/implementation/configuration/include/e2e.hpp index 8c873c2..705c92d 100644 --- a/implementation/configuration/include/e2e.hpp +++ b/implementation/configuration/include/e2e.hpp @@ -19,16 +19,14 @@ struct e2e { typedef std::map<std::string, std::string> custom_parameters_t; e2e() : - data_id(0), variant(""), profile(""), service_id(0), event_id(0) { } - e2e(uint16_t _data_id, std::string _variant, std::string _profile, service_t _service_id, + e2e(std::string _variant, std::string _profile, service_t _service_id, event_t _event_id, custom_parameters_t&& _custom_parameters) : - data_id(_data_id), variant(_variant), profile(_profile), service_id(_service_id), @@ -37,7 +35,6 @@ struct e2e { } // common config - uint16_t data_id; std::string variant; std::string profile; service_t service_id; diff --git a/implementation/configuration/include/internal.hpp.in b/implementation/configuration/include/internal.hpp.in index 94a6e66..cc21616 100644 --- a/implementation/configuration/include/internal.hpp.in +++ b/implementation/configuration/include/internal.hpp.in @@ -84,6 +84,9 @@ #define VSOMEIP_MAX_DISPATCH_TIME 100 #define VSOMEIP_REQUEST_DEBOUNCE_TIME 10 +#define VSOMEIP_DEFAULT_STATISTICS_MAX_MSG 50 +#define VSOMEIP_DEFAULT_STATISTICS_MIN_FREQ 50 +#define VSOMEIP_DEFAULT_STATISTICS_INTERVAL 10000 #define VSOMEIP_MAX_WAIT_SENT 5 @@ -134,6 +137,7 @@ #define VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE 0x26 #define VSOMEIP_UPDATE_SECURITY_CREDENTIALS 0x27 #define VSOMEIP_DISTRIBUTE_SECURITY_POLICIES 0x28 +#define VSOMEIP_UPDATE_SECURITY_POLICY_INT 0x29 #define VSOMEIP_SEND_COMMAND_SIZE 13 #define VSOMEIP_SEND_COMMAND_INSTANCE_POS_MIN 7 diff --git a/implementation/configuration/include/internal_android.hpp b/implementation/configuration/include/internal_android.hpp index 8043676..28cbb5c 100644 --- a/implementation/configuration/include/internal_android.hpp +++ b/implementation/configuration/include/internal_android.hpp @@ -68,6 +68,9 @@ #define VSOMEIP_MAX_DISPATCH_TIME 100 #define VSOMEIP_REQUEST_DEBOUNCE_TIME 10 +#define VSOMEIP_DEFAULT_STATISTICS_MAX_MSG 50 +#define VSOMEIP_DEFAULT_STATISTICS_MIN_FREQ 50 +#define VSOMEIP_DEFAULT_STATISTICS_INTERVAL 10000 #define VSOMEIP_MAX_WAIT_SENT 5 @@ -118,6 +121,7 @@ #define VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE 0x26 #define VSOMEIP_UPDATE_SECURITY_CREDENTIALS 0x27 #define VSOMEIP_DISTRIBUTE_SECURITY_POLICIES 0x28 +#define VSOMEIP_UPDATE_SECURITY_POLICY_INT 0x29 #define VSOMEIP_SEND_COMMAND_SIZE 13 #define VSOMEIP_SEND_COMMAND_INSTANCE_POS_MIN 7 diff --git a/implementation/configuration/src/configuration_impl.cpp b/implementation/configuration/src/configuration_impl.cpp index 9f7aeab..475d265 100644 --- a/implementation/configuration/src/configuration_impl.cpp +++ b/implementation/configuration/src/configuration_impl.cpp @@ -90,7 +90,11 @@ configuration_impl::configuration_impl() npdu_default_debounce_resp_(VSOMEIP_DEFAULT_NPDU_DEBOUNCING_NANO), npdu_default_max_retention_requ_(VSOMEIP_DEFAULT_NPDU_MAXIMUM_RETENTION_NANO), npdu_default_max_retention_resp_(VSOMEIP_DEFAULT_NPDU_MAXIMUM_RETENTION_NANO), - shutdown_timeout_(VSOMEIP_DEFAULT_SHUTDOWN_TIMEOUT) { + shutdown_timeout_(VSOMEIP_DEFAULT_SHUTDOWN_TIMEOUT), + log_statistics_(true), + statistics_interval_(VSOMEIP_DEFAULT_STATISTICS_INTERVAL), + statistics_min_freq_(VSOMEIP_DEFAULT_STATISTICS_MIN_FREQ), + statistics_max_messages_(VSOMEIP_DEFAULT_STATISTICS_MAX_MSG) { unicast_ = unicast_.from_string(VSOMEIP_UNICAST_ADDRESS); netmask_ = netmask_.from_string(VSOMEIP_NETMASK); for (auto i = 0; i < ET_MAX; i++) @@ -190,6 +194,11 @@ configuration_impl::configuration_impl(const configuration_impl &_other) has_issued_methods_warning_ = _other.has_issued_methods_warning_; has_issued_clients_warning_ = _other.has_issued_clients_warning_; + + log_statistics_ = _other.log_statistics_; + statistics_interval_ = _other.statistics_interval_; + statistics_min_freq_ = _other.statistics_min_freq_; + statistics_max_messages_ = _other.statistics_max_messages_; } configuration_impl::~configuration_impl() { @@ -595,6 +604,25 @@ bool configuration_impl::load_logging( if (log_status_interval_ > 0) { log_status_ = true; } + } else if (its_key == "statistics") { + for (auto j : i->second) { + std::stringstream its_converter; + std::string its_sub_key(j.first); + std::string its_sub_value(j.second.data()); + if (its_sub_key == "interval") { + its_converter << std::dec << its_sub_value; + its_converter >> statistics_interval_; + if (statistics_interval_ > 0) { + log_statistics_ = true; + } + } else if (its_sub_key == "min-frequency") { + its_converter << std::dec << its_sub_value; + its_converter >> statistics_min_freq_; + } else if (its_sub_key == "max-messages") { + its_converter << std::dec << its_sub_value; + its_converter >> statistics_max_messages_; + } + } } } } catch (...) { @@ -646,6 +674,9 @@ void configuration_impl::load_application_data( std::map<plugin_type_e, std::set<std::string>> plugins; int its_io_thread_nice_level(VSOMEIP_IO_THREAD_NICE_LEVEL); std::string its_overlay; +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + bool has_session_handling(true); +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG for (auto i = _tree.begin(); i != _tree.end(); ++i) { std::string its_key(i->first); std::string its_value(i->second.data()); @@ -690,6 +721,11 @@ void configuration_impl::load_application_data( } else if (its_key == "overlay") { its_overlay = its_value; } +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + else if (its_key == "has_session_handling") { + has_session_handling = (its_value != "false"); + } +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG } if (its_name != "") { if (applications_.find(its_name) == applications_.end()) { @@ -708,7 +744,11 @@ void configuration_impl::load_application_data( = std::make_tuple(its_id, its_max_dispatchers, its_max_dispatch_time, its_io_thread_count, its_request_debounce_time, plugins, its_io_thread_nice_level, - its_overlay); + its_overlay +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + , has_session_handling +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG + ); } else { VSOMEIP_WARNING << "Multiple configurations for application " << its_name << ". Ignoring a configuration from " @@ -2431,6 +2471,18 @@ std::size_t configuration_impl::get_max_dispatch_time( return its_max_dispatch_time; } +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG +bool configuration_impl::has_session_handling(const std::string &_name) const { + + bool its_value(true); + + auto found_application = applications_.find(_name); + if (found_application != applications_.end()) + its_value = std::get<8>(found_application->second); + + return (its_value); +} +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG std::set<std::pair<service_t, instance_t> > configuration_impl::get_remote_services() const { @@ -2854,7 +2906,6 @@ void configuration_impl::load_e2e(const configuration_element &_element) { void configuration_impl::load_e2e_protected(const boost::property_tree::ptree &_tree) { - uint16_t data_id(0); std::string variant(""); std::string profile(""); service_t service_id(0); @@ -2864,15 +2915,7 @@ void configuration_impl::load_e2e_protected(const boost::property_tree::ptree &_ for (auto l = _tree.begin(); l != _tree.end(); ++l) { std::stringstream its_converter; - if (l->first == "data_id" && data_id == 0) { - std::string value = l->second.data(); - if (value.size() > 1 && value[0] == '0' && value[1] == 'x') { - its_converter << std::hex << value; - } else { - its_converter << std::dec << value; - } - its_converter >> data_id; - } else if (l->first == "service_id") { + if (l->first == "service_id") { std::string value = l->second.data(); if (value.size() > 1 && value[0] == '0' && value[1] == 'x') { its_converter << std::hex << value; @@ -2901,7 +2944,6 @@ void configuration_impl::load_e2e_protected(const boost::property_tree::ptree &_ } } e2e_configuration_[std::make_pair(service_id, event_id)] = std::make_shared<cfg::e2e>( - data_id, variant, profile, service_id, @@ -3910,7 +3952,7 @@ configuration_impl::has_overlay(const std::string &_name) const { void configuration_impl::load_overlay(const std::string &_name) { std::set<std::string> its_input; - std::vector<element> its_elements; + std::vector<configuration_element> its_elements; std::set<std::string> its_failed; auto its_application = applications_.find(_name); @@ -3933,5 +3975,21 @@ std::uint32_t configuration_impl::get_shutdown_timeout() const { return shutdown_timeout_; } +bool configuration_impl::log_statistics() const { + return log_statistics_; +} + +uint32_t configuration_impl::get_statistics_interval() const { + return statistics_interval_; +} + +uint32_t configuration_impl::get_statistics_min_freq() const { + return statistics_min_freq_; +} + +uint32_t configuration_impl::get_statistics_max_messages() const { + return statistics_max_messages_; +} + } // namespace config } // namespace vsomeip_v3 diff --git a/implementation/e2e_protection/include/crc/crc.hpp b/implementation/e2e_protection/include/crc/crc.hpp index 6120d86..b8d5688 100644 --- a/implementation/e2e_protection/include/crc/crc.hpp +++ b/implementation/e2e_protection/include/crc/crc.hpp @@ -14,9 +14,9 @@ namespace vsomeip_v3 { class e2e_crc { public: static uint8_t calculate_profile_01(buffer_view _buffer_view, - const uint8_t _start_value = 0x00U); + const uint8_t _start_value = 0x00U); static uint32_t calculate_profile_04(buffer_view _buffer_view, - const uint32_t _start_value = 0x00000000U); + const uint32_t _start_value = 0x00000000U); static uint32_t calculate_profile_custom(buffer_view _buffer_view); diff --git a/implementation/e2e_protection/include/e2e/profile/e2e_provider.hpp b/implementation/e2e_protection/include/e2e/profile/e2e_provider.hpp index aa45829..41d7487 100644 --- a/implementation/e2e_protection/include/e2e/profile/e2e_provider.hpp +++ b/implementation/e2e_protection/include/e2e/profile/e2e_provider.hpp @@ -24,9 +24,13 @@ public: virtual bool is_protected(e2exf::data_identifier_t id) const = 0; virtual bool is_checked(e2exf::data_identifier_t id) const = 0; - virtual void protect(e2exf::data_identifier_t id, e2e_buffer &_buffer) = 0; - virtual void check(e2exf::data_identifier_t id, const e2e_buffer &_buffer, - e2e::profile_interface::check_status_t &_generic_check_status) = 0; + virtual std::size_t get_protection_base(e2exf::data_identifier_t _id) const = 0; + + virtual void protect(e2exf::data_identifier_t id, + e2e_buffer &_buffer, instance_t _instance) = 0; + virtual void check(e2exf::data_identifier_t id, + const e2e_buffer &_buffer, instance_t _instance, + e2e::profile_interface::check_status_t &_generic_check_status) = 0; }; } // namespace e2e diff --git a/implementation/e2e_protection/include/e2e/profile/e2e_provider_impl.hpp b/implementation/e2e_protection/include/e2e/profile/e2e_provider_impl.hpp index b7d41a6..62db2e2 100644 --- a/implementation/e2e_protection/include/e2e/profile/e2e_provider_impl.hpp +++ b/implementation/e2e_protection/include/e2e/profile/e2e_provider_impl.hpp @@ -32,13 +32,18 @@ public: VSOMEIP_EXPORT bool is_protected(e2exf::data_identifier_t id) const override; VSOMEIP_EXPORT bool is_checked(e2exf::data_identifier_t id) const override; - VSOMEIP_EXPORT void protect(e2exf::data_identifier_t id, e2e_buffer &_buffer) override; - VSOMEIP_EXPORT void check(e2exf::data_identifier_t id, const e2e_buffer &_buffer, - profile_interface::check_status_t &_generic_check_status) override; + VSOMEIP_EXPORT std::size_t get_protection_base(e2exf::data_identifier_t _id) const override; + + VSOMEIP_EXPORT void protect(e2exf::data_identifier_t id, + e2e_buffer &_buffer, instance_t _instance) override; + VSOMEIP_EXPORT void check(e2exf::data_identifier_t id, + const e2e_buffer &_buffer, instance_t _instance, + profile_interface::check_status_t &_generic_check_status) override; private: - std::map<e2exf::data_identifier_t, std::shared_ptr<profile_interface::protector>> custom_protectors; - std::map<e2exf::data_identifier_t, std::shared_ptr<profile_interface::checker>> custom_checkers; + std::map<e2exf::data_identifier_t, std::shared_ptr<profile_interface::protector>> custom_protectors_; + std::map<e2exf::data_identifier_t, std::shared_ptr<profile_interface::checker>> custom_checkers_; + std::map<e2exf::data_identifier_t, std::size_t> custom_bases_; template<typename config_t> config_t make_e2e_profile_config(const std::shared_ptr<cfg::e2e>& config); @@ -50,13 +55,15 @@ private: std::shared_ptr<e2e::profile_interface::checker> checker; if ((config->variant == "checker") || (config->variant == "both")) { - custom_checkers[data_identifier] = std::make_shared<checker_t>(profile_config); + custom_checkers_[data_identifier] = std::make_shared<checker_t>(profile_config); } std::shared_ptr<e2e::profile_interface::protector> protector; if ((config->variant == "protector") || (config->variant == "both")) { - custom_protectors[data_identifier] = std::make_shared<protector_t>(profile_config); + custom_protectors_[data_identifier] = std::make_shared<protector_t>(profile_config); } + + custom_bases_[data_identifier] = profile_config.base_; } }; diff --git a/implementation/e2e_protection/include/e2e/profile/profile01/checker.hpp b/implementation/e2e_protection/include/e2e/profile/profile01/checker.hpp index 3010949..4184996 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile01/checker.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile01/checker.hpp @@ -15,15 +15,15 @@ namespace profile01 { class profile_01_checker final : public e2e::profile_interface::checker { - public: +public: profile_01_checker(void) = delete; // [SWS_E2E_00389] initialize state explicit profile_01_checker(const profile_config &_config) : config_(_config) {} - virtual void check(const e2e_buffer &_buffer, - e2e::profile_interface::check_status_t &_generic_check_status) override final; + void check(const e2e_buffer &_buffer, instance_t _instance, + e2e::profile_interface::check_status_t &_generic_check_status) override final; private: profile_config config_; diff --git a/implementation/e2e_protection/include/e2e/profile/profile01/profile_01.hpp b/implementation/e2e_protection/include/e2e/profile/profile01/profile_01.hpp index c6cd5cf..54b2d5c 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile01/profile_01.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile01/profile_01.hpp @@ -7,6 +7,9 @@ #define VSOMEIP_V3_E2E_PROFILE01_PROFILE01_HPP #include <cstdint> + +#include <vsomeip/defines.hpp> + #include "../../../buffer/buffer.hpp" namespace vsomeip_v3 { @@ -26,14 +29,6 @@ class profile_01 { enum class p01_data_id_mode : uint8_t {E2E_P01_DATAID_BOTH, E2E_P01_DATAID_ALT, E2E_P01_DATAID_LOW, E2E_P01_DATAID_NIBBLE}; struct profile_config { - // [SWS_E2E_00018] - uint16_t crc_offset_; - uint16_t data_id_; - p01_data_id_mode data_id_mode_; - uint16_t data_length_; - uint16_t counter_offset_; - uint16_t data_id_nibble_offset_; - profile_config() = delete; profile_config(uint16_t _crc_offset, uint16_t _data_id, @@ -43,10 +38,22 @@ struct profile_config { : crc_offset_(_crc_offset), data_id_(_data_id), data_id_mode_(_data_id_mode), data_length_(_data_length), counter_offset_(_counter_offset), - data_id_nibble_offset_(_data_id_nibble_offset) { + data_id_nibble_offset_(_data_id_nibble_offset), + base_(VSOMEIP_FULL_HEADER_SIZE) { } profile_config(const profile_config &_config) = default; profile_config &operator=(const profile_config &_config) = default; + + // [SWS_E2E_00018] + uint16_t crc_offset_; + uint16_t data_id_; + p01_data_id_mode data_id_mode_; + uint16_t data_length_; + uint16_t counter_offset_; + uint16_t data_id_nibble_offset_; + + // SOME/IP base + size_t base_; }; } // namespace profile01 diff --git a/implementation/e2e_protection/include/e2e/profile/profile01/protector.hpp b/implementation/e2e_protection/include/e2e/profile/profile01/protector.hpp index fe8b603..13bd524 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile01/protector.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile01/protector.hpp @@ -7,6 +7,7 @@ #define VSOMEIP_V3_E2E_PROFILE01_PROTECTOR_HPP #include <mutex> + #include "../profile01/profile_01.hpp" #include "../profile_interface/protector.hpp" @@ -15,14 +16,14 @@ namespace e2e { namespace profile01 { class protector final : public e2e::profile_interface::protector { - public: +public: protector(void) = delete; - explicit protector(const profile_config &_config) : config_(_config), counter_(0){}; + explicit protector(const profile_config &_config) : config_(_config), counter_(0) {}; - void protect(e2e_buffer &_buffer) override final; + void protect(e2e_buffer &_buffer, instance_t _instance) override final; - private: +private: void write_counter(e2e_buffer &_buffer); @@ -32,8 +33,7 @@ class protector final : public e2e::profile_interface::protector { void increment_counter(void); - - private: +private: profile_config config_; uint8_t counter_; std::mutex protect_mutex_; diff --git a/implementation/e2e_protection/include/e2e/profile/profile04/checker.hpp b/implementation/e2e_protection/include/e2e/profile/profile04/checker.hpp new file mode 100644 index 0000000..c16eb10 --- /dev/null +++ b/implementation/e2e_protection/include/e2e/profile/profile04/checker.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef VSOMEIP_V3_E2E_PROFILE04_CHECKER_HPP +#define VSOMEIP_V3_E2E_PROFILE04_CHECKER_HPP + +#include "../profile04/profile_04.hpp" +#include "../profile_interface/checker.hpp" + +namespace vsomeip_v3 { +namespace e2e { +namespace profile04 { + +class profile_04_checker final : public e2e::profile_interface::checker { + +public: + profile_04_checker(void) = delete; + + // [SWS_E2E_00389] initialize state + explicit profile_04_checker(const profile_config &_config) : + config_(_config), counter_(0xffff) {} + + void check(const e2e_buffer &_buffer, instance_t _instance, + e2e::profile_interface::check_status_t &_generic_check_status) override final; + +private: + bool verify_input(const e2e_buffer &_buffer) const; + bool verify_counter(uint16_t _received_counter) const; + + bool read_16(const e2e_buffer &_buffer, uint16_t &_data, size_t _index) const; + bool read_32(const e2e_buffer &_buffer, uint32_t &_data, size_t _index) const; + + std::mutex check_mutex_; + + profile_config config_; + uint16_t counter_; +}; + +} // namespace profile_04 +} // namespace e2e +} // namespace vsomeip_v3 + +#endif // VSOMEIP_V3_E2E_PROFILE04_CHECKER_HPP diff --git a/implementation/e2e_protection/include/e2e/profile/profile04/profile_04.hpp b/implementation/e2e_protection/include/e2e/profile/profile04/profile_04.hpp new file mode 100644 index 0000000..88be05c --- /dev/null +++ b/implementation/e2e_protection/include/e2e/profile/profile04/profile_04.hpp @@ -0,0 +1,63 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef VSOMEIP_V3_E2E_PROFILE04_PROFILE04_HPP +#define VSOMEIP_V3_E2E_PROFILE04_PROFILE04_HPP + +#include <cstdint> + +#include <vsomeip/defines.hpp> + +#include "../../../buffer/buffer.hpp" + +// The MSB of the dataID is the instance identifier. +// Therefore, the instance identifier must fit into a single byte. +#define VSOMEIP_E2E_PROFILE04_MAX_INSTANCE 0x00ff + +namespace vsomeip_v3 { +namespace e2e { +namespace profile04 { + +struct profile_config; + +class profile_04 { +public: + static uint32_t compute_crc(const profile_config &_config, const e2e_buffer &_buffer); +}; + +// [SWS_E2E_00200] +struct profile_config { + profile_config() = delete; + + profile_config(uint32_t _data_id, size_t _offset, + size_t _min_data_length, size_t _max_data_length, + uint16_t _max_delta_counter) + : data_id_(_data_id), offset_(_offset), + min_data_length_(_min_data_length), max_data_length_(_max_data_length), + max_delta_counter_(_max_delta_counter), + base_(VSOMEIP_SOMEIP_HEADER_SIZE) { + } + profile_config(const profile_config &_config) = default; + profile_config &operator=(const profile_config &_config) = default; + + // [SWS_E2E_00334] + uint32_t data_id_; + size_t offset_; // This must be configured in bit but as a multiple of 8. + // As we must use it as an index, we do the math once at + // configuration time and use the correct data type here. + // Thus, this value is always the byte where the CRC starts. + size_t min_data_length_; + size_t max_data_length_; + uint16_t max_delta_counter_; + + // SOME/IP base + size_t base_; +}; + +} // namespace profile_04 +} // namespace e2e +} // namespace vsomeip_v3 + +#endif // VSOMEIP_V3_E2E_PROFILE04_PROFILE04_HPP diff --git a/implementation/e2e_protection/include/e2e/profile/profile04/protector.hpp b/implementation/e2e_protection/include/e2e/profile/profile04/protector.hpp new file mode 100644 index 0000000..fd4a6e7 --- /dev/null +++ b/implementation/e2e_protection/include/e2e/profile/profile04/protector.hpp @@ -0,0 +1,43 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef VSOMEIP_V3_E2E_PROFILE04_PROTECTOR_HPP +#define VSOMEIP_V3_E2E_PROFILE04_PROTECTOR_HPP + +#include <mutex> +#include "../profile04/profile_04.hpp" +#include "../profile_interface/protector.hpp" + +namespace vsomeip_v3 { +namespace e2e { +namespace profile04 { + +class protector final : public e2e::profile_interface::protector { +public: + protector(void) = delete; + + explicit protector(const profile_config &_config) + : config_(_config), counter_(0) {}; + + void protect(e2e_buffer &_buffer, instance_t _instance) override final; + +private: + bool verify_inputs(e2e_buffer &_buffer); + void increment_counter(); + + void write_16(e2e_buffer &_buffer, uint16_t _data, size_t _index); + void write_32(e2e_buffer &_buffer, uint32_t _data, size_t _index); + +private: + profile_config config_; + uint16_t counter_; + std::mutex protect_mutex_; +}; + +} // namespace profile_04 +} // namespace e2e +} // namespace vsomeip_v3 + +#endif // VSOMEIP_V3_E2E_PROFILE04_PROTECTOR_HPP diff --git a/implementation/e2e_protection/include/e2e/profile/profile_custom/checker.hpp b/implementation/e2e_protection/include/e2e/profile/profile_custom/checker.hpp index 73f1bbb..e233205 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile_custom/checker.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile_custom/checker.hpp @@ -23,10 +23,10 @@ public: explicit profile_custom_checker(const e2e::profile_custom::profile_config &_config) : config_(_config) {} - virtual void check(const e2e_buffer &_buffer, - e2e::profile_interface::check_status_t &_generic_check_status); + void check(const e2e_buffer &_buffer, instance_t _instance, + e2e::profile_interface::check_status_t &_generic_check_status) override final; - private: +private: uint32_t read_crc(const e2e_buffer &_buffer) const; private: diff --git a/implementation/e2e_protection/include/e2e/profile/profile_custom/profile_custom.hpp b/implementation/e2e_protection/include/e2e/profile/profile_custom/profile_custom.hpp index 1cabefa..244989d 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile_custom/profile_custom.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile_custom/profile_custom.hpp @@ -7,6 +7,9 @@ #define VSOMEIP_V3_E2E_PROFILE_CUSTOM_PROFILE_CUSTOM_HPP #include <cstdint> + +#include <vsomeip/defines.hpp> + #include "../../../buffer/buffer.hpp" namespace vsomeip_v3 { @@ -23,15 +26,17 @@ class profile_custom { }; struct profile_config { - uint16_t crc_offset_; - profile_config() = delete; profile_config(uint16_t _crc_offset) - : crc_offset_(_crc_offset) { + : crc_offset_(_crc_offset), + base_(VSOMEIP_FULL_HEADER_SIZE) { } profile_config(const profile_config &_config) = default; profile_config &operator=(const profile_config &_config) = default; + + uint16_t crc_offset_; + size_t base_; }; } // namespace profile_custom diff --git a/implementation/e2e_protection/include/e2e/profile/profile_custom/protector.hpp b/implementation/e2e_protection/include/e2e/profile/profile_custom/protector.hpp index 31b976b..a2ec5d1 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile_custom/protector.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile_custom/protector.hpp @@ -15,18 +15,18 @@ namespace e2e { namespace profile_custom { class protector final : public e2e::profile_interface::protector { - public: +public: protector(void) = delete; explicit protector(const profile_config &_config) : config_(_config){}; - void protect(e2e_buffer &_buffer) override final; + void protect(e2e_buffer &_buffer, instance_t _instance) override final; - private: +private: void write_crc(e2e_buffer &_buffer, uint32_t _computed_crc); - private: +private: profile_config config_; std::mutex protect_mutex_; }; diff --git a/implementation/e2e_protection/include/e2e/profile/profile_interface/checker.hpp b/implementation/e2e_protection/include/e2e/profile/profile_interface/checker.hpp index f160ea1..12cb6b7 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile_interface/checker.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile_interface/checker.hpp @@ -6,18 +6,21 @@ #ifndef VSOMEIP_V3_E2E_PROFILE_INTERFACE_CHECKER_HPP #define VSOMEIP_V3_E2E_PROFILE_INTERFACE_CHECKER_HPP +#include <mutex> + +#include <vsomeip/primitive_types.hpp> + #include "../profile_interface/profile_interface.hpp" #include "../../../buffer/buffer.hpp" -#include <mutex> namespace vsomeip_v3 { namespace e2e { namespace profile_interface { class checker : public profile_interface { - public: - virtual void check(const e2e_buffer &_buffer, - check_status_t &_generic_check_status) = 0; +public: + virtual void check(const e2e_buffer &_buffer, instance_t _instance, + check_status_t &_generic_check_status) = 0; }; } // namespace profile_interface diff --git a/implementation/e2e_protection/include/e2e/profile/profile_interface/profile_interface.hpp b/implementation/e2e_protection/include/e2e/profile/profile_interface/profile_interface.hpp index dc72aac..5fa018c 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile_interface/profile_interface.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile_interface/profile_interface.hpp @@ -18,8 +18,7 @@ enum generic_check_status : check_status_t { E2E_OK, E2E_WRONG_CRC, E2E_ERROR}; class profile_interface { public: - virtual ~profile_interface() { - } + virtual ~profile_interface() {} }; } // namespace profile_interface diff --git a/implementation/e2e_protection/include/e2e/profile/profile_interface/protector.hpp b/implementation/e2e_protection/include/e2e/profile/profile_interface/protector.hpp index 5bae188..4b02971 100644 --- a/implementation/e2e_protection/include/e2e/profile/profile_interface/protector.hpp +++ b/implementation/e2e_protection/include/e2e/profile/profile_interface/protector.hpp @@ -6,6 +6,8 @@ #ifndef VSOMEIP_V3_E2E_PROFILE_INTERFACE_PROTECTOR_HPP #define VSOMEIP_V3_E2E_PROFILE_INTERFACE_PROTECTOR_HPP +#include <vsomeip/primitive_types.hpp> + #include "../../../buffer/buffer.hpp" #include "../profile_interface/profile_interface.hpp" @@ -14,8 +16,9 @@ namespace e2e { namespace profile_interface { class protector : public profile_interface { - public: - virtual void protect(e2e_buffer &_buffer) = 0; +public: + virtual void protect(e2e_buffer &_buffer, + instance_t _instance) = 0; }; } // namespace profile_interface diff --git a/implementation/e2e_protection/src/crc/crc.cpp b/implementation/e2e_protection/src/crc/crc.cpp index 335b48d..60ddb6c 100644 --- a/implementation/e2e_protection/src/crc/crc.cpp +++ b/implementation/e2e_protection/src/crc/crc.cpp @@ -72,46 +72,48 @@ const uint8_t e2e_crc::lookup_table_profile_01_[256] = { * - ReflectOut = true */ uint32_t e2e_crc::calculate_profile_04(buffer_view _buffer_view, const uint32_t _start_value) { - uint32_t crc = _start_value ^ 0xFFFFFFFFU; - for (uint8_t byte : _buffer_view) { - crc = lookup_table_profile_04_[static_cast<uint8_t>(byte ^ crc)] ^ (crc >> 8U); - } - crc = crc ^ 0xFFFFFFFFU; + uint32_t crc = (_start_value ^ 0xFFFFFFFFU); - return crc; + for (uint8_t byte : _buffer_view) + crc = lookup_table_profile_04_[static_cast<uint8_t>(byte ^ crc)] ^ (crc >> 8U); + + return (crc ^ 0xFFFFFFFFU); } const uint32_t e2e_crc::lookup_table_profile_04_[256] = { - 0x00000000U, 0x30850FF5U, 0x610A1FEAU, 0x518F101FU, 0xC2143FD4U, 0xF2913021U, 0xA31E203EU, 0x939B2FCBU, 0x159615F7U, - 0x25131A02U, 0x749C0A1DU, 0x441905E8U, 0xD7822A23U, 0xE70725D6U, 0xB68835C9U, 0x860D3A3CU, 0x2B2C2BEEU, 0x1BA9241BU, - 0x4A263404U, 0x7AA33BF1U, 0xE938143AU, 0xD9BD1BCFU, 0x88320BD0U, 0xB8B70425U, 0x3EBA3E19U, 0x0E3F31ECU, 0x5FB021F3U, - 0x6F352E06U, 0xFCAE01CDU, 0xCC2B0E38U, 0x9DA41E27U, 0xAD2111D2U, 0x565857DCU, 0x66DD5829U, 0x37524836U, 0x07D747C3U, - 0x944C6808U, 0xA4C967FDU, 0xF54677E2U, 0xC5C37817U, 0x43CE422BU, 0x734B4DDEU, 0x22C45DC1U, 0x12415234U, 0x81DA7DFFU, - 0xB15F720AU, 0xE0D06215U, 0xD0556DE0U, 0x7D747C32U, 0x4DF173C7U, 0x1C7E63D8U, 0x2CFB6C2DU, 0xBF6043E6U, 0x8FE54C13U, - 0xDE6A5C0CU, 0xEEEF53F9U, 0x68E269C5U, 0x58676630U, 0x09E8762FU, 0x396D79DAU, 0xAAF65611U, 0x9A7359E4U, 0xCBFC49FBU, - 0xFB79460EU, 0xACB0AFB8U, 0x9C35A04DU, 0xCDBAB052U, 0xFD3FBFA7U, 0x6EA4906CU, 0x5E219F99U, 0x0FAE8F86U, 0x3F2B8073U, - 0xB926BA4FU, 0x89A3B5BAU, 0xD82CA5A5U, 0xE8A9AA50U, 0x7B32859BU, 0x4BB78A6EU, 0x1A389A71U, 0x2ABD9584U, 0x879C8456U, - 0xB7198BA3U, 0xE6969BBCU, 0xD6139449U, 0x4588BB82U, 0x750DB477U, 0x2482A468U, 0x1407AB9DU, 0x920A91A1U, 0xA28F9E54U, - 0xF3008E4BU, 0xC38581BEU, 0x501EAE75U, 0x609BA180U, 0x3114B19FU, 0x0191BE6AU, 0xFAE8F864U, 0xCA6DF791U, 0x9BE2E78EU, - 0xAB67E87BU, 0x38FCC7B0U, 0x0879C845U, 0x59F6D85AU, 0x6973D7AFU, 0xEF7EED93U, 0xDFFBE266U, 0x8E74F279U, 0xBEF1FD8CU, - 0x2D6AD247U, 0x1DEFDDB2U, 0x4C60CDADU, 0x7CE5C258U, 0xD1C4D38AU, 0xE141DC7FU, 0xB0CECC60U, 0x804BC395U, 0x13D0EC5EU, - 0x2355E3ABU, 0x72DAF3B4U, 0x425FFC41U, 0xC452C67DU, 0xF4D7C988U, 0xA558D997U, 0x95DDD662U, 0x0646F9A9U, 0x36C3F65CU, - 0x674CE643U, 0x57C9E9B6U, 0xC8DF352FU, 0xF85A3ADAU, 0xA9D52AC5U, 0x99502530U, 0x0ACB0AFBU, 0x3A4E050EU, 0x6BC11511U, - 0x5B441AE4U, 0xDD4920D8U, 0xEDCC2F2DU, 0xBC433F32U, 0x8CC630C7U, 0x1F5D1F0CU, 0x2FD810F9U, 0x7E5700E6U, 0x4ED20F13U, - 0xE3F31EC1U, 0xD3761134U, 0x82F9012BU, 0xB27C0EDEU, 0x21E72115U, 0x11622EE0U, 0x40ED3EFFU, 0x7068310AU, 0xF6650B36U, - 0xC6E004C3U, 0x976F14DCU, 0xA7EA1B29U, 0x347134E2U, 0x04F43B17U, 0x557B2B08U, 0x65FE24FDU, 0x9E8762F3U, 0xAE026D06U, - 0xFF8D7D19U, 0xCF0872ECU, 0x5C935D27U, 0x6C1652D2U, 0x3D9942CDU, 0x0D1C4D38U, 0x8B117704U, 0xBB9478F1U, 0xEA1B68EEU, - 0xDA9E671BU, 0x490548D0U, 0x79804725U, 0x280F573AU, 0x188A58CFU, 0xB5AB491DU, 0x852E46E8U, 0xD4A156F7U, 0xE4245902U, - 0x77BF76C9U, 0x473A793CU, 0x16B56923U, 0x263066D6U, 0xA03D5CEAU, 0x90B8531FU, 0xC1374300U, 0xF1B24CF5U, 0x6229633EU, - 0x52AC6CCBU, 0x03237CD4U, 0x33A67321U, 0x646F9A97U, 0x54EA9562U, 0x0565857DU, 0x35E08A88U, 0xA67BA543U, 0x96FEAAB6U, - 0xC771BAA9U, 0xF7F4B55CU, 0x71F98F60U, 0x417C8095U, 0x10F3908AU, 0x20769F7FU, 0xB3EDB0B4U, 0x8368BF41U, 0xD2E7AF5EU, - 0xE262A0ABU, 0x4F43B179U, 0x7FC6BE8CU, 0x2E49AE93U, 0x1ECCA166U, 0x8D578EADU, 0xBDD28158U, 0xEC5D9147U, 0xDCD89EB2U, - 0x5AD5A48EU, 0x6A50AB7BU, 0x3BDFBB64U, 0x0B5AB491U, 0x98C19B5AU, 0xA84494AFU, 0xF9CB84B0U, 0xC94E8B45U, 0x3237CD4BU, - 0x02B2C2BEU, 0x533DD2A1U, 0x63B8DD54U, 0xF023F29FU, 0xC0A6FD6AU, 0x9129ED75U, 0xA1ACE280U, 0x27A1D8BCU, 0x1724D749U, - 0x46ABC756U, 0x762EC8A3U, 0xE5B5E768U, 0xD530E89DU, 0x84BFF882U, 0xB43AF777U, 0x191BE6A5U, 0x299EE950U, 0x7811F94FU, - 0x4894F6BAU, 0xDB0FD971U, 0xEB8AD684U, 0xBA05C69BU, 0x8A80C96EU, 0x0C8DF352U, 0x3C08FCA7U, 0x6D87ECB8U, 0x5D02E34DU, - 0xCE99CC86U, 0xFE1CC373U, 0xAF93D36CU, 0x9F16DC99U + 0x00000000U, 0x30850FF5U, 0x610A1FEAU, 0x518F101FU, 0xC2143FD4U, 0xF2913021U, 0xA31E203EU, 0x939B2FCBU, + 0x159615F7U, 0x25131A02U, 0x749C0A1DU, 0x441905E8U, 0xD7822A23U, 0xE70725D6U, 0xB68835C9U, 0x860D3A3CU, + 0x2B2C2BEEU, 0x1BA9241BU, 0x4A263404U, 0x7AA33BF1U, 0xE938143AU, 0xD9BD1BCFU, 0x88320BD0U, 0xB8B70425U, + 0x3EBA3E19U, 0x0E3F31ECU, 0x5FB021F3U, 0x6F352E06U, 0xFCAE01CDU, 0xCC2B0E38U, 0x9DA41E27U, 0xAD2111D2U, + 0x565857DCU, 0x66DD5829U, 0x37524836U, 0x07D747C3U, 0x944C6808U, 0xA4C967FDU, 0xF54677E2U, 0xC5C37817U, + 0x43CE422BU, 0x734B4DDEU, 0x22C45DC1U, 0x12415234U, 0x81DA7DFFU, 0xB15F720AU, 0xE0D06215U, 0xD0556DE0U, + 0x7D747C32U, 0x4DF173C7U, 0x1C7E63D8U, 0x2CFB6C2DU, 0xBF6043E6U, 0x8FE54C13U, 0xDE6A5C0CU, 0xEEEF53F9U, + 0x68E269C5U, 0x58676630U, 0x09E8762FU, 0x396D79DAU, 0xAAF65611U, 0x9A7359E4U, 0xCBFC49FBU, 0xFB79460EU, + 0xACB0AFB8U, 0x9C35A04DU, 0xCDBAB052U, 0xFD3FBFA7U, 0x6EA4906CU, 0x5E219F99U, 0x0FAE8F86U, 0x3F2B8073U, + 0xB926BA4FU, 0x89A3B5BAU, 0xD82CA5A5U, 0xE8A9AA50U, 0x7B32859BU, 0x4BB78A6EU, 0x1A389A71U, 0x2ABD9584U, + 0x879C8456U, 0xB7198BA3U, 0xE6969BBCU, 0xD6139449U, 0x4588BB82U, 0x750DB477U, 0x2482A468U, 0x1407AB9DU, + 0x920A91A1U, 0xA28F9E54U, 0xF3008E4BU, 0xC38581BEU, 0x501EAE75U, 0x609BA180U, 0x3114B19FU, 0x0191BE6AU, + 0xFAE8F864U, 0xCA6DF791U, 0x9BE2E78EU, 0xAB67E87BU, 0x38FCC7B0U, 0x0879C845U, 0x59F6D85AU, 0x6973D7AFU, + 0xEF7EED93U, 0xDFFBE266U, 0x8E74F279U, 0xBEF1FD8CU, 0x2D6AD247U, 0x1DEFDDB2U, 0x4C60CDADU, 0x7CE5C258U, + 0xD1C4D38AU, 0xE141DC7FU, 0xB0CECC60U, 0x804BC395U, 0x13D0EC5EU, 0x2355E3ABU, 0x72DAF3B4U, 0x425FFC41U, + 0xC452C67DU, 0xF4D7C988U, 0xA558D997U, 0x95DDD662U, 0x0646F9A9U, 0x36C3F65CU, 0x674CE643U, 0x57C9E9B6U, + 0xC8DF352FU, 0xF85A3ADAU, 0xA9D52AC5U, 0x99502530U, 0x0ACB0AFBU, 0x3A4E050EU, 0x6BC11511U, 0x5B441AE4U, + 0xDD4920D8U, 0xEDCC2F2DU, 0xBC433F32U, 0x8CC630C7U, 0x1F5D1F0CU, 0x2FD810F9U, 0x7E5700E6U, 0x4ED20F13U, + 0xE3F31EC1U, 0xD3761134U, 0x82F9012BU, 0xB27C0EDEU, 0x21E72115U, 0x11622EE0U, 0x40ED3EFFU, 0x7068310AU, + 0xF6650B36U, 0xC6E004C3U, 0x976F14DCU, 0xA7EA1B29U, 0x347134E2U, 0x04F43B17U, 0x557B2B08U, 0x65FE24FDU, + 0x9E8762F3U, 0xAE026D06U, 0xFF8D7D19U, 0xCF0872ECU, 0x5C935D27U, 0x6C1652D2U, 0x3D9942CDU, 0x0D1C4D38U, + 0x8B117704U, 0xBB9478F1U, 0xEA1B68EEU, 0xDA9E671BU, 0x490548D0U, 0x79804725U, 0x280F573AU, 0x188A58CFU, + 0xB5AB491DU, 0x852E46E8U, 0xD4A156F7U, 0xE4245902U, 0x77BF76C9U, 0x473A793CU, 0x16B56923U, 0x263066D6U, + 0xA03D5CEAU, 0x90B8531FU, 0xC1374300U, 0xF1B24CF5U, 0x6229633EU, 0x52AC6CCBU, 0x03237CD4U, 0x33A67321U, + 0x646F9A97U, 0x54EA9562U, 0x0565857DU, 0x35E08A88U, 0xA67BA543U, 0x96FEAAB6U, 0xC771BAA9U, 0xF7F4B55CU, + 0x71F98F60U, 0x417C8095U, 0x10F3908AU, 0x20769F7FU, 0xB3EDB0B4U, 0x8368BF41U, 0xD2E7AF5EU, 0xE262A0ABU, + 0x4F43B179U, 0x7FC6BE8CU, 0x2E49AE93U, 0x1ECCA166U, 0x8D578EADU, 0xBDD28158U, 0xEC5D9147U, 0xDCD89EB2U, + 0x5AD5A48EU, 0x6A50AB7BU, 0x3BDFBB64U, 0x0B5AB491U, 0x98C19B5AU, 0xA84494AFU, 0xF9CB84B0U, 0xC94E8B45U, + 0x3237CD4BU, 0x02B2C2BEU, 0x533DD2A1U, 0x63B8DD54U, 0xF023F29FU, 0xC0A6FD6AU, 0x9129ED75U, 0xA1ACE280U, + 0x27A1D8BCU, 0x1724D749U, 0x46ABC756U, 0x762EC8A3U, 0xE5B5E768U, 0xD530E89DU, 0x84BFF882U, 0xB43AF777U, + 0x191BE6A5U, 0x299EE950U, 0x7811F94FU, 0x4894F6BAU, 0xDB0FD971U, 0xEB8AD684U, 0xBA05C69BU, 0x8A80C96EU, + 0x0C8DF352U, 0x3C08FCA7U, 0x6D87ECB8U, 0x5D02E34DU, 0xCE99CC86U, 0xFE1CC373U, 0xAF93D36CU, 0x9F16DC99U }; /** diff --git a/implementation/e2e_protection/src/e2e/profile/e2e_provider_impl.cpp b/implementation/e2e_protection/src/e2e/profile/e2e_provider_impl.cpp index 7a82446..111e126 100644 --- a/implementation/e2e_protection/src/e2e/profile/e2e_provider_impl.cpp +++ b/implementation/e2e_protection/src/e2e/profile/e2e_provider_impl.cpp @@ -3,33 +3,49 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <iomanip> +#include <sstream> + +#include <vsomeip/internal/logger.hpp> + #include "../../../../e2e_protection/include/e2e/profile/e2e_provider_impl.hpp" #include "../../../../e2e_protection/include/e2e/profile/profile01/checker.hpp" #include "../../../../e2e_protection/include/e2e/profile/profile01/profile_01.hpp" #include "../../../../e2e_protection/include/e2e/profile/profile01/protector.hpp" +#include "../../../../e2e_protection/include/e2e/profile/profile04/checker.hpp" +#include "../../../../e2e_protection/include/e2e/profile/profile04/profile_04.hpp" +#include "../../../../e2e_protection/include/e2e/profile/profile04/protector.hpp" + #include "../../../../e2e_protection/include/e2e/profile/profile_custom/checker.hpp" #include "../../../../e2e_protection/include/e2e/profile/profile_custom/profile_custom.hpp" #include "../../../../e2e_protection/include/e2e/profile/profile_custom/protector.hpp" -#include <sstream> - namespace { template<typename value_t> -value_t read_value_from_config(const std::shared_ptr<vsomeip_v3::cfg::e2e>& config, - const std::string& name, - value_t default_value = value_t()) { - if(config && config->custom_parameters.count(name)) { - value_t value; +value_t read_value_from_config(const std::shared_ptr<vsomeip_v3::cfg::e2e> &_config, + const std::string &_name, + value_t _default_value = value_t()) { + + if (_config && _config->custom_parameters.count(_name)) { + std::stringstream its_converter; - its_converter << config->custom_parameters[name]; - its_converter >> value; - return value; + std::string its_value(_config->custom_parameters[_name]); + + if (its_value.size() > 1 && its_value[0] == '0' && its_value[1] == 'x') { + its_converter << std::hex << its_value; + } else { + its_converter << std::dec << its_value; + } + + value_t its_converted_value; + its_converter >> its_converted_value; + return its_converted_value; } - return default_value; + return _default_value; } } // namespace @@ -51,67 +67,109 @@ e2e_provider_impl::~e2e_provider_impl() bool e2e_provider_impl::add_configuration(std::shared_ptr<cfg::e2e> config) { - if(config->profile == "CRC8") { + if (config->profile == "CRC8" || config->profile == "P01") { process_e2e_profile<profile01::profile_config, profile01::profile_01_checker, profile01::protector>(config); return true; } - if(config->profile == "CRC32") { + if (config->profile == "CRC32" || config->profile == "CSTM") { process_e2e_profile<profile_custom::profile_config, profile_custom::profile_custom_checker, profile_custom::protector>(config); return true; } + if (config->profile == "P04") { + process_e2e_profile<profile04::profile_config, profile04::profile_04_checker, profile04::protector>(config); + return true; + } + return false; } bool e2e_provider_impl::is_protected(e2exf::data_identifier_t id) const { - return custom_protectors.count(id) > 0; + return custom_protectors_.count(id) > 0; } bool e2e_provider_impl::is_checked(e2exf::data_identifier_t id) const { - return custom_checkers.count(id) > 0; + return custom_checkers_.count(id) > 0; +} + +std::size_t e2e_provider_impl::get_protection_base(e2exf::data_identifier_t id) const +{ + const auto found_base = custom_bases_.find(id); + if (found_base != custom_bases_.end()) + return (found_base->second); + + return (0); } -void e2e_provider_impl::protect(e2exf::data_identifier_t id, e2e_buffer &_buffer) +void e2e_provider_impl::protect(e2exf::data_identifier_t id, e2e_buffer &_buffer, + instance_t _instance) { - auto protector = custom_protectors.find(id); - if(protector != custom_protectors.end()) { - protector->second->protect(_buffer); + auto protector = custom_protectors_.find(id); + if(protector != custom_protectors_.end()) { + protector->second->protect(_buffer, _instance); } } -void e2e_provider_impl::check(e2exf::data_identifier_t id, const e2e_buffer &_buffer, - profile_interface::check_status_t &_generic_check_status) +void e2e_provider_impl::check(e2exf::data_identifier_t id, + const e2e_buffer &_buffer, instance_t _instance, + profile_interface::check_status_t &_generic_check_status) { - auto checker = custom_checkers.find(id); - if(checker != custom_checkers.end()) { - checker->second->check(_buffer, _generic_check_status); + auto checker = custom_checkers_.find(id); + if(checker != custom_checkers_.end()) { + checker->second->check(_buffer, _instance, _generic_check_status); } } template<> vsomeip_v3::e2e::profile01::profile_config -e2e_provider_impl::make_e2e_profile_config(const std::shared_ptr<cfg::e2e>& config) { - uint16_t crc_offset = read_value_from_config<uint16_t>(config, "crc_offset"); - uint16_t data_length = read_value_from_config<uint16_t>(config, "data_length"); +e2e_provider_impl::make_e2e_profile_config(const std::shared_ptr<cfg::e2e> &_config) { + uint16_t data_id = read_value_from_config<uint16_t>(_config, "data_id"); + uint16_t crc_offset = read_value_from_config<uint16_t>(_config, "crc_offset"); + uint16_t data_length = read_value_from_config<uint16_t>(_config, "data_length"); // counter field behind CRC8 - uint16_t counter_offset = read_value_from_config<uint16_t>(config, "counter_offset", 8); + uint16_t counter_offset = read_value_from_config<uint16_t>(_config, "counter_offset", 8); // data id nibble behind 4 bit counter value - uint16_t data_id_nibble_offset = read_value_from_config<uint16_t>(config, "data_id_nibble_offset", 12); + uint16_t data_id_nibble_offset = read_value_from_config<uint16_t>(_config, "data_id_nibble_offset", 12); e2e::profile01::p01_data_id_mode data_id_mode = static_cast<e2e::profile01::p01_data_id_mode>( - read_value_from_config<uint16_t>(config, "data_id_mode")); + read_value_from_config<uint16_t>(_config, "data_id_mode")); - return e2e::profile01::profile_config(crc_offset, config->data_id, data_id_mode, + return e2e::profile01::profile_config(crc_offset, data_id, data_id_mode, data_length, counter_offset, data_id_nibble_offset); } template<> +vsomeip_v3::e2e::profile04::profile_config +e2e_provider_impl::make_e2e_profile_config(const std::shared_ptr<cfg::e2e> &_config) { + + uint32_t data_id = read_value_from_config<uint32_t>(_config, "data_id"); + + size_t offset = read_value_from_config<size_t>(_config, "crc_offset"); + if (offset % 8) + VSOMEIP_ERROR << "Offset in E2E P04 configuration must be multiple of 8" + " (" << offset << ")"; + offset /= 8; + + size_t min_data_length = read_value_from_config<size_t>(_config, + "min_data_length", 0); + + size_t max_data_length = read_value_from_config<size_t>(_config, + "max_data_length", size_t(0xffff)); + + uint16_t max_delta_counter = read_value_from_config<uint16_t>(_config, + "max_delta_counter", uint16_t(0xffff)); + + return e2e::profile04::profile_config(data_id, offset, + min_data_length, max_data_length, max_delta_counter); +} + +template<> e2e::profile_custom::profile_config e2e_provider_impl::make_e2e_profile_config(const std::shared_ptr<cfg::e2e>& config) { uint16_t crc_offset = read_value_from_config<uint16_t>(config, "crc_offset"); diff --git a/implementation/e2e_protection/src/e2e/profile/profile01/checker.cpp b/implementation/e2e_protection/src/e2e/profile/profile01/checker.cpp index bea8dc1..a5d75d8 100644 --- a/implementation/e2e_protection/src/e2e/profile/profile01/checker.cpp +++ b/implementation/e2e_protection/src/e2e/profile/profile01/checker.cpp @@ -16,8 +16,11 @@ namespace e2e { namespace profile01 { // [SWS_E2E_00196] -void profile_01_checker::check(const e2e_buffer &_buffer, - e2e::profile_interface::check_status_t &_generic_check_status) { +void profile_01_checker::check(const e2e_buffer &_buffer, instance_t _instance, + e2e::profile_interface::check_status_t &_generic_check_status) { + + (void)_instance; + std::lock_guard<std::mutex> lock(check_mutex_); _generic_check_status = e2e::profile_interface::generic_check_status::E2E_ERROR; @@ -34,7 +37,6 @@ void profile_01_checker::check(const e2e_buffer &_buffer, << (uint32_t) calculated_crc << " received CRC: " << (uint32_t) received_crc; } } - return; } } // namespace profile01 diff --git a/implementation/e2e_protection/src/e2e/profile/profile01/protector.cpp b/implementation/e2e_protection/src/e2e/profile/profile01/protector.cpp index 551a801..e5cb4ba 100644 --- a/implementation/e2e_protection/src/e2e/profile/profile01/protector.cpp +++ b/implementation/e2e_protection/src/e2e/profile/profile01/protector.cpp @@ -16,7 +16,10 @@ namespace e2e { namespace profile01 { /** @req [SWS_E2E_00195] */ -void protector::protect(e2e_buffer &_buffer) { +void protector::protect(e2e_buffer &_buffer, instance_t _instance) { + + (void)_instance; + std::lock_guard<std::mutex> lock(protect_mutex_); if (profile_01::is_buffer_length_valid(config_, _buffer)) { diff --git a/implementation/e2e_protection/src/e2e/profile/profile04/checker.cpp b/implementation/e2e_protection/src/e2e/profile/profile04/checker.cpp new file mode 100644 index 0000000..c9761ca --- /dev/null +++ b/implementation/e2e_protection/src/e2e/profile/profile04/checker.cpp @@ -0,0 +1,114 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <iomanip> + +#include <vsomeip/internal/logger.hpp> +#include "../../utility/include/byteorder.hpp" + +#include "../../../../../e2e_protection/include/e2e/profile/profile04/checker.hpp" + + +namespace vsomeip_v3 { +namespace e2e { +namespace profile04 { + +// [SWS_E2E_00355] +void profile_04_checker::check(const e2e_buffer &_buffer, instance_t _instance, + e2e::profile_interface::check_status_t &_generic_check_status) { + + std::lock_guard<std::mutex> lock(check_mutex_); + _generic_check_status = e2e::profile_interface::generic_check_status::E2E_ERROR; + + if (_instance > VSOMEIP_E2E_PROFILE04_MAX_INSTANCE) { + VSOMEIP_ERROR << "E2E Profile 4 can only be used for instances [1-255]"; + return; + } + + /** @req [SWS_E2E_356] */ + if (verify_input(_buffer)) { + /** @req [SWS_E2E_357] */ + uint16_t its_received_length; + if (read_16(_buffer, its_received_length, 0)) { + /** @req [SWS_E2E_358] */ + uint16_t its_received_counter; + if (read_16(_buffer, its_received_counter, 2)) { + /** @req [SWS_E2E_359] */ + uint32_t its_received_data_id; + if (read_32(_buffer, its_received_data_id, 4)) { + /** @req [SWS_E2E_360] */ + uint32_t its_received_crc; + if (read_32(_buffer, its_received_crc, 8)) { + uint32_t its_crc = profile_04::compute_crc(config_, _buffer); + /** @req [SWS_E2E_361] */ + if (its_received_crc != its_crc) { + _generic_check_status = e2e::profile_interface::generic_check_status::E2E_WRONG_CRC; + VSOMEIP_ERROR << std::hex << "E2E P04 protection: CRC32 does not match: calculated CRC: " + << its_crc << " received CRC: " << its_received_crc; + } else { + uint32_t its_data_id(uint32_t(_instance) << 24 | config_.data_id_); + if (its_received_data_id == its_data_id + && static_cast<size_t>(its_received_length) == _buffer.size() + && verify_counter(its_received_counter)) { + _generic_check_status = e2e::profile_interface::generic_check_status::E2E_OK; + } + counter_ = its_received_counter; + } + } + } + } + } + } +} + +bool +profile_04_checker::verify_input(const e2e_buffer &_buffer) const { + + auto its_length = _buffer.size(); + return (its_length >= config_.min_data_length_ + && its_length <= config_.max_data_length_); +} + +bool +profile_04_checker::verify_counter(uint16_t _received_counter) const { + + static bool has_counter(false); + uint16_t its_delta(0); + + if (has_counter) { + if (counter_ < _received_counter) + its_delta = uint16_t(_received_counter - counter_); + else + its_delta = uint16_t(uint16_t(0xffff) - counter_ + _received_counter); + } else { + has_counter = true; + } + + return (its_delta <= config_.max_delta_counter_); +} + +bool +profile_04_checker::read_16(const e2e_buffer &_buffer, + uint16_t &_data, size_t _index) const { + + _data = VSOMEIP_BYTES_TO_WORD(_buffer[config_.offset_ + _index], + _buffer[config_.offset_ + _index + 1]); + return (true); +} + +bool +profile_04_checker::read_32(const e2e_buffer &_buffer, + uint32_t &_data, size_t _index) const { + + _data = VSOMEIP_BYTES_TO_LONG(_buffer[config_.offset_ + _index], + _buffer[config_.offset_ + _index + 1], + _buffer[config_.offset_ + _index + 2], + _buffer[config_.offset_ + _index + 3]); + return (true); +} + +} // namespace profile01 +} // namespace e2e +} // namespace vsomeip_v3 diff --git a/implementation/e2e_protection/src/e2e/profile/profile04/profile_04.cpp b/implementation/e2e_protection/src/e2e/profile/profile04/profile_04.cpp new file mode 100644 index 0000000..05ab5e9 --- /dev/null +++ b/implementation/e2e_protection/src/e2e/profile/profile04/profile_04.cpp @@ -0,0 +1,33 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <iomanip> +#include <iostream> +#include <sstream> +#include <string> + +#include "../../../../../e2e_protection/include/e2e/profile/profile04/profile_04.hpp" +#include "../../../../../e2e_protection/include/crc/crc.hpp" + +namespace vsomeip_v3 { +namespace e2e { +namespace profile04 { + +uint32_t profile_04::compute_crc(const profile_config &_config, const e2e_buffer &_buffer) { + + buffer_view its_before(_buffer, _config.offset_ + 8); + uint32_t computed_crc = e2e_crc::calculate_profile_04(its_before); + + if (_config.offset_ + 12 < _buffer.size()) { + buffer_view its_after(_buffer, _config.offset_ + 12, _buffer.size()); + computed_crc = e2e_crc::calculate_profile_04(its_after, computed_crc); + } + + return (computed_crc); +} + +} // namespace profile04 +} // namespace e2e +} // namespace vsomeip_v3 diff --git a/implementation/e2e_protection/src/e2e/profile/profile04/protector.cpp b/implementation/e2e_protection/src/e2e/profile/profile04/protector.cpp new file mode 100644 index 0000000..a9287f2 --- /dev/null +++ b/implementation/e2e_protection/src/e2e/profile/profile04/protector.cpp @@ -0,0 +1,81 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <iomanip> + +#include "../../../../../e2e_protection/include/e2e/profile/profile04/protector.hpp" + +#include <vsomeip/internal/logger.hpp> +#include "../../utility/include/byteorder.hpp" + +namespace vsomeip_v3 { +namespace e2e { +namespace profile04 { + +/** @req [SWS_E2E_00195] */ +void +protector::protect(e2e_buffer &_buffer, instance_t _instance) { + std::lock_guard<std::mutex> lock(protect_mutex_); + + if (_instance > VSOMEIP_E2E_PROFILE04_MAX_INSTANCE) { + VSOMEIP_ERROR << "E2E Profile 4 can only be used for instances [1-255]"; + return; + } + + /** @req: [SWS_E2E_00363] */ + if (verify_inputs(_buffer)) { + + /** @req [SWS_E2E_00364] */ + write_16(_buffer, static_cast<uint16_t>(_buffer.size()), 0); + + /** @req [SWS_E2E_00365] */ + write_16(_buffer, counter_, 2); + + /** @req [SWS_E2E_00366] */ + uint32_t its_data_id(uint32_t(_instance) << 24 | config_.data_id_); + write_32(_buffer, its_data_id, 4); + + /** @req [SWS_E2E_00367] */ + uint32_t its_crc = profile_04::compute_crc(config_, _buffer); + + /** @req [SWS_E2E_0368] */ + write_32(_buffer, its_crc, 8); + + /** @req [SWS_E2E_00369] */ + increment_counter(); + } +} + +bool +protector::verify_inputs(e2e_buffer &_buffer) { + + return (_buffer.size() >= config_.min_data_length_ + && _buffer.size() <= config_.max_data_length_); +} + +void +protector::write_16(e2e_buffer &_buffer, uint16_t _data, size_t _index) { + + _buffer[config_.offset_ + _index] = VSOMEIP_WORD_BYTE1(_data); + _buffer[config_.offset_ + _index + 1] = VSOMEIP_WORD_BYTE0(_data); +} + +void +protector::write_32(e2e_buffer &_buffer, uint32_t _data, size_t _index) { + + _buffer[config_.offset_ + _index] = VSOMEIP_LONG_BYTE3(_data); + _buffer[config_.offset_ + _index + 1] = VSOMEIP_LONG_BYTE2(_data); + _buffer[config_.offset_ + _index + 2] = VSOMEIP_LONG_BYTE1(_data); + _buffer[config_.offset_ + _index + 3] = VSOMEIP_LONG_BYTE0(_data); +} + +void +protector::increment_counter() { + counter_++; +} + +} // namespace profile04 +} // namespace e2e +} // namespace vsomeip_v3 diff --git a/implementation/e2e_protection/src/e2e/profile/profile_custom/checker.cpp b/implementation/e2e_protection/src/e2e/profile/profile_custom/checker.cpp index 42e1ca8..9916a2f 100644 --- a/implementation/e2e_protection/src/e2e/profile/profile_custom/checker.cpp +++ b/implementation/e2e_protection/src/e2e/profile/profile_custom/checker.cpp @@ -16,11 +16,15 @@ namespace e2e { namespace profile_custom { void profile_custom_checker::check(const e2e_buffer &_buffer, - e2e::profile_interface::check_status_t &_generic_check_status) { + instance_t _instance, + e2e::profile_interface::check_status_t &_generic_check_status) { + + (void)_instance; + std::lock_guard<std::mutex> lock(check_mutex_); _generic_check_status = e2e::profile_interface::generic_check_status::E2E_ERROR; - if (profile_custom::is_buffer_length_valid(config_, _buffer)) { + if (profile_custom::is_buffer_length_valid(config_, _buffer)) { uint32_t received_crc(0); uint32_t calculated_crc(0); @@ -34,7 +38,6 @@ void profile_custom_checker::check(const e2e_buffer &_buffer, << (uint32_t) calculated_crc << " received CRC: " << (uint32_t) received_crc; } } - return; } uint32_t profile_custom_checker::read_crc(const e2e_buffer &_buffer) const { diff --git a/implementation/e2e_protection/src/e2e/profile/profile_custom/protector.cpp b/implementation/e2e_protection/src/e2e/profile/profile_custom/protector.cpp index 276c02b..c71f91f 100644 --- a/implementation/e2e_protection/src/e2e/profile/profile_custom/protector.cpp +++ b/implementation/e2e_protection/src/e2e/profile/profile_custom/protector.cpp @@ -16,7 +16,10 @@ namespace vsomeip_v3 { namespace e2e { namespace profile_custom { -void protector::protect(e2e_buffer &_buffer) { +void protector::protect(e2e_buffer &_buffer, instance_t _instance) { + + (void)_instance; + std::lock_guard<std::mutex> lock(protect_mutex_); if (profile_custom::is_buffer_length_valid(config_, _buffer)) { diff --git a/implementation/endpoints/include/endpoint_manager_impl.hpp b/implementation/endpoints/include/endpoint_manager_impl.hpp index cdf41b8..a7d28c6 100644 --- a/implementation/endpoints/include/endpoint_manager_impl.hpp +++ b/implementation/endpoints/include/endpoint_manager_impl.hpp @@ -44,12 +44,13 @@ public: std::shared_ptr<endpoint> create_server_endpoint(uint16_t _port, bool _reliable, bool _start); + std::shared_ptr<endpoint> find_server_endpoint(uint16_t _port, bool _reliable) const; std::shared_ptr<endpoint> find_or_create_server_endpoint( uint16_t _port, bool _reliable, bool _start, service_t _service, - instance_t _instance); + instance_t _instance, bool &_is_found, bool _is_multicast = false); bool remove_server_endpoint(uint16_t _port, bool _reliable); @@ -57,6 +58,7 @@ public: bool _reliable); void find_or_create_multicast_endpoint( service_t _service, instance_t _instance, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_address, uint16_t _port); void clear_multicast_endpoints(service_t _service, instance_t _instance); @@ -70,7 +72,12 @@ public: instance_t find_instance(service_t _service, endpoint* const _endpoint) const; + instance_t find_instance_multicast(service_t _service, + const boost::asio::ip::address &_sender) const; + bool remove_instance(service_t _service, endpoint* const _endpoint); + bool remove_instance_multicast(service_t _service, instance_t _instance); + // endpoint_host interface void on_connect(std::shared_ptr<endpoint> _endpoint); @@ -111,6 +118,7 @@ private: client_endpoints_by_ip_t client_endpoints_by_ip_; std::map<service_t, std::map<endpoint *, instance_t> > service_instances_; + std::map<service_t, std::map<boost::asio::ip::address, instance_t> > service_instances_multicast_; std::map<bool, std::set<uint16_t>> used_client_ports_; std::mutex used_client_ports_mutex_; diff --git a/implementation/endpoints/include/server_endpoint_impl.hpp b/implementation/endpoints/include/server_endpoint_impl.hpp index 4e03115..193043c 100644 --- a/implementation/endpoints/include/server_endpoint_impl.hpp +++ b/implementation/endpoints/include/server_endpoint_impl.hpp @@ -107,6 +107,10 @@ protected: mutable std::mutex mutex_; + std::mutex sent_mutex_; + bool is_sending_; + boost::asio::steady_timer sent_timer_; + private: virtual std::string get_remote_information( const queue_iterator_type _queue_iterator) const = 0; diff --git a/implementation/endpoints/include/tcp_server_endpoint_impl.hpp b/implementation/endpoints/include/tcp_server_endpoint_impl.hpp index c6481b8..af9a724 100644 --- a/implementation/endpoints/include/tcp_server_endpoint_impl.hpp +++ b/implementation/endpoints/include/tcp_server_endpoint_impl.hpp @@ -106,6 +106,7 @@ private: service_t _service, method_t _method, client_t _client, session_t _session, const std::chrono::steady_clock::time_point _start); void stop_and_remove_connection(); + void wait_until_sent(const boost::system::error_code &_error); std::mutex socket_mutex_; tcp_server_endpoint_impl::socket_type socket_; diff --git a/implementation/endpoints/include/udp_server_endpoint_impl.hpp b/implementation/endpoints/include/udp_server_endpoint_impl.hpp index db936f3..4ceefb6 100644 --- a/implementation/endpoints/include/udp_server_endpoint_impl.hpp +++ b/implementation/endpoints/include/udp_server_endpoint_impl.hpp @@ -48,6 +48,7 @@ public: std::chrono::nanoseconds *_maximum_retention) const; VSOMEIP_EXPORT void join(const std::string &_address); + VSOMEIP_EXPORT void join_unlocked(const std::string &_address); void leave(const std::string &_address); void add_default_target(service_t _service, diff --git a/implementation/endpoints/src/endpoint_manager_impl.cpp b/implementation/endpoints/src/endpoint_manager_impl.cpp index dc02aa5..32ba31d 100644 --- a/implementation/endpoints/src/endpoint_manager_impl.cpp +++ b/implementation/endpoints/src/endpoint_manager_impl.cpp @@ -146,18 +146,60 @@ void endpoint_manager_impl::is_remote_service_known( void endpoint_manager_impl::add_remote_service_info( service_t _service, instance_t _instance, const std::shared_ptr<endpoint_definition>& _ep_definition) { - std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); - remote_service_info_[_service][_instance][_ep_definition->is_reliable()] = + + std::shared_ptr<serviceinfo> its_info; + std::shared_ptr<endpoint> its_endpoint; + bool must_report(false); + { + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); + remote_service_info_[_service][_instance][_ep_definition->is_reliable()] = _ep_definition; + + if (_ep_definition->is_reliable()) { + its_endpoint = find_remote_client(_service, _instance, true); + must_report = (its_endpoint && its_endpoint->is_established_or_connected()); + if (must_report) + its_info = rm_->find_service(_service, _instance); + } + } + + if (must_report) + static_cast<routing_manager_impl*>(rm_)->service_endpoint_connected( + _service, _instance, its_info->get_major(), its_info->get_minor(), + its_endpoint, false); } void endpoint_manager_impl::add_remote_service_info( service_t _service, instance_t _instance, const std::shared_ptr<endpoint_definition>& _ep_definition_reliable, const std::shared_ptr<endpoint_definition>& _ep_definition_unreliable) { - std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); - remote_service_info_[_service][_instance][true] = _ep_definition_reliable; - remote_service_info_[_service][_instance][false] = _ep_definition_unreliable; + + std::shared_ptr<serviceinfo> its_info; + std::shared_ptr<endpoint> its_reliable, its_unreliable; + bool must_report(false); + { + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); + remote_service_info_[_service][_instance][false] = _ep_definition_unreliable; + remote_service_info_[_service][_instance][true] = _ep_definition_reliable; + + its_unreliable = find_remote_client(_service, _instance, false); + its_reliable = find_remote_client(_service, _instance, true); + + must_report = (its_unreliable && its_unreliable->is_established_or_connected() + && its_reliable && its_reliable->is_established_or_connected()); + + if (must_report) + its_info = rm_->find_service(_service, _instance); + } + + if (must_report) { + static_cast<routing_manager_impl*>(rm_)->service_endpoint_connected( + _service, _instance, its_info->get_major(), its_info->get_minor(), + its_unreliable, false); + static_cast<routing_manager_impl*>(rm_)->service_endpoint_connected( + _service, _instance, its_info->get_major(), its_info->get_minor(), + its_reliable, false); + } } void endpoint_manager_impl::clear_remote_service_info(service_t _service, @@ -230,7 +272,6 @@ std::shared_ptr<endpoint> endpoint_manager_impl::create_server_endpoint( return (its_endpoint); } - std::shared_ptr<endpoint> endpoint_manager_impl::find_server_endpoint( uint16_t _port, bool _reliable) const { std::shared_ptr<endpoint> its_endpoint; @@ -247,15 +288,20 @@ std::shared_ptr<endpoint> endpoint_manager_impl::find_server_endpoint( std::shared_ptr<endpoint> endpoint_manager_impl::find_or_create_server_endpoint( uint16_t _port, bool _reliable, bool _start, service_t _service, - instance_t _instance) { + instance_t _instance, bool &_is_found, bool _is_multicast) { std::shared_ptr<endpoint> its_endpoint = find_server_endpoint(_port, _reliable); + _is_found = false; if (!its_endpoint) { its_endpoint = create_server_endpoint(_port, _reliable, _start); + } else { + _is_found = true; } if (its_endpoint) { std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); - service_instances_[_service][its_endpoint.get()] = _instance; + if (!_is_multicast) { + service_instances_[_service][its_endpoint.get()] = _instance; + } its_endpoint->increment_use_count(); } return (its_endpoint); @@ -369,6 +415,7 @@ void endpoint_manager_impl::clear_client_endpoints(service_t _service, instance_ void endpoint_manager_impl::find_or_create_multicast_endpoint( service_t _service, instance_t _instance, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_address, uint16_t _port) { bool multicast_known(false); { @@ -386,25 +433,27 @@ void endpoint_manager_impl::find_or_create_multicast_endpoint( } } } - if (!multicast_known) { - // Save multicast info to be able to delete the endpoint - // as soon as the instance stops offering its service - std::shared_ptr<endpoint_definition> endpoint_def = - endpoint_definition::get(_address, _port, false, _service, _instance); - multicast_info[_service][_instance] = endpoint_def; - } } const bool is_someip = configuration_->is_someip(_service, _instance); - + bool _is_found(false); // Create multicast endpoint & join multicase group std::shared_ptr<endpoint> its_endpoint = find_or_create_server_endpoint( - _port, false, is_someip, _service, _instance); + _port, false, is_someip, _service, _instance, _is_found, true); + if (!_is_found) { + // Only save multicast info if we created a new endpoint + // to be able to delete the new endpoint + // as soon as the instance stops offering its service + std::shared_ptr<endpoint_definition> endpoint_def = + endpoint_definition::get(_address, _port, false, _service, _instance); + multicast_info[_service][_instance] = endpoint_def; + } + if (its_endpoint) { if (!multicast_known) { std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); - service_instances_[_service][its_endpoint.get()] = _instance; + service_instances_multicast_[_service][_sender] = _instance; } - dynamic_cast<udp_server_endpoint_impl*>(its_endpoint.get())->join( + dynamic_cast<udp_server_endpoint_impl*>(its_endpoint.get())->join_unlocked( _address.to_string()); } else { VSOMEIP_ERROR <<"Could not find/create multicast endpoint!"; @@ -412,7 +461,6 @@ void endpoint_manager_impl::find_or_create_multicast_endpoint( } void endpoint_manager_impl::clear_multicast_endpoints(service_t _service, instance_t _instance) { - std::shared_ptr<endpoint> multicast_endpoint; std::string address; @@ -438,14 +486,14 @@ void endpoint_manager_impl::clear_multicast_endpoints(service_t _service, instan if (0 >= multicast_info[_service].size()) { multicast_info.erase(_service); } - // Clear service_instances_ for multicast endpoint - (void)remove_instance(_service, multicast_endpoint.get()); + (void)remove_instance_multicast(_service, _instance); } } } if (multicast_endpoint) { dynamic_cast<udp_server_endpoint_impl*>( multicast_endpoint.get())->leave(address); + multicast_endpoint->stop(); } } @@ -607,6 +655,20 @@ instance_t endpoint_manager_impl::find_instance( return its_instance; } +instance_t endpoint_manager_impl::find_instance_multicast( + service_t _service, const boost::asio::ip::address &_sender) const { + instance_t its_instance(0xFFFF); + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); + auto found_service = service_instances_multicast_.find(_service); + if (found_service != service_instances_multicast_.end()) { + auto found_sender = found_service->second.find(_sender); + if (found_sender != found_service->second.end()) { + its_instance = found_sender->second; + } + } + return its_instance; +} + bool endpoint_manager_impl::remove_instance(service_t _service, endpoint* const _endpoint) { std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); @@ -622,6 +684,25 @@ bool endpoint_manager_impl::remove_instance(service_t _service, return (_endpoint->get_use_count() == 0); } +bool endpoint_manager_impl::remove_instance_multicast(service_t _service, + instance_t _instance) { + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); + auto found_service = service_instances_multicast_.find(_service); + if (found_service != service_instances_multicast_.end()) { + for (auto &its_sender : found_service->second) { + if (its_sender.second == _instance) { + if (found_service->second.erase(its_sender.first)) { + if (!found_service->second.size()) { + service_instances_multicast_.erase(_service); + } + } + return (true); + } + } + } + return (false); +} + void endpoint_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { // Is called when endpoint->connect succeeded! struct service_info { diff --git a/implementation/endpoints/src/server_endpoint_impl.cpp b/implementation/endpoints/src/server_endpoint_impl.cpp index 06b61f4..90aea96 100644 --- a/implementation/endpoints/src/server_endpoint_impl.cpp +++ b/implementation/endpoints/src/server_endpoint_impl.cpp @@ -34,7 +34,9 @@ server_endpoint_impl<Protocol>::server_endpoint_impl( configuration::endpoint_queue_limit_t _queue_limit, const std::shared_ptr<configuration>& _configuration) : endpoint_impl<Protocol>(_endpoint_host, _routing_host, _local, _io, _max_message_size, - _queue_limit, _configuration) { + _queue_limit, _configuration), + sent_timer_(_io) { + is_sending_ = false; } template<typename Protocol> @@ -594,6 +596,14 @@ void server_endpoint_impl<Protocol>::send_cbk( boost::system::error_code const &_error, std::size_t _bytes) { (void)_bytes; + { + std::lock_guard<std::mutex> its_sent_lock(sent_mutex_); + is_sending_ = false; + + boost::system::error_code ec; + sent_timer_.cancel(ec); + } + std::lock_guard<std::mutex> its_lock(mutex_); auto check_if_all_msgs_for_stopped_service_are_sent = [&]() { diff --git a/implementation/endpoints/src/tcp_client_endpoint_impl.cpp b/implementation/endpoints/src/tcp_client_endpoint_impl.cpp index 2a86244..c490f53 100644 --- a/implementation/endpoints/src/tcp_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/tcp_client_endpoint_impl.cpp @@ -915,7 +915,7 @@ void tcp_client_endpoint_impl::wait_until_sent(const boost::system::error_code & its_sent_lock.unlock(); if (!_error) VSOMEIP_WARNING << __func__ - << ": Maximum wait time for send operation exceeded."; + << ": Maximum wait time for send operation exceeded for tce."; std::shared_ptr<endpoint_host> its_ep_host = endpoint_host_.lock(); its_ep_host->on_disconnect(shared_from_this()); diff --git a/implementation/endpoints/src/tcp_server_endpoint_impl.cpp b/implementation/endpoints/src/tcp_server_endpoint_impl.cpp index b7b1d0a..1cd2b5b 100644 --- a/implementation/endpoints/src/tcp_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/tcp_server_endpoint_impl.cpp @@ -408,6 +408,11 @@ void tcp_server_endpoint_impl::connection::send_queued( { std::lock_guard<std::mutex> its_lock(socket_mutex_); + { + std::lock_guard<std::mutex> its_sent_lock(its_server->sent_mutex_); + its_server->is_sending_ = true; + } + boost::asio::async_write(socket_, boost::asio::buffer(*its_buffer), std::bind(&tcp_server_endpoint_impl::connection::write_completion_condition, shared_from_this(), @@ -652,19 +657,15 @@ void tcp_server_endpoint_impl::connection::receive_cbk( << " remote: " << get_address_port_remote() << ". Closing connection due to missing/broken data TCP stream."; } - { - std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); - stop(); - } - its_server->remove_connection(this); + wait_until_sent(boost::asio::error::operation_aborted); return; } else if (max_message_size_ != MESSAGE_SIZE_UNLIMITED && current_message_size > max_message_size_) { - std::lock_guard<std::mutex> its_lock(socket_mutex_); recv_buffer_size_ = 0; recv_buffer_.resize(recv_buffer_size_initial_, 0x0); recv_buffer_.shrink_to_fit(); if (magic_cookies_enabled_) { + std::lock_guard<std::mutex> its_lock(socket_mutex_); VSOMEIP_ERROR << "Received a TCP message which exceeds " << "maximum message size (" << std::dec << current_message_size @@ -674,19 +675,18 @@ void tcp_server_endpoint_impl::connection::receive_cbk( << get_address_port_local() << " remote: " << get_address_port_remote(); } else { - VSOMEIP_ERROR << "Received a TCP message which exceeds " - << "maximum message size (" - << std::dec << current_message_size - << " > " << std::dec << max_message_size_ - << ") Magic cookies are disabled: " - << "Connection will be closed! local: " - << get_address_port_local() << " remote: " - << get_address_port_remote(); { - std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); - stop(); + std::lock_guard<std::mutex> its_lock(socket_mutex_); + VSOMEIP_ERROR << "Received a TCP message which exceeds " + << "maximum message size (" + << std::dec << current_message_size + << " > " << std::dec << max_message_size_ + << ") Magic cookies are disabled: " + << "Connection will be closed! local: " + << get_address_port_local() << " remote: " + << get_address_port_remote(); } - its_server->remove_connection(this); + wait_until_sent(boost::asio::error::operation_aborted); return; } } else if (current_message_size > recv_buffer_size_) { @@ -720,11 +720,7 @@ void tcp_server_endpoint_impl::connection::receive_cbk( << " remote: " << get_address_port_remote() << ". Closing connection due to missing/broken data TCP stream."; } - { - std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); - stop(); - } - its_server->remove_connection(this); + wait_until_sent(boost::asio::error::operation_aborted); return; } } @@ -752,11 +748,7 @@ void tcp_server_endpoint_impl::connection::receive_cbk( << " local: " << get_address_port_local() << " remote: " << get_address_port_remote(); } - { - std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); - stop(); - } - its_server->remove_connection(this); + wait_until_sent(boost::asio::error::operation_aborted); } } @@ -962,4 +954,27 @@ bool tcp_server_endpoint_impl::tp_segmentation_enabled(service_t _service, return false; } +void tcp_server_endpoint_impl::connection::wait_until_sent(const boost::system::error_code &_error) { + std::shared_ptr<tcp_server_endpoint_impl> its_server(server_.lock()); + std::unique_lock<std::mutex> its_sent_lock(its_server->sent_mutex_); + if (!its_server->is_sending_ || !_error) { + its_sent_lock.unlock(); + if (!_error) + VSOMEIP_WARNING << __func__ + << ": Maximum wait time for send operation exceeded for tse."; + { + std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); + stop(); + } + its_server->remove_connection(this); + } else { + std::chrono::milliseconds its_timeout(VSOMEIP_MAX_TCP_SENT_WAIT_TIME); + boost::system::error_code ec; + its_server->sent_timer_.expires_from_now(its_timeout, ec); + its_server->sent_timer_.async_wait(std::bind(&tcp_server_endpoint_impl::connection::wait_until_sent, + std::dynamic_pointer_cast<tcp_server_endpoint_impl::connection>(shared_from_this()), + std::placeholders::_1)); + } +} + } // namespace vsomeip_v3 diff --git a/implementation/endpoints/src/tp_message.cpp b/implementation/endpoints/src/tp_message.cpp index f786a4f..57696ca 100644 --- a/implementation/endpoints/src/tp_message.cpp +++ b/implementation/endpoints/src/tp_message.cpp @@ -301,6 +301,11 @@ bool tp_message::check_lengths(const byte_t* const _data, _data[VSOMEIP_LENGTH_POS_MIN + 1], _data[VSOMEIP_LENGTH_POS_MIN + 2], _data[VSOMEIP_LENGTH_POS_MAX]); + const tp_header_t its_tp_header = VSOMEIP_BYTES_TO_LONG( + _data[VSOMEIP_TP_HEADER_POS_MIN], + _data[VSOMEIP_TP_HEADER_POS_MIN + 1], + _data[VSOMEIP_TP_HEADER_POS_MIN + 2], + _data[VSOMEIP_TP_HEADER_POS_MAX]); bool ret(true); if (!tp::tp_flag_is_set(_data[VSOMEIP_MESSAGE_TYPE_POS])) { VSOMEIP_ERROR << __func__ << ": TP flag not set " @@ -344,6 +349,14 @@ bool tp_message::check_lengths(const byte_t* const _data, << " current message size: " << std::dec << current_message_size_ << " maximum message size: " << std::dec << max_message_size_; ret = false; + } else if (tp::get_offset(its_tp_header) + _segment_size > max_message_size_ ) { + VSOMEIP_ERROR << __func__ << ": SomeIP/TP offset field exceeds maximum configured message size: " + << get_message_id(_data, _data_length) + << " TP offset [bytes]: " << std::dec << tp::get_offset(its_tp_header) + << " segment size: " << std::dec << _segment_size + << " current message size: " << std::dec << current_message_size_ + << " maximum message size: " << std::dec << max_message_size_; + ret = false; } return ret; } diff --git a/implementation/endpoints/src/udp_client_endpoint_impl.cpp b/implementation/endpoints/src/udp_client_endpoint_impl.cpp index 6bbd337..ff24173 100644 --- a/implementation/endpoints/src/udp_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/udp_client_endpoint_impl.cpp @@ -93,8 +93,8 @@ void udp_client_endpoint_impl::connect() { // If specified, bind to device std::string its_device(configuration_->get_device()); if (its_device != "") { - if (!setsockopt(socket_->native_handle(), - SOL_SOCKET, SO_BINDTODEVICE, its_device.c_str(), (int)its_device.size())) { + if (setsockopt(socket_->native_handle(), + SOL_SOCKET, SO_BINDTODEVICE, its_device.c_str(), (int)its_device.size()) == -1) { VSOMEIP_WARNING << "UDP Client: Could not bind to device \"" << its_device << "\""; } } @@ -268,7 +268,7 @@ void udp_client_endpoint_impl::receive_cbk( #if 0 std::stringstream msg; msg << "ucei::rcb(" << _error.message() << "): "; - for (std::size_t i = 0; i < _bytes + recv_buffer_size_; ++i) + for (std::size_t i = 0; i < _bytes; ++i) msg << std::hex << std::setw(2) << std::setfill('0') << (int) (*_recv_buffer)[i] << " "; VSOMEIP_INFO << msg.str(); diff --git a/implementation/endpoints/src/udp_server_endpoint_impl.cpp b/implementation/endpoints/src/udp_server_endpoint_impl.cpp index ce1bb2d..add828c 100644 --- a/implementation/endpoints/src/udp_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/udp_server_endpoint_impl.cpp @@ -291,6 +291,12 @@ bool udp_server_endpoint_impl::is_joined( void udp_server_endpoint_impl::join(const std::string &_address) { + std::lock_guard<std::mutex> its_lock(multicast_mutex_); + join_unlocked(_address); +} + +void udp_server_endpoint_impl::join_unlocked(const std::string &_address) { + bool has_received(false); // @@ -317,11 +323,11 @@ void udp_server_endpoint_impl::join(const std::string &_address) { if (!multicast_local_) { if (is_v4) { multicast_local_ = std::unique_ptr<endpoint_type>( - new endpoint_type(boost::asio::ip::address_v4::any(), local_port_)); + new endpoint_type(boost::asio::ip::address_v4::any(), local_port_)); } if (is_v6) { multicast_local_ = std::unique_ptr<endpoint_type>( - new endpoint_type(boost::asio::ip::address_v6::any(), local_port_)); + new endpoint_type(boost::asio::ip::address_v6::any(), local_port_)); } } @@ -398,7 +404,6 @@ void udp_server_endpoint_impl::join(const std::string &_address) { } }; - std::lock_guard<std::mutex> its_lock(multicast_mutex_); if (!is_joined(_address, &has_received)) { join_func(_address); } else if (!has_received) { @@ -556,7 +561,8 @@ void udp_server_endpoint_impl::on_message_received( } else if (current_message_size > VSOMEIP_RETURN_CODE_POS && (_buffer[i + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION || !utility::is_valid_message_type(tp::tp::tp_flag_unset(_buffer[i + VSOMEIP_MESSAGE_TYPE_POS])) || - !utility::is_valid_return_code(static_cast<return_code_e>(_buffer[i + VSOMEIP_RETURN_CODE_POS])) + !utility::is_valid_return_code(static_cast<return_code_e>(_buffer[i + VSOMEIP_RETURN_CODE_POS])) || + (tp::tp::tp_flag_is_set(_buffer[i + VSOMEIP_MESSAGE_TYPE_POS]) && get_local_port() == configuration_->get_sd_port()) )) { if (_buffer[i + VSOMEIP_PROTOCOL_VERSION_POS] != VSOMEIP_PROTOCOL_VERSION) { VSOMEIP_ERROR << "use: Wrong protocol version: 0x" @@ -585,6 +591,11 @@ void udp_server_endpoint_impl::on_message_received( << std::uint32_t(_buffer[i + VSOMEIP_RETURN_CODE_POS]) << " local: " << get_address_port_local() << " remote: " << its_remote_address << ":" << std::dec << its_remote_port; + } else if (tp::tp::tp_flag_is_set(_buffer[i + VSOMEIP_MESSAGE_TYPE_POS]) + && get_local_port() == configuration_->get_sd_port()) { + VSOMEIP_WARNING << "use: Received a SomeIP/TP message on SD port:" + << " local: " << get_address_port_local() + << " remote: " << its_remote_address << ":" << std::dec << its_remote_port; } return; } @@ -614,6 +625,15 @@ void udp_server_endpoint_impl::on_message_received( } } if (tp::tp::tp_flag_is_set(_buffer[i + VSOMEIP_MESSAGE_TYPE_POS])) { + const method_t its_method = VSOMEIP_BYTES_TO_WORD(_buffer[i + VSOMEIP_METHOD_POS_MIN], + _buffer[i + VSOMEIP_METHOD_POS_MAX]); + if (!tp_segmentation_enabled(its_service, its_method)) { + VSOMEIP_WARNING << "use: Received a SomeIP/TP message for service: 0x" << std::hex << its_service + << " method: 0x" << its_method << " which is not configured for TP:" + << " local: " << get_address_port_local() + << " remote: " << its_remote_address << ":" << std::dec << its_remote_port; + return; + } const auto res = tp_reassembler_->process_tp_message( &_buffer[i], current_message_size, its_remote_address, its_remote_port); diff --git a/implementation/helper/1.74/boost/asio/basic_datagram_socket_ext.hpp b/implementation/helper/1.74/boost/asio/basic_datagram_socket_ext.hpp new file mode 100644 index 0000000..49a4950 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/basic_datagram_socket_ext.hpp @@ -0,0 +1,1118 @@ +// +// basic_datagram_socket_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_DATAGRAM_SOCKET_EXT_HPP +#define BOOST_ASIO_BASIC_DATAGRAM_SOCKET_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <cstddef> +#include <boost/asio/basic_socket_ext.hpp> +#include <boost/asio/detail/handler_type_requirements_ext.hpp> +#include <boost/asio/detail/non_const_lvalue.hpp> +#include <boost/asio/detail/throw_error.hpp> +#include <boost/asio/detail/type_traits.hpp> +#include <boost/asio/error.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { + +#if !defined(BOOST_ASIO_BASIC_DATAGRAM_SOCKET_EXT_FWD_DECL) +#define BOOST_ASIO_BASIC_DATAGRAM_SOCKET_EXT_FWD_DECL + +// Forward declaration with defaulted arguments. +template <typename Protocol, typename Executor = executor> +class basic_datagram_socket_ext; + +#endif // !defined(BOOST_ASIO_BASIC_DATAGRAM_SOCKET_EXT_FWD_DECL) + +/// Provides datagram-oriented socket functionality. +/** + * The basic_datagram_socket class template provides asynchronous and blocking + * datagram-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template <typename Protocol, typename Executor> +class basic_datagram_socket_ext + : public basic_socket_ext<Protocol, Executor> +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template <typename Executor1> + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_datagram_socket_ext<Protocol, Executor1> other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket_ext<Protocol, + Executor>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_datagram_socket_ext(const executor_type& ex) + : basic_socket_ext<Protocol, Executor>(ex) + { + } + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template <typename ExecutionContext> + explicit basic_datagram_socket_ext(ExecutionContext& context, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext<Protocol, Executor>(context) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_datagram_socket_ext(const executor_type& ex, const protocol_type& protocol) + : basic_socket_ext<Protocol, Executor>(ex, protocol) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_datagram_socket_ext(ExecutionContext& context, + const protocol_type& protocol, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext<Protocol, Executor>(context, protocol) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_datagram_socket_ext(const executor_type& ex, const endpoint_type& endpoint) + : basic_socket_ext<Protocol, Executor>(ex, endpoint) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_datagram_socket_ext(ExecutionContext& context, + const endpoint_type& endpoint, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext<Protocol, Executor>(context, endpoint) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_datagram_socket_ext(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket_ext<Protocol, Executor>(ex, protocol, native_socket) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_datagram_socket_ext(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext<Protocol, Executor>(context, protocol, native_socket) + { + } + +#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_datagram_socket from another. + /** + * This constructor moves a datagram socket from one object to another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + basic_datagram_socket_ext(basic_datagram_socket_ext&& other) + : basic_socket_ext<Protocol, Executor>(std::move(other)) + { + } + + /// Move-assign a basic_datagram_socket from another. + /** + * This assignment operator moves a datagram socket from one object to + * another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + basic_datagram_socket_ext& operator=(basic_datagram_socket_ext&& other) + { + basic_socket_ext<Protocol, Executor>::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_datagram_socket from a socket of another protocol + /// type. + /** + * This constructor moves a datagram socket from one object to another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + template <typename Protocol1, typename Executor1> + basic_datagram_socket_ext(basic_datagram_socket_ext<Protocol1, Executor1>&& other, + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value + >::type* = 0) + : basic_socket_ext<Protocol, Executor>(std::move(other)) + { + } + + /// Move-assign a basic_datagram_socket from a socket of another protocol + /// type. + /** + * This assignment operator moves a datagram socket from one object to + * another. + * + * @param other The other basic_datagram_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. + */ + template <typename Protocol1, typename Executor1> + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value, + basic_datagram_socket_ext& + >::type operator=(basic_datagram_socket_ext<Protocol1, Executor1>&& other) + { + basic_socket_ext<Protocol, Executor>::operator=(std::move(other)); + return *this; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_datagram_socket_ext() + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(boost::asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence> + std::size_t send(const ConstBufferSequence& buffers) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + boost::asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template <typename ConstBufferSequence> + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + boost::asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template <typename ConstBufferSequence> + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to asynchronously send data on the datagram socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return async_initiate<WriteHandler, + void (boost::system::error_code, std::size_t)>( + initiate_async_send(), handler, this, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to asynchronously send data on the datagram socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + */ + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return async_initiate<WriteHandler, + void (boost::system::error_code, std::size_t)>( + initiate_async_send(), handler, this, buffers, flags); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * boost::asio::ip::udp::endpoint destination( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(boost::asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence> + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, 0, ec); + boost::asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ConstBufferSequence> + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, flags, ec); + boost::asio::detail::throw_error(ec, "send_to"); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + */ + template <typename ConstBufferSequence> + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + boost::system::error_code& ec) + { + return this->impl_.get_service().send_to(this->impl_.get_implementation(), + buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * boost::asio::ip::udp::endpoint destination( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * boost::asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return async_initiate<WriteHandler, + void (boost::system::error_code, std::size_t)>( + initiate_async_send_to(), handler, this, buffers, + destination, socket_base::message_flags(0)); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + */ + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return async_initiate<WriteHandler, + void (boost::system::error_code, std::size_t)>( + initiate_async_send_to(), handler, this, buffers, destination, flags); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(boost::asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence> + std::size_t receive(const MutableBufferSequence& buffers) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + boost::asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template <typename MutableBufferSequence> + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + boost::asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template <typename MutableBufferSequence> + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)) + async_receive(const MutableBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return async_initiate<ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)>( + initiate_async_receive(), handler, this, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + */ + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return async_initiate<ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)>( + initiate_async_receive(), handler, this, buffers, flags); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @returns The number of bytes received. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * boost::asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * boost::asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence> + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, 0, ec); + boost::asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename MutableBufferSequence> + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); + boost::asio::detail::throw_error(ec, "receive_from"); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + */ + template <typename MutableBufferSequence> + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + boost::system::error_code& ec) + { + return this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * boost::asio::buffer(data, size), sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return async_initiate<ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)>( + initiate_async_receive_from(), handler, this, buffers, + &sender_endpoint, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + */ + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)) + async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return async_initiate<ReadHandler, + void (boost::system::error_code, std::size_t, boost::asio::ip::address)>( + initiate_async_receive_from(), handler, + this, buffers, &sender_endpoint, flags); + } + +private: + struct initiate_async_send + { + template <typename WriteHandler, typename ConstBufferSequence> + void operator()(BOOST_ASIO_MOVE_ARG(WriteHandler) handler, + basic_datagram_socket_ext* self, const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue<WriteHandler> handler2(handler); + self->impl_.get_service().async_send( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_executor()); + } + }; + + struct initiate_async_send_to + { + template <typename WriteHandler, typename ConstBufferSequence> + void operator()(BOOST_ASIO_MOVE_ARG(WriteHandler) handler, + basic_datagram_socket_ext* self, const ConstBufferSequence& buffers, + const endpoint_type& destination, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue<WriteHandler> handler2(handler); + self->impl_.get_service().async_send_to( + self->impl_.get_implementation(), buffers, destination, flags, + handler2.value, self->impl_.get_executor()); + } + }; + + struct initiate_async_receive + { + template <typename ReadHandler, typename MutableBufferSequence> + void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler, + basic_datagram_socket_ext* self, const MutableBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + BOOST_ASIO_READ_HANDLER_CHECK_EXT(ReadHandler, handler) type_check; + + detail::non_const_lvalue<ReadHandler> handler2(handler); + self->impl_.get_service().async_receive( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_executor()); + } + }; + + struct initiate_async_receive_from + { + template <typename ReadHandler, typename MutableBufferSequence> + void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler, + basic_datagram_socket_ext* self, const MutableBufferSequence& buffers, + endpoint_type* sender_endpoint, socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + BOOST_ASIO_READ_HANDLER_CHECK_EXT(ReadHandler, handler) type_check; + + detail::non_const_lvalue<ReadHandler> handler2(handler); + self->impl_.get_service().async_receive_from( + self->impl_.get_implementation(), buffers, *sender_endpoint, flags, + handler2.value, self->impl_.get_executor()); + } + }; +}; + +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_BASIC_DATAGRAM_SOCKET_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/basic_socket_acceptor_ext.hpp b/implementation/helper/1.74/boost/asio/basic_socket_acceptor_ext.hpp new file mode 100644 index 0000000..998b86d --- /dev/null +++ b/implementation/helper/1.74/boost/asio/basic_socket_acceptor_ext.hpp @@ -0,0 +1,2381 @@ +// +// basic_socket_acceptor_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_EXT_HPP +#define BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/basic_socket_ext_local.hpp> +#include <boost/asio/detail/handler_type_requirements_ext.hpp> +#include <boost/asio/detail/io_object_impl.hpp> +#include <boost/asio/detail/non_const_lvalue.hpp> +#include <boost/asio/detail/throw_error.hpp> +#include <boost/asio/detail/type_traits.hpp> +#include <boost/asio/error.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/socket_base.hpp> + +#if defined(BOOST_ASIO_WINDOWS_RUNTIME) +# include <boost/asio/detail/null_socket_service.hpp> +#elif defined(BOOST_ASIO_HAS_IOCP) +# include <boost/asio/detail/win_iocp_socket_service.hpp> +#else +# include <boost/asio/detail/reactive_socket_service_ext_local.hpp> +#endif + +#if defined(BOOST_ASIO_HAS_MOVE) +# include <utility> +#endif // defined(BOOST_ASIO_HAS_MOVE) + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { + +#if !defined(BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_EXT_FWD_DECL) +#define BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_EXT_FWD_DECL + +// Forward declaration with defaulted arguments. +template <typename Protocol, typename Executor = executor> +class basic_socket_acceptor_ext; + +#endif // !defined(BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_EXT_FWD_DECL) + +/// Provides the ability to accept new connections. +/** + * The basic_socket_acceptor_ext class template is used for accepting new socket + * connections. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * Opening a socket acceptor with the SO_REUSEADDR option enabled: + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port); + * acceptor.open(endpoint.protocol()); + * acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ +template <typename Protocol, typename Executor> +class basic_socket_acceptor_ext + : public socket_base +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// The native representation of an acceptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(BOOST_ASIO_WINDOWS_RUNTIME) + typedef typename detail::null_socket_service< + Protocol>::native_handle_type native_handle_type; +#elif defined(BOOST_ASIO_HAS_IOCP) + typedef typename detail::win_iocp_socket_service< + Protocol>::native_handle_type native_handle_type; +#else + typedef typename detail::reactive_socket_service_ext_local< + Protocol>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + */ + explicit basic_socket_acceptor_ext(const executor_type& ex) + : impl_(ex) + { + } + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + */ + template <typename ExecutionContext> + explicit basic_socket_acceptor_ext(ExecutionContext& context, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_acceptor_ext(const executor_type& ex, const protocol_type& protocol) + : impl_(ex) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_acceptor_ext(ExecutionContext& context, + const protocol_type& protocol, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor<Protocol> acceptor(my_context); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ + basic_socket_acceptor_ext(const executor_type& ex, + const endpoint_type& endpoint, bool reuse_addr = true) + : impl_(ex) + { + boost::system::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + if (reuse_addr) + { + impl_.get_service().set_option(impl_.get_implementation(), + socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "set_option"); + } + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + impl_.get_service().listen(impl_.get_implementation(), + socket_base::max_listen_connections, ec); + boost::asio::detail::throw_error(ec, "listen"); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor<Protocol> acceptor(my_context); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ + template <typename ExecutionContext> + basic_socket_acceptor_ext(ExecutionContext& context, + const endpoint_type& endpoint, bool reuse_addr = true, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + if (reuse_addr) + { + impl_.get_service().set_option(impl_.get_implementation(), + socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "set_option"); + } + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + impl_.get_service().listen(impl_.get_implementation(), + socket_base::max_listen_connections, ec); + boost::asio::detail::throw_error(ec, "listen"); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param ex The I/O executor that the acceptor will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_acceptor_ext(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_acceptor) + : impl_(ex) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_acceptor_ext(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_acceptor, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + +#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket_acceptor from another. + /** + * This constructor moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + basic_socket_acceptor_ext(basic_socket_acceptor_ext&& other) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket_acceptor from another. + /** + * This assignment operator moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + basic_socket_acceptor_ext& operator=(basic_socket_acceptor_ext&& other) + { + impl_ = std::move(other.impl_); + return *this; + } + + // All socket acceptors have access to each other's implementations. + template <typename Protocol1, typename Executor1> + friend class basic_socket_acceptor_ext; + + /// Move-construct a basic_socket_acceptor from an acceptor of another + /// protocol type. + /** + * This constructor moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + template <typename Protocol1, typename Executor1> + basic_socket_acceptor_ext(basic_socket_acceptor_ext<Protocol1, Executor1>&& other, + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value + >::type* = 0) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket_acceptor from an acceptor of another protocol + /// type. + /** + * This assignment operator moves an acceptor from one object to another. + * + * @param other The other basic_socket_acceptor object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. + */ + template <typename Protocol1, typename Executor1> + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value, + basic_socket_acceptor_ext& + >::type operator=(basic_socket_acceptor_ext<Protocol1, Executor1>&& other) + { + basic_socket_acceptor_ext tmp(std::move(other)); + impl_ = std::move(tmp.impl_); + return *this; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the acceptor. + /** + * This function destroys the acceptor, cancelling any outstanding + * asynchronous operations associated with the acceptor as if by calling + * @c cancel. + */ + ~basic_socket_acceptor_ext() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() BOOST_ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * acceptor.open(boost::asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * boost::system::error_code ec; + * acceptor.open(boost::asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID open(const protocol_type& protocol, + boost::system::error_code& ec) + { + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws boost::system::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, + const native_handle_type& native_acceptor) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @param ec Set to indicate what error occurred, if any. + */ + BOOST_ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, + const native_handle_type& native_acceptor, boost::system::error_code& ec) + { + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the acceptor is open. + bool is_open() const + { + return impl_.get_service().is_open(impl_.get_implementation()); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 12345); + * acceptor.open(endpoint.protocol()); + * acceptor.bind(endpoint); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + boost::system::error_code ec; + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 12345); + * acceptor.open(endpoint.protocol()); + * boost::system::error_code ec; + * acceptor.bind(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, + boost::system::error_code& ec) + { + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @throws boost::system::system_error Thrown on failure. + */ + void listen(int backlog = socket_base::max_listen_connections) + { + boost::system::error_code ec; + impl_.get_service().listen(impl_.get_implementation(), backlog, ec); + boost::asio::detail::throw_error(ec, "listen"); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::system::error_code ec; + * acceptor.listen(boost::asio::socket_base::max_listen_connections, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID listen(int backlog, boost::system::error_code& ec) + { + impl_.get_service().listen(impl_.get_implementation(), backlog, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @throws boost::system::system_error Thrown on failure. + */ + void close() + { + boost::system::error_code ec; + impl_.get_service().close(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "close"); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::system::error_code ec; + * acceptor.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID close(boost::system::error_code& ec) + { + impl_.get_service().close(impl_.get_implementation(), ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the boost::asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with boost::asio::error::operation_not_supported on + * these platforms. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + boost::system::error_code ec; + native_handle_type s = impl_.get_service().release( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the boost::asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with boost::asio::error::operation_not_supported on + * these platforms. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(boost::system::error_code& ec) + { + return impl_.get_service().release(impl_.get_implementation(), ec); + } + + /// Get the native acceptor representation. + /** + * This function may be used to obtain the underlying representation of the + * acceptor. This is intended to allow access to native acceptor functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return impl_.get_service().native_handle(impl_.get_implementation()); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @throws boost::system::system_error Thrown on failure. + */ + void cancel() + { + boost::system::error_code ec; + impl_.get_service().cancel(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + BOOST_ASIO_SYNC_OP_VOID cancel(boost::system::error_code& ec) + { + impl_.get_service().cancel(impl_.get_implementation(), ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * boost::asio::socket_base::reuse_address + * boost::asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::acceptor::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + */ + template <typename SettableSocketOption> + void set_option(const SettableSocketOption& option) + { + boost::system::error_code ec; + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + boost::asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * boost::asio::socket_base::reuse_address + * boost::asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::acceptor::reuse_address option(true); + * boost::system::error_code ec; + * acceptor.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename SettableSocketOption> + BOOST_ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, + boost::system::error_code& ec) + { + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * boost::asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::acceptor::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template <typename GettableSocketOption> + void get_option(GettableSocketOption& option) const + { + boost::system::error_code ec; + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + boost::asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * boost::asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::acceptor::reuse_address option; + * boost::system::error_code ec; + * acceptor.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template <typename GettableSocketOption> + BOOST_ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + boost::system::error_code& ec) const + { + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the acceptor. + /** + * This function is used to execute an IO control command on the acceptor. + * + * @param command The IO control command to be performed on the acceptor. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::acceptor::non_blocking_io command(true); + * socket.io_control(command); + * @endcode + */ + template <typename IoControlCommand> + void io_control(IoControlCommand& command) + { + boost::system::error_code ec; + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + boost::asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the acceptor. + /** + * This function is used to execute an IO control command on the acceptor. + * + * @param command The IO control command to be performed on the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::acceptor::non_blocking_io command(true); + * boost::system::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename IoControlCommand> + BOOST_ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + boost::system::error_code& ec) + { + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the acceptor. + /** + * @returns @c true if the acceptor's synchronous operations will fail with + * boost::asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + bool non_blocking() const + { + return impl_.get_service().non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the acceptor. + /** + * @param mode If @c true, the acceptor's synchronous operations will fail + * with boost::asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + void non_blocking(bool mode) + { + boost::system::error_code ec; + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + boost::asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the acceptor. + /** + * @param mode If @c true, the acceptor's synchronous operations will fail + * with boost::asio::error::would_block if they are unable to perform the + * requested operation immediately. If @c false, synchronous operations will + * block until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + BOOST_ASIO_SYNC_OP_VOID non_blocking( + bool mode, boost::system::error_code& ec) + { + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native acceptor. This mode has no effect on the behaviour of the acceptor + * object's synchronous operations. + * + * @returns @c true if the underlying acceptor is in non-blocking mode and + * direct system calls may fail with boost::asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the acceptor object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native acceptor. + */ + bool native_non_blocking() const + { + return impl_.get_service().native_non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native acceptor. It has no effect on the behaviour of the acceptor object's + * synchronous operations. + * + * @param mode If @c true, the underlying acceptor is put into non-blocking + * mode and direct system calls may fail with boost::asio::error::would_block + * (or the equivalent system error). + * + * @throws boost::system::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with boost::asio::error::invalid_argument, as the + * combination does not make sense. + */ + void native_non_blocking(bool mode) + { + boost::system::error_code ec; + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + boost::asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native acceptor implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native acceptor. It has no effect on the behaviour of the acceptor object's + * synchronous operations. + * + * @param mode If @c true, the underlying acceptor is put into non-blocking + * mode and direct system calls may fail with boost::asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with boost::asio::error::invalid_argument, as the + * combination does not make sense. + */ + BOOST_ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, boost::system::error_code& ec) + { + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @returns An object that represents the local endpoint of the acceptor. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + boost::system::error_code ec; + endpoint_type ep = impl_.get_service().local_endpoint( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "local_endpoint"); + return ep; + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the acceptor. + * Returns a default-constructed endpoint object if an error occurred and the + * error handler did not throw an exception. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::system::error_code ec; + * boost::asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(boost::system::error_code& ec) const + { + return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); + } + + /// Wait for the acceptor to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for an acceptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @par Example + * Waiting for an acceptor to become readable. + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.wait(boost::asio::ip::tcp::acceptor::wait_read); + * @endcode + */ + void wait(wait_type w) + { + boost::system::error_code ec; + impl_.get_service().wait(impl_.get_implementation(), w, ec); + boost::asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the acceptor to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for an acceptor to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for an acceptor to become readable. + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::system::error_code ec; + * acceptor.wait(boost::asio::ip::tcp::acceptor::wait_read, ec); + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID wait(wait_type w, boost::system::error_code& ec) + { + impl_.get_service().wait(impl_.get_implementation(), w, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the acceptor to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for an acceptor to + * enter a ready to read, write or error condition state. + * + * @param w Specifies the desired acceptor state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void wait_handler(const boost::system::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_wait( + * boost::asio::ip::tcp::acceptor::wait_read, + * wait_handler); + * @endcode + */ + template <typename WaitHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (boost::system::error_code)) + async_wait(wait_type w, BOOST_ASIO_MOVE_ARG(WaitHandler) handler) + { + return async_initiate<WaitHandler, void (boost::system::error_code)>( + initiate_async_wait(), handler, this, w); + } + +#if !defined(BOOST_ASIO_NO_EXTENSIONS) + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(my_context); + * acceptor.accept(socket); + * @endcode + */ + template <typename Protocol1, typename Executor1> + void accept(basic_socket_ext_local<Protocol1, Executor1>& peer, + typename enable_if< + is_convertible<Protocol, Protocol1>::value + >::type* = 0) + { + boost::system::error_code ec; + impl_.get_service().accept(impl_.get_implementation(), + peer, static_cast<endpoint_type*>(0), ec); + boost::asio::detail::throw_error(ec, "accept"); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(my_context); + * boost::system::error_code ec; + * acceptor.accept(socket, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename Protocol1, typename Executor1> + BOOST_ASIO_SYNC_OP_VOID accept( + basic_socket_ext_local<Protocol1, Executor1>& peer, boost::system::error_code& ec, + typename enable_if< + is_convertible<Protocol, Protocol1>::value + >::type* = 0) + { + impl_.get_service().accept(impl_.get_implementation(), + peer, static_cast<endpoint_type*>(0), ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket. The function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void accept_handler(const boost::system::error_code& error) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(my_context); + * acceptor.async_accept(socket, accept_handler); + * @endcode + */ + template <typename Protocol1, typename Executor1, typename AcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, + void (boost::system::error_code)) + async_accept(basic_socket_ext_local<Protocol1, Executor1>& peer, + BOOST_ASIO_MOVE_ARG(AcceptHandler) handler, + typename enable_if< + is_convertible<Protocol, Protocol1>::value + >::type* = 0) + { + return async_initiate<AcceptHandler, void (boost::system::error_code)>( + initiate_async_accept(), handler, this, + &peer, static_cast<endpoint_type*>(0)); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint; + * acceptor.accept(socket, endpoint); + * @endcode + */ + template <typename Executor1> + void accept(basic_socket_ext_local<protocol_type, Executor1>& peer, + endpoint_type& peer_endpoint) + { + boost::system::error_code ec; + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + boost::asio::detail::throw_error(ec, "accept"); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint; + * boost::system::error_code ec; + * acceptor.accept(socket, endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename Executor1> + BOOST_ASIO_SYNC_OP_VOID accept(basic_socket_ext_local<protocol_type, Executor1>& peer, + endpoint_type& peer_endpoint, boost::system::error_code& ec) + { + impl_.get_service().accept( + impl_.get_implementation(), peer, &peer_endpoint, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket, and additionally obtain the endpoint of the remote peer. The + * function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + */ + template <typename Executor1, typename AcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, + void (boost::system::error_code)) + async_accept(basic_socket_ext_local<protocol_type, Executor1>& peer, + endpoint_type& peer_endpoint, BOOST_ASIO_MOVE_ARG(AcceptHandler) handler) + { + return async_initiate<AcceptHandler, void (boost::system::error_code)>( + initiate_async_accept(), handler, this, &peer, &peer_endpoint); + } +#endif // !defined(BOOST_ASIO_NO_EXTENSIONS) + +#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + typename Protocol::socket accept() + { + boost::system::error_code ec; + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + boost::asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(acceptor.accept(ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket accept(boost::system::error_code& ec) + { + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * typename Protocol::socket peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void accept_handler(const boost::system::error_code& error, + * boost::asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_accept(accept_handler); + * @endcode + */ + template <typename MoveAcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (boost::system::error_code, typename Protocol::socket)) + async_accept(BOOST_ASIO_MOVE_ARG(MoveAcceptHandler) handler) + { + return async_initiate<MoveAcceptHandler, + void (boost::system::error_code, typename Protocol::socket)>( + initiate_async_move_accept(), handler, this, + impl_.get_executor(), static_cast<endpoint_type*>(0), + static_cast<typename Protocol::socket*>(0)); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly + * accepted socket. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + template <typename Executor1> + typename Protocol::socket::template rebind_executor<Executor1>::other + accept(const Executor1& ex, + typename enable_if< + is_executor<Executor1>::value + >::type* = 0) + { + boost::system::error_code ec; + typename Protocol::socket::template + rebind_executor<Executor1>::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + boost::asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + template <typename ExecutionContext> + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + { + boost::system::error_code ec; + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + boost::asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(acceptor.accept(my_context2, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename Executor1> + typename Protocol::socket::template rebind_executor<Executor1>::other + accept(const Executor1& ex, boost::system::error_code& ec, + typename enable_if< + is_executor<Executor1>::value + >::type* = 0) + { + typename Protocol::socket::template + rebind_executor<Executor1>::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::socket socket(acceptor.accept(my_context2, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename ExecutionContext> + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, boost::system::error_code& ec, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + { + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * Executor1>::other peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void accept_handler(const boost::system::error_code& error, + * boost::asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_accept(my_context2, accept_handler); + * @endcode + */ + template <typename Executor1, typename MoveAcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (boost::system::error_code, + typename Protocol::socket::template rebind_executor< + Executor1>::other)) + async_accept(const Executor1& ex, + BOOST_ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_executor<Executor1>::value + >::type* = 0) + { + typedef typename Protocol::socket::template rebind_executor< + Executor1>::other other_socket_type; + + return async_initiate<MoveAcceptHandler, + void (boost::system::error_code, other_socket_type)>( + initiate_async_move_accept(), handler, this, + ex, static_cast<endpoint_type*>(0), + static_cast<other_socket_type*>(0)); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * typename ExecutionContext::executor_type>::other peer + * // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void accept_handler(const boost::system::error_code& error, + * boost::asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_accept(my_context2, accept_handler); + * @endcode + */ + template <typename ExecutionContext, typename MoveAcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (boost::system::error_code, + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other)) + async_accept(ExecutionContext& context, + BOOST_ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + { + typedef typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other other_socket_type; + + return async_initiate<MoveAcceptHandler, + void (boost::system::error_code, other_socket_type)>( + initiate_async_move_accept(), handler, this, + context.get_executor(), static_cast<endpoint_type*>(0), + static_cast<other_socket_type*>(0)); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * boost::asio::ip::tcp::socket socket(acceptor.accept(endpoint)); + * @endcode + */ + typename Protocol::socket accept(endpoint_type& peer_endpoint) + { + boost::system::error_code ec; + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + boost::asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * boost::asio::ip::tcp::socket socket(acceptor.accept(endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + typename Protocol::socket accept( + endpoint_type& peer_endpoint, boost::system::error_code& ec) + { + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * typename Protocol::socket peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void accept_handler(const boost::system::error_code& error, + * boost::asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(endpoint, accept_handler); + * @endcode + */ + template <typename MoveAcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (boost::system::error_code, typename Protocol::socket)) + async_accept(endpoint_type& peer_endpoint, + BOOST_ASIO_MOVE_ARG(MoveAcceptHandler) handler) + { + return async_initiate<MoveAcceptHandler, + void (boost::system::error_code, typename Protocol::socket)>( + initiate_async_move_accept(), handler, this, + impl_.get_executor(), &peer_endpoint, + static_cast<typename Protocol::socket*>(0)); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * boost::asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint)); + * @endcode + */ + template <typename Executor1> + typename Protocol::socket::template rebind_executor<Executor1>::other + accept(const Executor1& ex, endpoint_type& peer_endpoint, + typename enable_if< + is_executor<Executor1>::value + >::type* = 0) + { + boost::system::error_code ec; + typename Protocol::socket::template + rebind_executor<Executor1>::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + boost::asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * boost::asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint)); + * @endcode + */ + template <typename ExecutionContext> + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, endpoint_type& peer_endpoint, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + { + boost::system::error_code ec; + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + boost::asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * boost::asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename Executor1> + typename Protocol::socket::template rebind_executor<Executor1>::other + accept(const executor_type& ex, + endpoint_type& peer_endpoint, boost::system::error_code& ec, + typename enable_if< + is_executor<Executor1>::value + >::type* = 0) + { + typename Protocol::socket::template + rebind_executor<Executor1>::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * boost::asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename ExecutionContext> + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, + endpoint_type& peer_endpoint, boost::system::error_code& ec, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + { + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly accepted + * socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * Executor1>::other peer // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void accept_handler(const boost::system::error_code& error, + * boost::asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(my_context2, endpoint, accept_handler); + * @endcode + */ + template <typename Executor1, typename MoveAcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (boost::system::error_code, + typename Protocol::socket::template rebind_executor< + Executor1>::other)) + async_accept(const Executor1& ex, endpoint_type& peer_endpoint, + BOOST_ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_executor<Executor1>::value + >::type* = 0) + { + typedef typename Protocol::socket::template rebind_executor< + Executor1>::other other_socket_type; + + return async_initiate<MoveAcceptHandler, + void (boost::system::error_code, other_socket_type)>( + initiate_async_move_accept(), handler, this, + ex, &peer_endpoint, + static_cast<other_socket_type*>(0)); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * typename ExecutionContext::executor_type>::other peer + * // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void accept_handler(const boost::system::error_code& error, + * boost::asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::acceptor acceptor(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(my_context2, endpoint, accept_handler); + * @endcode + */ + template <typename ExecutionContext, typename MoveAcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (boost::system::error_code, + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other)) + async_accept(ExecutionContext& context, + endpoint_type& peer_endpoint, + BOOST_ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + { + typedef typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other other_socket_type; + + return async_initiate<MoveAcceptHandler, + void (boost::system::error_code, other_socket_type)>( + initiate_async_move_accept(), handler, this, + context.get_executor(), &peer_endpoint, + static_cast<other_socket_type*>(0)); + } +#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +private: + // Disallow copying and assignment. + basic_socket_acceptor_ext(const basic_socket_acceptor_ext&) BOOST_ASIO_DELETED; + basic_socket_acceptor_ext& operator=( + const basic_socket_acceptor_ext&) BOOST_ASIO_DELETED; + + struct initiate_async_wait + { + template <typename WaitHandler> + void operator()(BOOST_ASIO_MOVE_ARG(WaitHandler) handler, + basic_socket_acceptor_ext* self, wait_type w) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + BOOST_ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue<WaitHandler> handler2(handler); + self->impl_.get_service().async_wait( + self->impl_.get_implementation(), w, handler2.value, + self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_accept + { + template <typename AcceptHandler, typename Protocol1, typename Executor1> + void operator()(BOOST_ASIO_MOVE_ARG(AcceptHandler) handler, + basic_socket_acceptor_ext* self, basic_socket_ext_local<Protocol1, Executor1>* peer, + endpoint_type* peer_endpoint) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a AcceptHandler. + BOOST_ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check; + + detail::non_const_lvalue<AcceptHandler> handler2(handler); + self->impl_.get_service().async_accept( + self->impl_.get_implementation(), *peer, peer_endpoint, + handler2.value, self->impl_.get_executor()); + } + }; + + struct initiate_async_move_accept + { + template <typename MoveAcceptHandler, typename Executor1, typename Socket> + void operator()(BOOST_ASIO_MOVE_ARG(MoveAcceptHandler) handler, + basic_socket_acceptor_ext* self, const Executor1& peer_ex, + endpoint_type* peer_endpoint, Socket*) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a MoveAcceptHandler. + BOOST_ASIO_MOVE_ACCEPT_HANDLER_CHECK( + MoveAcceptHandler, handler, Socket) type_check; + + detail::non_const_lvalue<MoveAcceptHandler> handler2(handler); + self->impl_.get_service().async_move_accept( + self->impl_.get_implementation(), peer_ex, peer_endpoint, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + +#if defined(BOOST_ASIO_WINDOWS_RUNTIME) + detail::io_object_impl< + detail::null_socket_service<Protocol>, Executor> impl_; +#elif defined(BOOST_ASIO_HAS_IOCP) + detail::io_object_impl< + detail::win_iocp_socket_service<Protocol>, Executor> impl_; +#else + detail::io_object_impl< + detail::reactive_socket_service_ext_local<Protocol>, Executor> impl_; +#endif +}; + +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_BASIC_SOCKET_ACCEPTOR_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/basic_socket_ext.hpp b/implementation/helper/1.74/boost/asio/basic_socket_ext.hpp new file mode 100644 index 0000000..523f97a --- /dev/null +++ b/implementation/helper/1.74/boost/asio/basic_socket_ext.hpp @@ -0,0 +1,1859 @@ +// +// basic_socket_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2018,2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_SOCKET_EXT_HPP +#define BOOST_ASIO_BASIC_SOCKET_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/async_result.hpp> +#include <boost/asio/detail/handler_type_requirements.hpp> +#include <boost/asio/detail/io_object_impl.hpp> +#include <boost/asio/detail/non_const_lvalue.hpp> +#include <boost/asio/detail/throw_error.hpp> +#include <boost/asio/detail/type_traits.hpp> +#include <boost/asio/error.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/post.hpp> +#include <boost/asio/socket_base.hpp> + +#if defined(BOOST_ASIO_WINDOWS_RUNTIME) +# include <boost/asio/detail/null_socket_service.hpp> +#elif defined(BOOST_ASIO_HAS_IOCP) +# include <boost/asio/detail/win_iocp_socket_service.hpp> +#else +# include <boost/asio/detail/reactive_socket_service_ext.hpp> +#endif + +#if defined(BOOST_ASIO_HAS_MOVE) +# include <utility> +#endif // defined(BOOST_ASIO_HAS_MOVE) + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { + +#if !defined(BOOST_ASIO_BASIC_SOCKET_EXT_FWD_DECL) +#define BOOST_ASIO_BASIC_SOCKET_EXT_FWD_DECL + +// Forward declaration with defaulted arguments. +template <typename Protocol, typename Executor = executor> +class basic_socket_ext; + +#endif // !defined(BOOST_ASIO_BASIC_SOCKET_EXT_FWD_DECL) + +/// Provides socket functionality. +/** + * The basic_socket class template provides functionality that is common to both + * stream-oriented and datagram-oriented sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template <typename Protocol, typename Executor> +class basic_socket_ext + : public socket_base +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template <typename Executor1> + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_socket_ext<Protocol, Executor1> other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(BOOST_ASIO_WINDOWS_RUNTIME) + typedef typename detail::null_socket_service< + Protocol>::native_handle_type native_handle_type; +#elif defined(BOOST_ASIO_HAS_IOCP) + typedef typename detail::win_iocp_socket_service< + Protocol>::native_handle_type native_handle_type; +#else + typedef typename detail::reactive_socket_service_ext< + Protocol>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +#if !defined(BOOST_ASIO_NO_EXTENSIONS) + /// A basic_socket is always the lowest layer. + typedef basic_socket_ext<Protocol, Executor> lowest_layer_type; +#endif // !defined(BOOST_ASIO_NO_EXTENSIONS) + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_socket_ext(const executor_type& ex) + : impl_(ex) + { + } + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template <typename ExecutionContext> + explicit basic_socket_ext(ExecutionContext& context, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_ext(const executor_type& ex, const protocol_type& protocol) + : impl_(ex) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_ext(ExecutionContext& context, const protocol_type& protocol, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_ext(const executor_type& ex, const endpoint_type& endpoint) + : impl_(ex) + { + boost::system::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_ext(ExecutionContext& context, const endpoint_type& endpoint, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_ext(const executor_type& ex, const protocol_type& protocol, + const native_handle_type& native_socket) + : impl_(ex) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_ext(ExecutionContext& context, const protocol_type& protocol, + const native_handle_type& native_socket, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + +#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket from another. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + basic_socket_ext(basic_socket_ext&& other) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket from another. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + basic_socket_ext& operator=(basic_socket_ext&& other) + { + impl_ = std::move(other.impl_); + return *this; + } + + // All sockets have access to each other's implementations. + template <typename Protocol1, typename Executor1> + friend class basic_socket_ext; + + /// Move-construct a basic_socket from a socket of another protocol type. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + template <typename Protocol1, typename Executor1> + basic_socket_ext(basic_socket_ext<Protocol1, Executor1>&& other, + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value + >::type* = 0) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket from a socket of another protocol type. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + template <typename Protocol1, typename Executor1> + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value, + basic_socket_ext& + >::type operator=(basic_socket_ext<Protocol1, Executor1> && other) + { + basic_socket_ext tmp(std::move(other)); + impl_ = std::move(tmp.impl_); + return *this; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get the executor associated with the object. + executor_type get_executor() BOOST_ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + +#if !defined(BOOST_ASIO_NO_EXTENSIONS) + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } +#endif // !defined(BOOST_ASIO_NO_EXTENSIONS) + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * socket.open(boost::asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * boost::system::error_code ec; + * socket.open(boost::asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID open(const protocol_type& protocol, + boost::system::error_code& ec) + { + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @throws boost::system::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, + const native_handle_type& native_socket) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @param ec Set to indicate what error occurred, if any. + */ + BOOST_ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, + const native_handle_type& native_socket, boost::system::error_code& ec) + { + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open() const + { + return impl_.get_service().is_open(impl_.get_implementation()); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the boost::asio::error::operation_aborted error. + * + * @throws boost::system::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + void close() + { + boost::system::error_code ec; + impl_.get_service().close(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "close"); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the boost::asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * socket.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + BOOST_ASIO_SYNC_OP_VOID close(boost::system::error_code& ec) + { + impl_.get_service().close(impl_.get_implementation(), ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with boost::asio::error::operation_not_supported on + * these platforms. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + boost::system::error_code ec; + native_handle_type s = impl_.get_service().release( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with boost::asio::error::operation_not_supported on + * these platforms. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(boost::system::error_code& ec) + { + return impl_.get_service().release(impl_.get_implementation(), ec); + } + + /// Get the native socket representation. + /** + * This function may be used to obtain the underlying representation of the + * socket. This is intended to allow access to native socket functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return impl_.get_service().native_handle(impl_.get_implementation()); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note Calls to cancel() will always fail with + * boost::asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * BOOST_ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(BOOST_ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + void cancel() + { + boost::system::error_code ec; + impl_.get_service().cancel(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls to cancel() will always fail with + * boost::asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * BOOST_ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(BOOST_ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + BOOST_ASIO_SYNC_OP_VOID cancel(boost::system::error_code& ec) + { + impl_.get_service().cancel(impl_.get_implementation(), ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + * + * @throws boost::system::system_error Thrown on failure. + */ + bool at_mark() const + { + boost::system::error_code ec; + bool b = impl_.get_service().at_mark(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "at_mark"); + return b; + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + */ + bool at_mark(boost::system::error_code& ec) const + { + return impl_.get_service().at_mark(impl_.get_implementation(), ec); + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + * + * @throws boost::system::system_error Thrown on failure. + */ + std::size_t available() const + { + boost::system::error_code ec; + std::size_t s = impl_.get_service().available( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "available"); + return s; + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + */ + std::size_t available(boost::system::error_code& ec) const + { + return impl_.get_service().available(impl_.get_implementation(), ec); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * socket.open(boost::asio::ip::tcp::v4()); + * socket.bind(boost::asio::ip::tcp::endpoint( + * boost::asio::ip::tcp::v4(), 12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + boost::system::error_code ec; + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * socket.open(boost::asio::ip::tcp::v4()); + * boost::system::error_code ec; + * socket.bind(boost::asio::ip::tcp::endpoint( + * boost::asio::ip::tcp::v4(), 12345), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, + boost::system::error_code& ec) + { + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.connect(endpoint); + * @endcode + */ + void connect(const endpoint_type& peer_endpoint) + { + boost::system::error_code ec; + if (!is_open()) + { + impl_.get_service().open(impl_.get_implementation(), + peer_endpoint.protocol(), ec); + boost::asio::detail::throw_error(ec, "connect"); + } + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); + boost::asio::detail::throw_error(ec, "connect"); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * boost::system::error_code ec; + * socket.connect(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID connect(const endpoint_type& peer_endpoint, + boost::system::error_code& ec) + { + if (!is_open()) + { + impl_.get_service().open(impl_.get_implementation(), + peer_endpoint.protocol(), ec); + if (ec) + { + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a socket to the specified + * remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void connect_handler(const boost::system::error_code& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template <typename ConnectHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void (boost::system::error_code)) + async_connect(const endpoint_type& peer_endpoint, + BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) + { + boost::system::error_code open_ec; + if (!is_open()) + { + const protocol_type protocol = peer_endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, open_ec); + } + + return async_initiate<ConnectHandler, void (boost::system::error_code)>( + initiate_async_connect(), handler, this, peer_endpoint, open_ec); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + */ + template <typename SettableSocketOption> + void set_option(const SettableSocketOption& option) + { + boost::system::error_code ec; + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + boost::asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::no_delay option(true); + * boost::system::error_code ec; + * socket.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename SettableSocketOption> + BOOST_ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, + boost::system::error_code& ec) + { + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::keep_alive option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + */ + template <typename GettableSocketOption> + void get_option(GettableSocketOption& option) const + { + boost::system::error_code ec; + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + boost::asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::keep_alive option; + * boost::system::error_code ec; + * socket.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.value(); + * @endcode + */ + template <typename GettableSocketOption> + BOOST_ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + boost::system::error_code& ec) const + { + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template <typename IoControlCommand> + void io_control(IoControlCommand& command) + { + boost::system::error_code ec; + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + boost::asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::bytes_readable command; + * boost::system::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template <typename IoControlCommand> + BOOST_ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + boost::system::error_code& ec) + { + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + /** + * @returns @c true if the socket's synchronous operations will fail with + * boost::asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + bool non_blocking() const + { + return impl_.get_service().non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * boost::asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + void non_blocking(bool mode) + { + boost::system::error_code ec; + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + boost::asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * boost::asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + BOOST_ASIO_SYNC_OP_VOID non_blocking( + bool mode, boost::system::error_code& ec) + { + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native socket. This mode has no effect on the behaviour of the socket + * object's synchronous operations. + * + * @returns @c true if the underlying socket is in non-blocking mode and + * direct system calls may fail with boost::asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the socket object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native socket. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template <typename Handler> + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(boost::system::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = boost::system::error_code(n < 0 ? errno : 0, + * boost::asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == boost::asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == boost::asio::error::would_block + * || ec == boost::asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template <typename Handler> + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + bool native_non_blocking() const + { + return impl_.get_service().native_non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with boost::asio::error::would_block + * (or the equivalent system error). + * + * @throws boost::system::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with boost::asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template <typename Handler> + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(boost::system::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = boost::system::error_code(n < 0 ? errno : 0, + * boost::asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == boost::asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == boost::asio::error::would_block + * || ec == boost::asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template <typename Handler> + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + void native_non_blocking(bool mode) + { + boost::system::error_code ec; + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + boost::asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with boost::asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with boost::asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template <typename Handler> + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(boost::system::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = boost::system::error_code(n < 0 ? errno : 0, + * boost::asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == boost::asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == boost::asio::error::would_block + * || ec == boost::asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template <typename Handler> + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + BOOST_ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, boost::system::error_code& ec) + { + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @returns An object that represents the local endpoint of the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + boost::system::error_code ec; + endpoint_type ep = impl_.get_service().local_endpoint( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "local_endpoint"); + return ep; + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(boost::system::error_code& ec) const + { + return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @returns An object that represents the remote endpoint of the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); + * @endcode + */ + endpoint_type remote_endpoint() const + { + boost::system::error_code ec; + endpoint_type ep = impl_.get_service().remote_endpoint( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "remote_endpoint"); + return ep; + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the remote endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type remote_endpoint(boost::system::error_code& ec) const + { + return impl_.get_service().remote_endpoint(impl_.get_implementation(), ec); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + boost::system::error_code ec; + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); + boost::asio::detail::throw_error(ec, "shutdown"); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID shutdown(shutdown_type what, + boost::system::error_code& ec) + { + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * socket.wait(boost::asio::ip::tcp::socket::wait_read); + * @endcode + */ + void wait(wait_type w) + { + boost::system::error_code ec; + impl_.get_service().wait(impl_.get_implementation(), w, ec); + boost::asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * socket.wait(boost::asio::ip::tcp::socket::wait_read, ec); + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID wait(wait_type w, boost::system::error_code& ec) + { + impl_.get_service().wait(impl_.get_implementation(), w, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void wait_handler(const boost::system::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * socket.async_wait(boost::asio::ip::tcp::socket::wait_read, wait_handler); + * @endcode + */ + template <typename WaitHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (boost::system::error_code)) + async_wait(wait_type w, BOOST_ASIO_MOVE_ARG(WaitHandler) handler) + { + return async_initiate<WaitHandler, void (boost::system::error_code)>( + initiate_async_wait(), handler, this, w); + } + +protected: + /// Protected destructor to prevent deletion through this type. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_socket_ext() + { + } + +#if defined(BOOST_ASIO_WINDOWS_RUNTIME) + detail::io_object_impl< + detail::null_socket_service<Protocol>, Executor> impl_; +#elif defined(BOOST_ASIO_HAS_IOCP) + detail::io_object_impl< + detail::win_iocp_socket_service<Protocol>, Executor> impl_; +#else + detail::io_object_impl< + detail::reactive_socket_service_ext<Protocol>, Executor> impl_; +#endif + +private: + // Disallow copying and assignment. + basic_socket_ext(const basic_socket_ext&) BOOST_ASIO_DELETED; + basic_socket_ext& operator=(const basic_socket_ext&) BOOST_ASIO_DELETED; + + struct initiate_async_connect + { + template <typename ConnectHandler> + void operator()(BOOST_ASIO_MOVE_ARG(ConnectHandler) handler, + basic_socket_ext* self, const endpoint_type& peer_endpoint, + const boost::system::error_code& open_ec) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ConnectHandler. + BOOST_ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check; + + if (open_ec) + { + boost::asio::post(self->impl_.get_executor(), + boost::asio::detail::bind_handler( + BOOST_ASIO_MOVE_CAST(ConnectHandler)(handler), open_ec)); + } + else + { + detail::non_const_lvalue<ConnectHandler> handler2(handler); + self->impl_.get_service().async_connect( + self->impl_.get_implementation(), peer_endpoint, + handler2.value, self->impl_.get_implementation_executor()); + } + } + }; + + struct initiate_async_wait + { + template <typename WaitHandler> + void operator()(BOOST_ASIO_MOVE_ARG(WaitHandler) handler, + basic_socket_ext* self, wait_type w) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + BOOST_ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue<WaitHandler> handler2(handler); + self->impl_.get_service().async_wait( + self->impl_.get_implementation(), w, handler2.value, + self->impl_.get_implementation_executor()); + } + }; +}; + +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_BASIC_SOCKET_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/basic_socket_ext_local.hpp b/implementation/helper/1.74/boost/asio/basic_socket_ext_local.hpp new file mode 100644 index 0000000..855d01c --- /dev/null +++ b/implementation/helper/1.74/boost/asio/basic_socket_ext_local.hpp @@ -0,0 +1,1859 @@ +// +// basic_socket_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2018,2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_SOCKET_EXT_LOCAL_HPP +#define BOOST_ASIO_BASIC_SOCKET_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/async_result.hpp> +#include <boost/asio/detail/handler_type_requirements_ext.hpp> +#include <boost/asio/detail/io_object_impl.hpp> +#include <boost/asio/detail/non_const_lvalue.hpp> +#include <boost/asio/detail/throw_error.hpp> +#include <boost/asio/detail/type_traits.hpp> +#include <boost/asio/error.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/post.hpp> +#include <boost/asio/socket_base.hpp> + +#if defined(BOOST_ASIO_WINDOWS_RUNTIME) +# include <boost/asio/detail/null_socket_service.hpp> +#elif defined(BOOST_ASIO_HAS_IOCP) +# include <boost/asio/detail/win_iocp_socket_service.hpp> +#else +# include <boost/asio/detail/reactive_socket_service_ext_local.hpp> +#endif + +#if defined(BOOST_ASIO_HAS_MOVE) +# include <utility> +#endif // defined(BOOST_ASIO_HAS_MOVE) + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { + +#if !defined(BOOST_ASIO_BASIC_SOCKET_EXT_LOCAL_FWD_DECL) +#define BOOST_ASIO_BASIC_SOCKET_EXT_LOCAL_FWD_DECL + +// Forward declaration with defaulted arguments. +template <typename Protocol, typename Executor = executor> +class basic_socket_ext_local; + +#endif // !defined(BOOST_ASIO_BASIC_SOCKET_EXT_LOCAL_FWD_DECL) + +/// Provides socket functionality. +/** + * The basic_socket class template provides functionality that is common to both + * stream-oriented and datagram-oriented sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template <typename Protocol, typename Executor> +class basic_socket_ext_local + : public socket_base +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template <typename Executor1> + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_socket_ext_local<Protocol, Executor1> other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(BOOST_ASIO_WINDOWS_RUNTIME) + typedef typename detail::null_socket_service< + Protocol>::native_handle_type native_handle_type; +#elif defined(BOOST_ASIO_HAS_IOCP) + typedef typename detail::win_iocp_socket_service< + Protocol>::native_handle_type native_handle_type; +#else + typedef typename detail::reactive_socket_service_ext_local< + Protocol>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +#if !defined(BOOST_ASIO_NO_EXTENSIONS) + /// A basic_socket is always the lowest layer. + typedef basic_socket_ext_local<Protocol, Executor> lowest_layer_type; +#endif // !defined(BOOST_ASIO_NO_EXTENSIONS) + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_socket_ext_local(const executor_type& ex) + : impl_(ex) + { + } + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template <typename ExecutionContext> + explicit basic_socket_ext_local(ExecutionContext& context, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_ext_local(const executor_type& ex, const protocol_type& protocol) + : impl_(ex) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_ext_local(ExecutionContext& context, const protocol_type& protocol, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_ext_local(const executor_type& ex, const endpoint_type& endpoint) + : impl_(ex) + { + boost::system::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_ext_local(ExecutionContext& context, const endpoint_type& endpoint, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_socket_ext_local(const executor_type& ex, const protocol_type& protocol, + const native_handle_type& native_socket) + : impl_(ex) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_socket_ext_local(ExecutionContext& context, const protocol_type& protocol, + const native_handle_type& native_socket, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : impl_(context) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + +#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_socket from another. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + basic_socket_ext_local(basic_socket_ext_local&& other) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket from another. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + basic_socket_ext_local& operator=(basic_socket_ext_local&& other) + { + impl_ = std::move(other.impl_); + return *this; + } + + // All sockets have access to each other's implementations. + template <typename Protocol1, typename Executor1> + friend class basic_socket_ext_local; + + /// Move-construct a basic_socket from a socket of another protocol type. + /** + * This constructor moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + template <typename Protocol1, typename Executor1> + basic_socket_ext_local(basic_socket_ext_local<Protocol1, Executor1>&& other, + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value + >::type* = 0) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_socket from a socket of another protocol type. + /** + * This assignment operator moves a socket from one object to another. + * + * @param other The other basic_socket object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_socket(const executor_type&) constructor. + */ + template <typename Protocol1, typename Executor1> + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value, + basic_socket_ext_local& + >::type operator=(basic_socket_ext_local<Protocol1, Executor1> && other) + { + basic_socket_ext_local tmp(std::move(other)); + impl_ = std::move(tmp.impl_); + return *this; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Get the executor associated with the object. + executor_type get_executor() BOOST_ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + +#if !defined(BOOST_ASIO_NO_EXTENSIONS) + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Get a const reference to the lowest layer. + /** + * This function returns a const reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A const reference to the lowest layer in the stack of layers. + * Ownership is not transferred to the caller. + */ + const lowest_layer_type& lowest_layer() const + { + return *this; + } +#endif // !defined(BOOST_ASIO_NO_EXTENSIONS) + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * socket.open(boost::asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + boost::system::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * boost::system::error_code ec; + * socket.open(boost::asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID open(const protocol_type& protocol, + boost::system::error_code& ec) + { + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @throws boost::system::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, + const native_handle_type& native_socket) + { + boost::system::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + boost::asio::detail::throw_error(ec, "assign"); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @param ec Set to indicate what error occurred, if any. + */ + BOOST_ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, + const native_handle_type& native_socket, boost::system::error_code& ec) + { + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is open. + bool is_open() const + { + return impl_.get_service().is_open(impl_.get_implementation()); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the boost::asio::error::operation_aborted error. + * + * @throws boost::system::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + void close() + { + boost::system::error_code ec; + impl_.get_service().close(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "close"); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the boost::asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * socket.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + * + * @note For portable behaviour with respect to graceful closure of a + * connected socket, call shutdown() before closing the socket. + */ + BOOST_ASIO_SYNC_OP_VOID close(boost::system::error_code& ec) + { + impl_.get_service().close(impl_.get_implementation(), ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with boost::asio::error::operation_not_supported on + * these platforms. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + boost::system::error_code ec; + native_handle_type s = impl_.get_service().release( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with boost::asio::error::operation_not_supported on + * these platforms. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(boost::system::error_code& ec) + { + return impl_.get_service().release(impl_.get_implementation(), ec); + } + + /// Get the native socket representation. + /** + * This function may be used to obtain the underlying representation of the + * socket. This is intended to allow access to native socket functionality + * that is not otherwise provided. + */ + native_handle_type native_handle() + { + return impl_.get_service().native_handle(impl_.get_implementation()); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note Calls to cancel() will always fail with + * boost::asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * BOOST_ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(BOOST_ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + void cancel() + { + boost::system::error_code ec; + impl_.get_service().cancel(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "cancel"); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the boost::asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls to cancel() will always fail with + * boost::asio::error::operation_not_supported when run on Windows XP, Windows + * Server 2003, and earlier versions of Windows, unless + * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has + * two issues that should be considered before enabling its use: + * + * @li It will only cancel asynchronous operations that were initiated in the + * current thread. + * + * @li It can appear to complete without error, but the request to cancel the + * unfinished operations may be silently ignored by the operating system. + * Whether it works or not seems to depend on the drivers that are installed. + * + * For portable cancellation, consider using one of the following + * alternatives: + * + * @li Disable asio's I/O completion port backend by defining + * BOOST_ASIO_DISABLE_IOCP. + * + * @li Use the close() function to simultaneously cancel the outstanding + * operations and close the socket. + * + * When running on Windows Vista, Windows Server 2008, and later, the + * CancelIoEx function is always used. This function does not have the + * problems described above. + */ +#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ + && !defined(BOOST_ASIO_ENABLE_CANCELIO) + __declspec(deprecated("By default, this function always fails with " + "operation_not_supported when used on Windows XP, Windows Server 2003, " + "or earlier. Consult documentation for details.")) +#endif + BOOST_ASIO_SYNC_OP_VOID cancel(boost::system::error_code& ec) + { + impl_.get_service().cancel(impl_.get_implementation(), ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + * + * @throws boost::system::system_error Thrown on failure. + */ + bool at_mark() const + { + boost::system::error_code ec; + bool b = impl_.get_service().at_mark(impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "at_mark"); + return b; + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + */ + bool at_mark(boost::system::error_code& ec) const + { + return impl_.get_service().at_mark(impl_.get_implementation(), ec); + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + * + * @throws boost::system::system_error Thrown on failure. + */ + std::size_t available() const + { + boost::system::error_code ec; + std::size_t s = impl_.get_service().available( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "available"); + return s; + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + */ + std::size_t available(boost::system::error_code& ec) const + { + return impl_.get_service().available(impl_.get_implementation(), ec); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * socket.open(boost::asio::ip::tcp::v4()); + * socket.bind(boost::asio::ip::tcp::endpoint( + * boost::asio::ip::tcp::v4(), 12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + boost::system::error_code ec; + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * socket.open(boost::asio::ip::tcp::v4()); + * boost::system::error_code ec; + * socket.bind(boost::asio::ip::tcp::endpoint( + * boost::asio::ip::tcp::v4(), 12345), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, + boost::system::error_code& ec) + { + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.connect(endpoint); + * @endcode + */ + void connect(const endpoint_type& peer_endpoint) + { + boost::system::error_code ec; + if (!is_open()) + { + impl_.get_service().open(impl_.get_implementation(), + peer_endpoint.protocol(), ec); + boost::asio::detail::throw_error(ec, "connect"); + } + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); + boost::asio::detail::throw_error(ec, "connect"); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * boost::system::error_code ec; + * socket.connect(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID connect(const endpoint_type& peer_endpoint, + boost::system::error_code& ec) + { + if (!is_open()) + { + impl_.get_service().open(impl_.get_implementation(), + peer_endpoint.protocol(), ec); + if (ec) + { + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a socket to the specified + * remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * not returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void connect_handler(const boost::system::error_code& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::socket socket(my_context); + * boost::asio::ip::tcp::endpoint endpoint( + * boost::asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template <typename ConnectHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void (boost::system::error_code)) + async_connect(const endpoint_type& peer_endpoint, + BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) + { + boost::system::error_code open_ec; + if (!is_open()) + { + const protocol_type protocol = peer_endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, open_ec); + } + + return async_initiate<ConnectHandler, void (boost::system::error_code)>( + initiate_async_connect(), handler, this, peer_endpoint, open_ec); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + */ + template <typename SettableSocketOption> + void set_option(const SettableSocketOption& option) + { + boost::system::error_code ec; + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + boost::asio::detail::throw_error(ec, "set_option"); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::no_delay option(true); + * boost::system::error_code ec; + * socket.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template <typename SettableSocketOption> + BOOST_ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, + boost::system::error_code& ec) + { + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::keep_alive option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + */ + template <typename GettableSocketOption> + void get_option(GettableSocketOption& option) const + { + boost::system::error_code ec; + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + boost::asio::detail::throw_error(ec, "get_option"); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * boost::asio::socket_base::broadcast @n + * boost::asio::socket_base::do_not_route @n + * boost::asio::socket_base::keep_alive @n + * boost::asio::socket_base::linger @n + * boost::asio::socket_base::receive_buffer_size @n + * boost::asio::socket_base::receive_low_watermark @n + * boost::asio::socket_base::reuse_address @n + * boost::asio::socket_base::send_buffer_size @n + * boost::asio::socket_base::send_low_watermark @n + * boost::asio::ip::multicast::join_group @n + * boost::asio::ip::multicast::leave_group @n + * boost::asio::ip::multicast::enable_loopback @n + * boost::asio::ip::multicast::outbound_interface @n + * boost::asio::ip::multicast::hops @n + * boost::asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::keep_alive option; + * boost::system::error_code ec; + * socket.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.value(); + * @endcode + */ + template <typename GettableSocketOption> + BOOST_ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + boost::system::error_code& ec) const + { + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template <typename IoControlCommand> + void io_control(IoControlCommand& command) + { + boost::system::error_code ec; + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + boost::asio::detail::throw_error(ec, "io_control"); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * boost::asio::socket_base::bytes_readable @n + * boost::asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::socket::bytes_readable command; + * boost::system::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template <typename IoControlCommand> + BOOST_ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, + boost::system::error_code& ec) + { + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the socket. + /** + * @returns @c true if the socket's synchronous operations will fail with + * boost::asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + bool non_blocking() const + { + return impl_.get_service().non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * boost::asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + void non_blocking(bool mode) + { + boost::system::error_code ec; + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + boost::asio::detail::throw_error(ec, "non_blocking"); + } + + /// Sets the non-blocking mode of the socket. + /** + * @param mode If @c true, the socket's synchronous operations will fail with + * boost::asio::error::would_block if they are unable to perform the requested + * operation immediately. If @c false, synchronous operations will block + * until complete. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note The non-blocking mode has no effect on the behaviour of asynchronous + * operations. Asynchronous operations will never fail with the error + * boost::asio::error::would_block. + */ + BOOST_ASIO_SYNC_OP_VOID non_blocking( + bool mode, boost::system::error_code& ec) + { + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Gets the non-blocking mode of the native socket implementation. + /** + * This function is used to retrieve the non-blocking mode of the underlying + * native socket. This mode has no effect on the behaviour of the socket + * object's synchronous operations. + * + * @returns @c true if the underlying socket is in non-blocking mode and + * direct system calls may fail with boost::asio::error::would_block (or the + * equivalent system error). + * + * @note The current non-blocking mode is cached by the socket object. + * Consequently, the return value may be incorrect if the non-blocking mode + * was set directly on the native socket. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template <typename Handler> + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(boost::system::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = boost::system::error_code(n < 0 ? errno : 0, + * boost::asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == boost::asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == boost::asio::error::would_block + * || ec == boost::asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template <typename Handler> + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + bool native_non_blocking() const + { + return impl_.get_service().native_non_blocking(impl_.get_implementation()); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with boost::asio::error::would_block + * (or the equivalent system error). + * + * @throws boost::system::system_error Thrown on failure. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with boost::asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template <typename Handler> + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(boost::system::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = boost::system::error_code(n < 0 ? errno : 0, + * boost::asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == boost::asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == boost::asio::error::would_block + * || ec == boost::asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template <typename Handler> + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + void native_non_blocking(bool mode) + { + boost::system::error_code ec; + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + boost::asio::detail::throw_error(ec, "native_non_blocking"); + } + + /// Sets the non-blocking mode of the native socket implementation. + /** + * This function is used to modify the non-blocking mode of the underlying + * native socket. It has no effect on the behaviour of the socket object's + * synchronous operations. + * + * @param mode If @c true, the underlying socket is put into non-blocking + * mode and direct system calls may fail with boost::asio::error::would_block + * (or the equivalent system error). + * + * @param ec Set to indicate what error occurred, if any. If the @c mode is + * @c false, but the current value of @c non_blocking() is @c true, this + * function fails with boost::asio::error::invalid_argument, as the + * combination does not make sense. + * + * @par Example + * This function is intended to allow the encapsulation of arbitrary + * non-blocking system calls as asynchronous operations, in a way that is + * transparent to the user of the socket object. The following example + * illustrates how Linux's @c sendfile system call might be encapsulated: + * @code template <typename Handler> + * struct sendfile_op + * { + * tcp::socket& sock_; + * int fd_; + * Handler handler_; + * off_t offset_; + * std::size_t total_bytes_transferred_; + * + * // Function call operator meeting WriteHandler requirements. + * // Used as the handler for the async_write_some operation. + * void operator()(boost::system::error_code ec, std::size_t) + * { + * // Put the underlying socket into non-blocking mode. + * if (!ec) + * if (!sock_.native_non_blocking()) + * sock_.native_non_blocking(true, ec); + * + * if (!ec) + * { + * for (;;) + * { + * // Try the system call. + * errno = 0; + * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); + * ec = boost::system::error_code(n < 0 ? errno : 0, + * boost::asio::error::get_system_category()); + * total_bytes_transferred_ += ec ? 0 : n; + * + * // Retry operation immediately if interrupted by signal. + * if (ec == boost::asio::error::interrupted) + * continue; + * + * // Check if we need to run the operation again. + * if (ec == boost::asio::error::would_block + * || ec == boost::asio::error::try_again) + * { + * // We have to wait for the socket to become ready again. + * sock_.async_wait(tcp::socket::wait_write, *this); + * return; + * } + * + * if (ec || n == 0) + * { + * // An error occurred, or we have reached the end of the file. + * // Either way we must exit the loop so we can call the handler. + * break; + * } + * + * // Loop around to try calling sendfile again. + * } + * } + * + * // Pass result back to user's handler. + * handler_(ec, total_bytes_transferred_); + * } + * }; + * + * template <typename Handler> + * void async_sendfile(tcp::socket& sock, int fd, Handler h) + * { + * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; + * sock.async_wait(tcp::socket::wait_write, op); + * } @endcode + */ + BOOST_ASIO_SYNC_OP_VOID native_non_blocking( + bool mode, boost::system::error_code& ec) + { + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @returns An object that represents the local endpoint of the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + boost::system::error_code ec; + endpoint_type ep = impl_.get_service().local_endpoint( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "local_endpoint"); + return ep; + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(boost::system::error_code& ec) const + { + return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @returns An object that represents the remote endpoint of the socket. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); + * @endcode + */ + endpoint_type remote_endpoint() const + { + boost::system::error_code ec; + endpoint_type ep = impl_.get_service().remote_endpoint( + impl_.get_implementation(), ec); + boost::asio::detail::throw_error(ec, "remote_endpoint"); + return ep; + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the remote endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type remote_endpoint(boost::system::error_code& ec) const + { + return impl_.get_service().remote_endpoint(impl_.get_implementation(), ec); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws boost::system::system_error Thrown on failure. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + boost::system::error_code ec; + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); + boost::asio::detail::throw_error(ec, "shutdown"); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID shutdown(shutdown_type what, + boost::system::error_code& ec) + { + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * socket.wait(boost::asio::ip::tcp::socket::wait_read); + * @endcode + */ + void wait(wait_type w) + { + boost::system::error_code ec; + impl_.get_service().wait(impl_.get_implementation(), w, ec); + boost::asio::detail::throw_error(ec, "wait"); + } + + /// Wait for the socket to become ready to read, ready to write, or to have + /// pending error conditions. + /** + * This function is used to perform a blocking wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Waiting for a socket to become readable. + * @code + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * boost::system::error_code ec; + * socket.wait(boost::asio::ip::tcp::socket::wait_read, ec); + * @endcode + */ + BOOST_ASIO_SYNC_OP_VOID wait(wait_type w, boost::system::error_code& ec) + { + impl_.get_service().wait(impl_.get_implementation(), w, ec); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Asynchronously wait for the socket to become ready to read, ready to + /// write, or to have pending error conditions. + /** + * This function is used to perform an asynchronous wait for a socket to enter + * a ready to read, write or error condition state. + * + * @param w Specifies the desired socket state. + * + * @param handler The handler to be called when the wait operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Example + * @code + * void wait_handler(const boost::system::error_code& error) + * { + * if (!error) + * { + * // Wait succeeded. + * } + * } + * + * ... + * + * boost::asio::ip::tcp::socket socket(my_context); + * ... + * socket.async_wait(boost::asio::ip::tcp::socket::wait_read, wait_handler); + * @endcode + */ + template <typename WaitHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WaitHandler, + void (boost::system::error_code)) + async_wait(wait_type w, BOOST_ASIO_MOVE_ARG(WaitHandler) handler) + { + return async_initiate<WaitHandler, void (boost::system::error_code)>( + initiate_async_wait(), handler, this, w); + } + +protected: + /// Protected destructor to prevent deletion through this type. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_socket_ext_local() + { + } + +#if defined(BOOST_ASIO_WINDOWS_RUNTIME) + detail::io_object_impl< + detail::null_socket_service<Protocol>, Executor> impl_; +#elif defined(BOOST_ASIO_HAS_IOCP) + detail::io_object_impl< + detail::win_iocp_socket_service<Protocol>, Executor> impl_; +#else + detail::io_object_impl< + detail::reactive_socket_service_ext_local<Protocol>, Executor> impl_; +#endif + +private: + // Disallow copying and assignment. + basic_socket_ext_local(const basic_socket_ext_local&) BOOST_ASIO_DELETED; + basic_socket_ext_local& operator=(const basic_socket_ext_local&) BOOST_ASIO_DELETED; + + struct initiate_async_connect + { + template <typename ConnectHandler> + void operator()(BOOST_ASIO_MOVE_ARG(ConnectHandler) handler, + basic_socket_ext_local* self, const endpoint_type& peer_endpoint, + const boost::system::error_code& open_ec) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ConnectHandler. + BOOST_ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check; + + if (open_ec) + { + boost::asio::post(self->impl_.get_executor(), + boost::asio::detail::bind_handler( + BOOST_ASIO_MOVE_CAST(ConnectHandler)(handler), open_ec)); + } + else + { + detail::non_const_lvalue<ConnectHandler> handler2(handler); + self->impl_.get_service().async_connect( + self->impl_.get_implementation(), peer_endpoint, + handler2.value, self->impl_.get_implementation_executor()); + } + } + }; + + struct initiate_async_wait + { + template <typename WaitHandler> + void operator()(BOOST_ASIO_MOVE_ARG(WaitHandler) handler, + basic_socket_ext_local* self, wait_type w) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + BOOST_ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue<WaitHandler> handler2(handler); + self->impl_.get_service().async_wait( + self->impl_.get_implementation(), w, handler2.value, + self->impl_.get_implementation_executor()); + } + }; +}; + +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_BASIC_SOCKET_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/basic_stream_socket_ext.hpp b/implementation/helper/1.74/boost/asio/basic_stream_socket_ext.hpp new file mode 100644 index 0000000..e77f5eb --- /dev/null +++ b/implementation/helper/1.74/boost/asio/basic_stream_socket_ext.hpp @@ -0,0 +1,996 @@ +// +// basic_stream_socket_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_BASIC_STREAM_SOCKET_EXT_HPP +#define BOOST_ASIO_BASIC_STREAM_SOCKET_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <cstddef> +#include <boost/asio/async_result.hpp> +#include <boost/asio/basic_socket_ext_local.hpp> +#include <boost/asio/detail/handler_type_requirements_ext_local.hpp> +#include <boost/asio/detail/non_const_lvalue.hpp> +#include <boost/asio/detail/throw_error.hpp> +#include <boost/asio/error.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { + +#if !defined(BOOST_ASIO_BASIC_STREAM_SOCKET_EXT_FWD_DECL) +#define BOOST_ASIO_BASIC_STREAM_SOCKET_EXT_FWD_DECL + +// Forward declaration with defaulted arguments. +template <typename Protocol, typename Executor = executor> +class basic_stream_socket_ext; + +#endif // !defined(BOOST_ASIO_BASIC_STREAM_SOCKET_EXT_FWD_DECL) + +/// Provides stream-oriented socket functionality. +/** + * The basic_stream_socket_ext class template provides asynchronous and blocking + * stream-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template <typename Protocol, typename Executor> +class basic_stream_socket_ext + : public basic_socket_ext_local<Protocol, Executor> +{ +public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template <typename Executor1> + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_stream_socket_ext<Protocol, Executor1> other; + }; + + /// The native representation of a socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket<Protocol, + Executor>::native_handle_type native_handle_type; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without opening it. The socket + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_stream_socket_ext(const executor_type& ex) + : basic_socket_ext_local<Protocol, Executor>(ex) + { + } + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without opening it. The socket + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template <typename ExecutionContext> + explicit basic_stream_socket_ext(ExecutionContext& context, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext_local<Protocol, Executor>(context) + { + } + + /// Construct and open a basic_stream_socket. + /** + * This constructor creates and opens a stream socket. The socket needs to be + * connected or accepted before data can be sent or received on it. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_stream_socket_ext(const executor_type& ex, const protocol_type& protocol) + : basic_socket_ext_local<Protocol, Executor>(ex, protocol) + { + } + + /// Construct and open a basic_stream_socket. + /** + * This constructor creates and opens a stream socket. The socket needs to be + * connected or accepted before data can be sent or received on it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_stream_socket_ext(ExecutionContext& context, const protocol_type& protocol, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext_local<Protocol, Executor>(context, protocol) + { + } + + /// Construct a basic_stream_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a stream socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the stream + * socket will be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_stream_socket_ext(const executor_type& ex, const endpoint_type& endpoint) + : basic_socket_ext_local<Protocol, Executor>(ex, endpoint) + { + } + + /// Construct a basic_stream_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a stream socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the stream + * socket will be bound. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_stream_socket_ext(ExecutionContext& context, const endpoint_type& endpoint, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext_local<Protocol, Executor>(context, endpoint) + { + } + + /// Construct a basic_stream_socket on an existing native socket. + /** + * This constructor creates a stream socket object to hold an existing native + * socket. + * + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws boost::system::system_error Thrown on failure. + */ + basic_stream_socket_ext(const executor_type& ex, + const protocol_type& protocol, const native_handle_type& native_socket) + : basic_socket_ext_local<Protocol, Executor>(ex, protocol, native_socket) + { + } + + /// Construct a basic_stream_socket on an existing native socket. + /** + * This constructor creates a stream socket object to hold an existing native + * socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws boost::system::system_error Thrown on failure. + */ + template <typename ExecutionContext> + basic_stream_socket_ext(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename enable_if< + is_convertible<ExecutionContext&, execution_context&>::value + >::type* = 0) + : basic_socket_ext_local<Protocol, Executor>(context, protocol, native_socket) + { + } + +#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_stream_socket from another. + /** + * This constructor moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + basic_stream_socket_ext(basic_stream_socket_ext&& other) + : basic_socket_ext_local<Protocol, Executor>(std::move(other)) + { + } + + /// Move-assign a basic_stream_socket from another. + /** + * This assignment operator moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + basic_stream_socket_ext& operator=(basic_stream_socket_ext&& other) + { + basic_socket_ext_local<Protocol, Executor>::operator=(std::move(other)); + return *this; + } + + /// Move-construct a basic_stream_socket from a socket of another protocol + /// type. + /** + * This constructor moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + template <typename Protocol1, typename Executor1> + basic_stream_socket_ext(basic_stream_socket_ext<Protocol1, Executor1>&& other, + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value + >::type* = 0) + : basic_socket_ext_local<Protocol, Executor>(std::move(other)) + { + } + + /// Move-assign a basic_stream_socket from a socket of another protocol type. + /** + * This assignment operator moves a stream socket from one object to another. + * + * @param other The other basic_stream_socket object from which the move + * will occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_stream_socket(const executor_type&) + * constructor. + */ + template <typename Protocol1, typename Executor1> + typename enable_if< + is_convertible<Protocol1, Protocol>::value + && is_convertible<Executor1, Executor>::value, + basic_stream_socket_ext& + >::type operator=(basic_stream_socket_ext<Protocol1, Executor1>&& other) + { + basic_socket_ext_local<Protocol, Executor>::operator=(std::move(other)); + return *this; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_stream_socket_ext() + { + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence> + std::size_t send(const ConstBufferSequence& buffers) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + boost::asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws boost::system::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(boost::asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence> + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + boost::asio::detail::throw_error(ec, "send"); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. Returns 0 if an error occurred. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template <typename ConstBufferSequence> + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return async_initiate<WriteHandler, + void (boost::system::error_code, std::size_t)>( + initiate_async_send(), handler, this, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(boost::asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return async_initiate<WriteHandler, + void (boost::system::error_code, std::size_t)>( + initiate_async_send(), handler, this, buffers, flags); + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws boost::system::system_error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence> + std::size_t receive(const MutableBufferSequence& buffers) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + boost::asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws boost::system::system_error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(boost::asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence> + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + boost::asio::detail::throw_error(ec, "receive"); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. Returns 0 if an error occurred. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template <typename MutableBufferSequence> + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)) + async_receive(const MutableBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return async_initiate<ReadHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)>( + initiate_async_receive(), handler, this, + buffers, socket_base::message_flags(0)); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(boost::asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)) + async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return async_initiate<ReadHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)>( + initiate_async_receive(), handler, this, buffers, flags); + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @returns The number of bytes written. + * + * @throws boost::system::system_error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.write_some(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence> + std::size_t write_some(const ConstBufferSequence& buffers) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + boost::asio::detail::throw_error(ec, "write_some"); + return s; + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template <typename ConstBufferSequence> + std::size_t write_some(const ConstBufferSequence& buffers, + boost::system::error_code& ec) + { + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the socket. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_write_some(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)) + async_write_some(const ConstBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return async_initiate<WriteHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)>( + initiate_async_send(), handler, this, + buffers, socket_base::message_flags(0)); + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws boost::system::system_error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.read_some(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence> + std::size_t read_some(const MutableBufferSequence& buffers) + { + boost::system::error_code ec; + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + boost::asio::detail::throw_error(ec, "read_some"); + return s; + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template <typename MutableBufferSequence> + std::size_t read_some(const MutableBufferSequence& buffers, + boost::system::error_code& ec) + { + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_read_some(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)) + async_read_some(const MutableBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return async_initiate<ReadHandler, + void (boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t)>( + initiate_async_receive(), handler, this, + buffers, socket_base::message_flags(0)); + } + +private: + struct initiate_async_send + { + template <typename WriteHandler, typename ConstBufferSequence> + void operator()(BOOST_ASIO_MOVE_ARG(WriteHandler) handler, + basic_stream_socket_ext* self, const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue<WriteHandler> handler2(handler); + self->impl_.get_service().async_send( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_executor()); + } + }; + + struct initiate_async_receive + { + template <typename ReadHandler, typename MutableBufferSequence> + void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler) handler, + basic_stream_socket_ext* self, const MutableBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + BOOST_ASIO_READ_HANDLER_CHECK_EXT_LOCAL(ReadHandler, handler) type_check; + + detail::non_const_lvalue<ReadHandler> handler2(handler); + self->impl_.get_service().async_receive( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_executor()); + } + }; +}; + +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_BASIC_STREAM_SOCKET_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/handler_type_requirements_ext.hpp b/implementation/helper/1.74/boost/asio/detail/handler_type_requirements_ext.hpp new file mode 100644 index 0000000..67fe6bd --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/handler_type_requirements_ext.hpp @@ -0,0 +1,586 @@ +// +// detail/handler_type_requirements_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_EXT_HPP +#define BOOST_ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +// Older versions of gcc have difficulty compiling the sizeof expressions where +// we test the handler type requirements. We'll disable checking of handler type +// requirements for those compilers, but otherwise enable it by default. +#if !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) +# if !defined(__GNUC__) || (__GNUC__ >= 4) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS 1 +# endif // !defined(__GNUC__) || (__GNUC__ >= 4) +#endif // !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +// With C++0x we can use a combination of enhanced SFINAE and static_assert to +// generate better template error messages. As this technique is not yet widely +// portable, we'll only enable it for tested compilers. +#if !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(BOOST_ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(BOOST_ASIO_MSVC) +# if defined(__clang__) +# if __has_feature(__cxx_static_assert__) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // __has_feature(cxx_static_assert) +# endif // defined(__clang__) +#endif // !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +#if defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) +# include <boost/asio/async_result.hpp> +#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +# if defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template <typename Handler> +auto zero_arg_copyable_handler_test(Handler h, void*) + -> decltype( + sizeof(Handler(static_cast<const Handler&>(h))), + ((h)()), + char(0)); + +template <typename Handler> +char (&zero_arg_copyable_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1> +auto one_arg_handler_test(Handler h, Arg1* a1) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1)), + char(0)); + +template <typename Handler> +char (&one_arg_handler_test(Handler h, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2> +auto two_arg_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, *a2)), + char(0)); + +template <typename Handler> +char (&two_arg_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2> +auto two_arg_move_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, BOOST_ASIO_MOVE_CAST(Arg2)(*a2))), + char(0)); + +template <typename Handler> +char (&two_arg_move_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3> +auto three_arg_handler_test(Handler h, Arg1* a1, Arg2* a2, Arg3* a3) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, *a2, *a3)), + char(0)); + +template <typename Handler> +char (&three_arg_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3> +auto three_arg_move_handler_test(Handler h, Arg1* a1, Arg2* a2, Arg3 *a3) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, BOOST_ASIO_MOVE_CAST(Arg2)(*a2), BOOST_ASIO_MOVE_CAST(Arg3)(*a3))), + char(0)); + +template <typename Handler> +char (&three_arg_move_handler_test(Handler, ...))[2]; + +# define BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) \ + static_assert(expr, msg); + +# else // defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +# define BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) + +# endif // defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template <typename T> T& lvref(); +template <typename T> T& lvref(T); +template <typename T> const T& clvref(); +template <typename T> const T& clvref(T); +#if defined(BOOST_ASIO_HAS_MOVE) +template <typename T> T rvref(); +template <typename T> T rvref(T); +#else // defined(BOOST_ASIO_HAS_MOVE) +template <typename T> const T& rvref(); +template <typename T> const T& rvref(T); +#endif // defined(BOOST_ASIO_HAS_MOVE) +template <typename T> char argbyv(T); + +#if 0 +template <int> +struct handler_type_requirements +{ +}; +#endif + +#define BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void()) asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::zero_arg_copyable_handler_test( \ + boost::asio::detail::clvref< \ + asio_true_handler_type>(), 0)) == 1, \ + "CompletionHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::clvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()(), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_READ_HANDLER_CHECK_EXT( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, std::size_t, \ + boost::asio::ip::address)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::three_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const std::size_t*>(0), \ + static_cast<const boost::asio::ip::address*>(0))) == 1, \ + "ReadHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const std::size_t>(), \ + boost::asio::detail::lvref<const boost::asio::ip::address>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const std::size_t*>(0))) == 1, \ + "WriteHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const std::size_t>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#if 0 +#define BOOST_ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "AcceptHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, socket_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_move_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<socket_type*>(0))) == 1, \ + "MoveAcceptHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::rvref<socket_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF +#endif + +#define BOOST_ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "ConnectHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, endpoint_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, endpoint_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const endpoint_type*>(0))) == 1, \ + "RangeConnectHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const endpoint_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, iter_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const iter_type*>(0))) == 1, \ + "IteratorConnectHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const iter_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, range_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, range_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const range_type*>(0))) == 1, \ + "ResolveHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const range_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "WaitHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, int)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const int*>(0))) == 1, \ + "SignalHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const int>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "HandshakeHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const std::size_t*>(0))) == 1, \ + "BufferedHandshakeHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const std::size_t>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "ShutdownHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#else // !defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +#define BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_READ_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#endif // !defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // BOOST_ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/handler_type_requirements_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/handler_type_requirements_ext_local.hpp new file mode 100644 index 0000000..65640ad --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/handler_type_requirements_ext_local.hpp @@ -0,0 +1,588 @@ +// +// detail/handler_type_requirements_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +// Older versions of gcc have difficulty compiling the sizeof expressions where +// we test the handler type requirements. We'll disable checking of handler type +// requirements for those compilers, but otherwise enable it by default. +#if !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) +# if !defined(__GNUC__) || (__GNUC__ >= 4) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS 1 +# endif // !defined(__GNUC__) || (__GNUC__ >= 4) +#endif // !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +// With C++0x we can use a combination of enhanced SFINAE and static_assert to +// generate better template error messages. As this technique is not yet widely +// portable, we'll only enable it for tested compilers. +#if !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) +# if defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(BOOST_ASIO_MSVC) +# if (_MSC_VER >= 1600) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // (_MSC_VER >= 1600) +# endif // defined(BOOST_ASIO_MSVC) +# if defined(__clang__) +# if __has_feature(__cxx_static_assert__) +# define BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT 1 +# endif // __has_feature(cxx_static_assert) +# endif // defined(__clang__) +#endif // !defined(BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS) + +#if defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) +# include <boost/asio/async_result.hpp> +#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +namespace boost { +namespace asio { +namespace detail { + +#if defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +# if defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template <typename Handler> +auto zero_arg_copyable_handler_test(Handler h, void*) + -> decltype( + sizeof(Handler(static_cast<const Handler&>(h))), + ((h)()), + char(0)); + +template <typename Handler> +char (&zero_arg_copyable_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1> +auto one_arg_handler_test(Handler h, Arg1* a1) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1)), + char(0)); + +template <typename Handler> +char (&one_arg_handler_test(Handler h, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2> +auto two_arg_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, *a2)), + char(0)); + +template <typename Handler> +char (&two_arg_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2> +auto two_arg_move_handler_test(Handler h, Arg1* a1, Arg2* a2) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, BOOST_ASIO_MOVE_CAST(Arg2)(*a2))), + char(0)); + +template <typename Handler> +char (&two_arg_move_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +auto four_arg_handler_test(Handler h, Arg1* a1, Arg2* a2, Arg3* a3, Arg4* a4) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, *a2, *a3, *a4)), + char(0)); + +template <typename Handler> +char (&four_arg_handler_test(Handler, ...))[2]; + +template <typename Handler, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +auto four_arg_move_handler_test(Handler h, Arg1* a1, Arg2* a2, Arg3* a3, Arg4* a4) + -> decltype( + sizeof(Handler(BOOST_ASIO_MOVE_CAST(Handler)(h))), + ((h)(*a1, BOOST_ASIO_MOVE_CAST(Arg2)(*a2), BOOST_ASIO_MOVE_CAST(Arg3)(*a3), BOOST_ASIO_MOVE_CAST(Arg4)(*a4))), + char(0)); + +template <typename Handler> +char (&four_arg_move_handler_test(Handler, ...))[2]; + +# define BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) \ + static_assert(expr, msg); + +# else // defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +# define BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) + +# endif // defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS_ASSERT) + +template <typename T> T& lvref(); +template <typename T> T& lvref(T); +template <typename T> const T& clvref(); +template <typename T> const T& clvref(T); +#if defined(BOOST_ASIO_HAS_MOVE) +template <typename T> T rvref(); +template <typename T> T rvref(T); +#else // defined(BOOST_ASIO_HAS_MOVE) +template <typename T> const T& rvref(); +template <typename T> const T& rvref(T); +#endif // defined(BOOST_ASIO_HAS_MOVE) +template <typename T> char argbyv(T); + +#if 0 +template <int> +struct handler_type_requirements +{ +}; +#endif + +#define BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void()) asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::zero_arg_copyable_handler_test( \ + boost::asio::detail::clvref< \ + asio_true_handler_type>(), 0)) == 1, \ + "CompletionHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::clvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()(), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_READ_HANDLER_CHECK_EXT_LOCAL( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, std::size_t, \ + std::uint32_t, std::uint32_t)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::four_arg_handler_test( \ + boost::asio::detail::clvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const std::size_t*>(0), \ + static_cast<const std::uint32_t*>(0), \ + static_cast<const std::uint32_t*>(0))) == 1, \ + "ReadHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::clvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const std::size_t>(), \ + boost::asio::detail::lvref<const std::uint32_t>(), \ + boost::asio::detail::lvref<const std::uint32_t>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const std::size_t*>(0))) == 1, \ + "WriteHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const std::size_t>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#if 0 +#define BOOST_ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "AcceptHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, socket_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_move_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<socket_type*>(0))) == 1, \ + "MoveAcceptHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::rvref<socket_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF +#endif + +#define BOOST_ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "ConnectHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, endpoint_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, endpoint_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const endpoint_type*>(0))) == 1, \ + "RangeConnectHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const endpoint_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, iter_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const iter_type*>(0))) == 1, \ + "IteratorConnectHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const iter_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, range_type) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, range_type)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const range_type*>(0))) == 1, \ + "ResolveHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const range_type>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "WaitHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, int)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const int*>(0))) == 1, \ + "SignalHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const int>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "HandshakeHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code, std::size_t)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::two_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0), \ + static_cast<const std::size_t*>(0))) == 1, \ + "BufferedHandshakeHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>(), \ + boost::asio::detail::lvref<const std::size_t>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + \ + typedef BOOST_ASIO_HANDLER_TYPE(handler_type, \ + void(boost::system::error_code)) \ + asio_true_handler_type; \ + \ + BOOST_ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \ + sizeof(boost::asio::detail::one_arg_handler_test( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>(), \ + static_cast<const boost::system::error_code*>(0))) == 1, \ + "ShutdownHandler type requirements not met") \ + \ + typedef boost::asio::detail::handler_type_requirements< \ + sizeof( \ + boost::asio::detail::argbyv( \ + boost::asio::detail::rvref< \ + asio_true_handler_type>())) + \ + sizeof( \ + boost::asio::detail::lvref< \ + asio_true_handler_type>()( \ + boost::asio::detail::lvref<const boost::system::error_code>()), \ + char(0))> BOOST_ASIO_UNUSED_TYPEDEF + +#else // !defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +#define BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_READ_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WRITE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_ACCEPT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_MOVE_ACCEPT_HANDLER_CHECK( \ + handler_type, handler, socket_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_CONNECT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RANGE_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_ITERATOR_CONNECT_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_RESOLVE_HANDLER_CHECK( \ + handler_type, handler, iter_type) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_WAIT_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SIGNAL_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_BUFFERED_HANDSHAKE_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#define BOOST_ASIO_SHUTDOWN_HANDLER_CHECK( \ + handler_type, handler) \ + typedef int BOOST_ASIO_UNUSED_TYPEDEF + +#endif // !defined(BOOST_ASIO_ENABLE_HANDLER_TYPE_REQUIREMENTS) + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // BOOST_ASIO_DETAIL_HANDLER_TYPE_REQUIREMENTS_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/impl/reactive_socket_service_base_ext.ipp b/implementation/helper/1.74/boost/asio/detail/impl/reactive_socket_service_base_ext.ipp new file mode 100644 index 0000000..04036ad --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/impl/reactive_socket_service_base_ext.ipp @@ -0,0 +1,302 @@ +// +// detail/reactive_socket_service_base_ext.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_EXT_IPP +#define BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_EXT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#if !defined(BOOST_ASIO_HAS_IOCP) \ + && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#include <boost/asio/detail/reactive_socket_service_base_ext.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +reactive_socket_service_base_ext::reactive_socket_service_base_ext( + execution_context& context) + : reactor_(use_service<reactor>(context)) +{ + reactor_.init_task(); +} + +void reactive_socket_service_base_ext::base_shutdown() +{ +} + +void reactive_socket_service_base_ext::construct( + reactive_socket_service_base_ext::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; +} + +void reactive_socket_service_base_ext::base_move_construct( + reactive_socket_service_base_ext::base_implementation_type& impl, + reactive_socket_service_base_ext::base_implementation_type& other_impl) +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base_ext::base_move_assign( + reactive_socket_service_base_ext::base_implementation_type& impl, + reactive_socket_service_base_ext& other_service, + reactive_socket_service_base_ext::base_implementation_type& other_impl) +{ + destroy(impl); + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + other_service.reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base_ext::destroy( + reactive_socket_service_base_ext::base_implementation_type& impl) +{ + if (impl.socket_ != invalid_socket) + { + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + boost::system::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } +} + +boost::system::error_code reactive_socket_service_base_ext::close( + reactive_socket_service_base_ext::base_implementation_type& impl, + boost::system::error_code& ec) +{ + if (is_open(impl)) + { + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + socket_ops::close(impl.socket_, impl.state_, false, ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = boost::system::error_code(); + } + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour. The + // known exception is when Windows's closesocket() function fails with + // WSAEWOULDBLOCK, but this case is handled inside socket_ops::close(). + construct(impl); + + return ec; +} +/* +socket_type reactive_socket_service_base::release( + reactive_socket_service_base::base_implementation_type& impl, + boost::system::error_code& ec) +{ + if (!is_open(impl)) + { + ec = boost::asio::error::bad_descriptor; + return invalid_socket; + } + + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "release")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, false); + reactor_.cleanup_descriptor_data(impl.reactor_data_); + socket_type sock = impl.socket_; + construct(impl); + ec = boost::system::error_code(); + return sock; +} +*/ +boost::system::error_code reactive_socket_service_base_ext::cancel( + reactive_socket_service_base_ext::base_implementation_type& impl, + boost::system::error_code& ec) +{ + if (!is_open(impl)) + { + ec = boost::asio::error::bad_descriptor; + return ec; + } + + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "cancel")); + + reactor_.cancel_ops(impl.socket_, impl.reactor_data_); + ec = boost::system::error_code(); + return ec; +} + +boost::system::error_code reactive_socket_service_base_ext::do_open( + reactive_socket_service_base_ext::base_implementation_type& impl, + int af, int type, int protocol, boost::system::error_code& ec) +{ + if (is_open(impl)) + { + ec = boost::asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(af, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) + { + ec = boost::system::error_code(err, + boost::asio::error::get_system_category()); + return ec; + } + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + ec = boost::system::error_code(); + return ec; +} + +boost::system::error_code reactive_socket_service_base_ext::do_assign( + reactive_socket_service_base_ext::base_implementation_type& impl, int type, + const reactive_socket_service_base_ext::native_handle_type& native_socket, + boost::system::error_code& ec) +{ + if (is_open(impl)) + { + ec = boost::asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_socket, impl.reactor_data_)) + { + ec = boost::system::error_code(err, + boost::asio::error::get_system_category()); + return ec; + } + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.state_ |= socket_ops::possible_dup; + ec = boost::system::error_code(); + return ec; +} + +void reactive_socket_service_base_ext::start_op( + reactive_socket_service_base_ext::base_implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool is_non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + reactor_.start_op(op_type, impl.socket_, + impl.reactor_data_, op, is_continuation, is_non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +void reactive_socket_service_base_ext::start_accept_op( + reactive_socket_service_base_ext::base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open) +{ + if (!peer_is_open) + start_op(impl, reactor::read_op, op, is_continuation, true, false); + else + { + op->ec_ = boost::asio::error::already_open; + reactor_.post_immediate_completion(op, is_continuation); + } +} + +void reactive_socket_service_base_ext::start_connect_op( + reactive_socket_service_base_ext::base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen) +{ + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == boost::asio::error::in_progress + || op->ec_ == boost::asio::error::would_block) + { + op->ec_ = boost::system::error_code(); + reactor_.start_op(reactor::connect_op, impl.socket_, + impl.reactor_data_, op, is_continuation, false); + return; + } + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // !defined(BOOST_ASIO_HAS_IOCP) + // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#endif // BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_EXT_IPP diff --git a/implementation/helper/1.74/boost/asio/detail/impl/reactive_socket_service_base_ext_local.ipp b/implementation/helper/1.74/boost/asio/detail/impl/reactive_socket_service_base_ext_local.ipp new file mode 100644 index 0000000..288cf19 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/impl/reactive_socket_service_base_ext_local.ipp @@ -0,0 +1,302 @@ +// +// detail/reactive_socket_service_base_ext_local.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_EXT_LOCAL_IPP +#define BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_EXT_LOCAL_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#if !defined(BOOST_ASIO_HAS_IOCP) \ + && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#include <boost/asio/detail/reactive_socket_service_base_ext_local.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +reactive_socket_service_base_ext_local::reactive_socket_service_base_ext_local( + execution_context& context) + : reactor_(use_service<reactor>(context)) +{ + reactor_.init_task(); +} + +void reactive_socket_service_base_ext_local::base_shutdown() +{ +} + +void reactive_socket_service_base_ext_local::construct( + reactive_socket_service_base_ext_local::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; +} + +void reactive_socket_service_base_ext_local::base_move_construct( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + reactive_socket_service_base_ext_local::base_implementation_type& other_impl) +{ + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base_ext_local::base_move_assign( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + reactive_socket_service_base_ext_local& other_service, + reactive_socket_service_base_ext_local::base_implementation_type& other_impl) +{ + destroy(impl); + + impl.socket_ = other_impl.socket_; + other_impl.socket_ = invalid_socket; + + impl.state_ = other_impl.state_; + other_impl.state_ = 0; + + other_service.reactor_.move_descriptor(impl.socket_, + impl.reactor_data_, other_impl.reactor_data_); +} + +void reactive_socket_service_base_ext_local::destroy( + reactive_socket_service_base_ext_local::base_implementation_type& impl) +{ + if (impl.socket_ != invalid_socket) + { + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + boost::system::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } +} + +boost::system::error_code reactive_socket_service_base_ext_local::close( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + boost::system::error_code& ec) +{ + if (is_open(impl)) + { + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "close")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, + (impl.state_ & socket_ops::possible_dup) == 0); + + socket_ops::close(impl.socket_, impl.state_, false, ec); + + reactor_.cleanup_descriptor_data(impl.reactor_data_); + } + else + { + ec = boost::system::error_code(); + } + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour. The + // known exception is when Windows's closesocket() function fails with + // WSAEWOULDBLOCK, but this case is handled inside socket_ops::close(). + construct(impl); + + return ec; +} +/* +socket_type reactive_socket_service_base::release( + reactive_socket_service_base::base_implementation_type& impl, + boost::system::error_code& ec) +{ + if (!is_open(impl)) + { + ec = boost::asio::error::bad_descriptor; + return invalid_socket; + } + + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "release")); + + reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, false); + reactor_.cleanup_descriptor_data(impl.reactor_data_); + socket_type sock = impl.socket_; + construct(impl); + ec = boost::system::error_code(); + return sock; +} +*/ +boost::system::error_code reactive_socket_service_base_ext_local::cancel( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + boost::system::error_code& ec) +{ + if (!is_open(impl)) + { + ec = boost::asio::error::bad_descriptor; + return ec; + } + + BOOST_ASIO_HANDLER_OPERATION((reactor_.context(), + "socket", &impl, impl.socket_, "cancel")); + + reactor_.cancel_ops(impl.socket_, impl.reactor_data_); + ec = boost::system::error_code(); + return ec; +} + +boost::system::error_code reactive_socket_service_base_ext_local::do_open( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + int af, int type, int protocol, boost::system::error_code& ec) +{ + if (is_open(impl)) + { + ec = boost::asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(af, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) + { + ec = boost::system::error_code(err, + boost::asio::error::get_system_category()); + return ec; + } + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + ec = boost::system::error_code(); + return ec; +} + +boost::system::error_code reactive_socket_service_base_ext_local::do_assign( + reactive_socket_service_base_ext_local::base_implementation_type& impl, int type, + const reactive_socket_service_base_ext_local::native_handle_type& native_socket, + boost::system::error_code& ec) +{ + if (is_open(impl)) + { + ec = boost::asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_socket, impl.reactor_data_)) + { + ec = boost::system::error_code(err, + boost::asio::error::get_system_category()); + return ec; + } + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.state_ |= socket_ops::possible_dup; + ec = boost::system::error_code(); + return ec; +} + +void reactive_socket_service_base_ext_local::start_op( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool is_non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + reactor_.start_op(op_type, impl.socket_, + impl.reactor_data_, op, is_continuation, is_non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +void reactive_socket_service_base_ext_local::start_accept_op( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open) +{ + if (!peer_is_open) + start_op(impl, reactor::read_op, op, is_continuation, true, false); + else + { + op->ec_ = boost::asio::error::already_open; + reactor_.post_immediate_completion(op, is_continuation); + } +} + +void reactive_socket_service_base_ext_local::start_connect_op( + reactive_socket_service_base_ext_local::base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen) +{ + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, true, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == boost::asio::error::in_progress + || op->ec_ == boost::asio::error::would_block) + { + op->ec_ = boost::system::error_code(); + reactor_.start_op(reactor::connect_op, impl.socket_, + impl.reactor_data_, op, is_continuation, false); + return; + } + } + } + + reactor_.post_immediate_completion(op, is_continuation); +} + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // !defined(BOOST_ASIO_HAS_IOCP) + // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#endif // BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_EXT_LOCAL_IPP diff --git a/implementation/helper/1.74/boost/asio/detail/impl/socket_ops_ext.ipp b/implementation/helper/1.74/boost/asio/detail/impl/socket_ops_ext.ipp new file mode 100644 index 0000000..d9585f6 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/impl/socket_ops_ext.ipp @@ -0,0 +1,214 @@ +// +// detail/impl/socket_ops_ext.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_IPP +#define BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_IPP + +#include <boost/asio/detail/impl/socket_ops.ipp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { +namespace socket_ops { + +signed_size_type recvfrom(socket_type s, buf* bufs, size_t count, + int flags, socket_addr_type* addr, std::size_t* addrlen, + boost::system::error_code& ec, boost::asio::ip::address& da) +{ + +#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) + GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; + LPFN_WSARECVMSG WSARecvMsg; + DWORD NumberOfBytes; + signed_size_type result; + + result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecvMsg_GUID, sizeof WSARecvMsg_GUID, + &WSARecvMsg, sizeof WSARecvMsg, + &NumberOfBytes, NULL, NULL); + get_last_error(ec, true); + if (ec.value() == SOCKET_ERROR) { + WSARecvMsg = NULL; + return 0; + } + + WSABUF wsaBuf; + WSAMSG msg; + char controlBuffer[1024]; + msg.name = addr; + msg.namelen = *addrlen; + wsaBuf.buf = bufs->buf; + wsaBuf.len = bufs->len; + msg.lpBuffers = &wsaBuf; + msg.dwBufferCount = count; + msg.Control.len = sizeof controlBuffer; + msg.Control.buf = controlBuffer; + msg.dwFlags = flags; + + DWORD dwNumberOfBytesRecvd; + result = WSARecvMsg(s, &msg, &dwNumberOfBytesRecvd, NULL, NULL); + get_last_error(ec, true); + + if (result >= 0) { + ec = boost::system::error_code(); + + // Find destination address + for (LPWSACMSGHDR cmsg = WSA_CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = WSA_CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level != IPPROTO_IP || cmsg->cmsg_type != IP_PKTINFO) + continue; + + struct in_pktinfo *pi = (struct in_pktinfo *) WSA_CMSG_DATA(cmsg); + if (pi) + { + da = boost::asio::ip::address_v4(ntohl(pi->ipi_addr.s_addr)); + } + } + } else { + dwNumberOfBytesRecvd = -1; + } + return dwNumberOfBytesRecvd; +#else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) + char cmbuf[0x100]; + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = static_cast<int>(*addrlen); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast<int>(count); + msg.msg_control = cmbuf; + msg.msg_controllen = sizeof(cmbuf); + signed_size_type result = ::recvmsg(s, &msg, flags); + get_last_error(ec, true); + *addrlen = msg.msg_namelen; + if (result >= 0) { + ec.assign(0, ec.category()); + + // Find destination address + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level != IPPROTO_IP || cmsg->cmsg_type != IP_PKTINFO) + continue; + + struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cmsg); + if (pi) + { + da = boost::asio::ip::address_v4(ntohl(pi->ipi_addr.s_addr)); + } + } + } + return result; +#endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, boost::system::error_code& ec, boost::asio::ip::address& da) +{ + if (s == invalid_socket) + { + ec = boost::asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec, da); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != boost::asio::error::would_block + && ec != boost::asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(BOOST_ASIO_HAS_IOCP) + +void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + boost::system::error_code& ec, boost::asio::ip::address& da) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = boost::asio::error::operation_aborted; + else + ec = boost::asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = boost::asio::error::connection_refused; + } +} + +#else // defined(BOOST_ASIO_HAS_IOCP) + +bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + boost::system::error_code& ec, size_t& bytes_transferred, boost::asio::ip::address& da) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec, da); + + // Retry operation if interrupted by signal. + if (ec == boost::asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == boost::asio::error::would_block + || ec == boost::asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = boost::system::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(BOOST_ASIO_HAS_IOCP) + +} // namespace socket_ops +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_IPP diff --git a/implementation/helper/1.74/boost/asio/detail/impl/socket_ops_ext_local.ipp b/implementation/helper/1.74/boost/asio/detail/impl/socket_ops_ext_local.ipp new file mode 100644 index 0000000..83a673b --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/impl/socket_ops_ext_local.ipp @@ -0,0 +1,307 @@ +// +// detail/impl/socket_ops_ext_local.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_LOCAL_IPP +#define BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_LOCAL_IPP + +#include <boost/asio/detail/impl/socket_ops.ipp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { +namespace socket_ops { + +signed_size_type recv(socket_type s, buf* bufs, size_t count, + int flags, boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid) +{ + uid = 0xFFFFFFFF; + gid = 0xFFFFFFFF; + struct ucred *ucredp; + +#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast<DWORD>(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(s, bufs, + recv_buf_count, &bytes_transferred, &recv_flags, 0, 0); + get_last_error(ec, true); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = boost::asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = boost::asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast<int>(count); + + union { + struct cmsghdr cmh; + char control[CMSG_SPACE(sizeof(struct ucred))]; + } control_un; + + // Set 'control_un' to describe ancillary data that we want to receive + control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + control_un.cmh.cmsg_level = SOL_SOCKET; + control_un.cmh.cmsg_type = SCM_CREDENTIALS; + + // Set 'msg' fields to describe 'control_un' + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + + signed_size_type result = ::recvmsg(s, &msg, flags); + get_last_error(ec, true); + if (result >= 0) { + ec.assign(0, ec.category()); + + // Find UID / GID + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDENTIALS + || cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) + continue; + + ucredp = (struct ucred *) CMSG_DATA(cmsg); + if (ucredp) { + uid = ucredp->uid; + gid = ucredp->gid; + } + } + } + return result; +#endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) +} + +signed_size_type recvfrom(socket_type s, buf* bufs, size_t count, + int flags, socket_addr_type* addr, std::size_t* addrlen, + boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid) +{ + uid = 0xFFFFFFFF; + gid = 0xFFFFFFFF; + struct ucred *ucredp; + clear_last_error(); +#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast<DWORD>(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int tmp_addrlen = (int)*addrlen; + int result = ::WSARecvFrom(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0); + get_last_error(ec, true); + *addrlen = (std::size_t)tmp_addrlen; + if (ec.value() == ERROR_NETNAME_DELETED) + ec = boost::asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = boost::asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec.assign(0, ec.category()); + return bytes_transferred; +#else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = static_cast<int>(*addrlen); + msg.msg_iov = bufs; + msg.msg_iovlen = static_cast<int>(count); + + union { + struct cmsghdr cmh; + char control[CMSG_SPACE(sizeof(struct ucred))]; + } control_un; + + // Set 'control_un' to describe ancillary data that we want to receive + control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + control_un.cmh.cmsg_level = SOL_SOCKET; + control_un.cmh.cmsg_type = SCM_CREDENTIALS; + + // Set 'msg' fields to describe 'control_un' + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + + signed_size_type result = ::recvmsg(s, &msg, flags); + get_last_error(ec, true); + *addrlen = msg.msg_namelen; + if (result >= 0) { + ec.assign(0, ec.category()); + + // Find UID / GID + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDENTIALS + || cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) + continue; + + ucredp = (struct ucred *) CMSG_DATA(cmsg); + if (ucredp) { + uid = ucredp->uid; + gid = ucredp->gid; + } + } + } + return result; +#endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) +} + +size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid) +{ + if (s == invalid_socket) + { + ec = boost::asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec, uid, gid); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != boost::asio::error::would_block + && ec != boost::asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, 0, -1, ec) < 0) + return 0; + } +} + +#if defined(BOOST_ASIO_HAS_IOCP) + +void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid) +{ + uid = 0xFFFFFFFF; + gid = 0xFFFFFFFF; + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = boost::asio::error::operation_aborted; + else + ec = boost::asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = boost::asio::error::connection_refused; + } +} + +#else // defined(BOOST_ASIO_HAS_IOCP) + +bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + boost::system::error_code& ec, size_t& bytes_transferred, + std::uint32_t& uid, std::uint32_t& gid) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec, uid, gid); + + // Check for end of stream. + if (is_stream && bytes == 0) + { + ec = boost::asio::error::eof; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == boost::asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == boost::asio::error::would_block + || ec == boost::asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = boost::system::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + boost::system::error_code& ec, size_t& bytes_transferred, + std::uint32_t& uid, std::uint32_t& gid) +{ + for (;;) + { + // Read some data. + signed_size_type bytes = socket_ops::recvfrom( + s, bufs, count, flags, addr, addrlen, ec, uid, gid); + + // Retry operation if interrupted by signal. + if (ec == boost::asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == boost::asio::error::would_block + || ec == boost::asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = boost::system::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(BOOST_ASIO_HAS_IOCP) + +} // namespace socket_ops +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_LOCAL_IPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_recv_op_ext.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recv_op_ext.hpp new file mode 100644 index 0000000..1167d57 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recv_op_ext.hpp @@ -0,0 +1,162 @@ +// +// detail/reactive_socket_recv_op_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_EXT_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/handler_alloc_helpers.hpp> +#include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/handler_work.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactor_op_ext.hpp> +#include <boost/asio/detail/socket_ops_ext.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename MutableBufferSequence> +class reactive_socket_recv_op_base_ext : public reactor_op_ext +{ +public: + reactive_socket_recv_op_base_ext(const boost::system::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op_ext(success_ec, &reactive_socket_recv_op_base_ext::do_perform, complete_func), + socket_(socket), + state_(state), + buffers_(buffers), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recv_op_base_ext* o( + static_cast<reactive_socket_recv_op_base_ext*>(base)); + + typedef buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs_type; + + status result; + if (bufs_type::is_single_buffer) + { + result = socket_ops::non_blocking_recv1(o->socket_, + bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), o->flags_, + (o->state_ & socket_ops::stream_oriented) != 0, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + else + { + bufs_type bufs(o->buffers_); + result = socket_ops::non_blocking_recv(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + (o->state_ & socket_ops::stream_oriented) != 0, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + + if (result == done) + if ((o->state_ & socket_ops::stream_oriented) != 0) + if (o->bytes_transferred_ == 0) + result = done_and_exhausted; + + BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recv", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + socket_ops::state_type state_; + MutableBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template <typename MutableBufferSequence, typename Handler, typename IoExecutor> +class reactive_socket_recv_op_ext : + public reactive_socket_recv_op_base_ext<MutableBufferSequence> +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(reactive_socket_recv_op_ext); + + reactive_socket_recv_op_ext(const boost::system::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + : reactive_socket_recv_op_base_ext<MutableBufferSequence>(success_ec, + socket, state, buffers, flags, + &reactive_socket_recv_op_ext::do_complete), + handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const boost::system::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recv_op_ext* o(static_cast<reactive_socket_recv_op_ext*>(base)); + ptr p = { boost::asio::detail::addressof(o->handler_), o, o }; + + BOOST_ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work<Handler, IoExecutor> w( + BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder3<Handler, boost::system::error_code, std::size_t, boost::asio::ip::address> + handler(o->handler_, o->ec_, o->bytes_transferred_, o->da_); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_, handler.arg3_)); + w.complete(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work<Handler, IoExecutor> work_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_recv_op_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recv_op_ext_local.hpp new file mode 100644 index 0000000..bedbd29 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recv_op_ext_local.hpp @@ -0,0 +1,162 @@ +// +// detail/reactive_socket_recv_op_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/handler_alloc_helpers.hpp> +#include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/handler_work.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactor_op_ext_local.hpp> +#include <boost/asio/detail/socket_ops_ext_local.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename MutableBufferSequence> +class reactive_socket_recv_op_base_ext_local : public reactor_op_ext_local +{ +public: + reactive_socket_recv_op_base_ext_local(const boost::system::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op_ext_local(success_ec, &reactive_socket_recv_op_base_ext_local::do_perform, complete_func), + socket_(socket), + state_(state), + buffers_(buffers), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recv_op_base_ext_local* o( + static_cast<reactive_socket_recv_op_base_ext_local*>(base)); + + typedef buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs_type; + + status result; + if (bufs_type::is_single_buffer) + { + result = socket_ops::non_blocking_recv1(o->socket_, + bufs_type::first(o->buffers_).data(), + bufs_type::first(o->buffers_).size(), o->flags_, + (o->state_ & socket_ops::stream_oriented) != 0, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + else + { + bufs_type bufs(o->buffers_); + result = socket_ops::non_blocking_recv(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + (o->state_ & socket_ops::stream_oriented) != 0, + o->ec_, o->bytes_transferred_) ? done : not_done; + } + + if (result == done) + if ((o->state_ & socket_ops::stream_oriented) != 0) + if (o->bytes_transferred_ == 0) + result = done_and_exhausted; + + BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recv", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + socket_ops::state_type state_; + MutableBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template <typename MutableBufferSequence, typename Handler, typename IoExecutor> +class reactive_socket_recv_op_ext_local : + public reactive_socket_recv_op_base_ext_local<MutableBufferSequence> +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(reactive_socket_recv_op_ext_local); + + reactive_socket_recv_op_ext_local(const boost::system::error_code& success_ec, + socket_type socket, socket_ops::state_type state, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + : reactive_socket_recv_op_base_ext_local<MutableBufferSequence>(success_ec, + socket, state, buffers, flags, + &reactive_socket_recv_op_ext_local::do_complete), + handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const boost::system::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recv_op_ext_local* o(static_cast<reactive_socket_recv_op_ext_local*>(base)); + ptr p = { boost::asio::detail::addressof(o->handler_), o, o }; + + BOOST_ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work<Handler, IoExecutor> w( + BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder4<Handler, boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t> + handler(o->handler_, o->ec_, o->bytes_transferred_, o->uid_, o->gid_); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_, handler.arg3, handler.arg4)); + w.complete(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work<Handler, IoExecutor> work_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvfrom_op_ext.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvfrom_op_ext.hpp new file mode 100644 index 0000000..875fe14 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvfrom_op_ext.hpp @@ -0,0 +1,153 @@ +// +// detail/reactive_socket_recvfrom_op_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_EXT_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/handler_alloc_helpers.hpp> +#include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/handler_work.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactor_op_ext.hpp> +#include <boost/asio/detail/socket_ops_ext.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename MutableBufferSequence, typename Endpoint> +class reactive_socket_recvfrom_op_base_ext : public reactor_op_ext +{ +public: + reactive_socket_recvfrom_op_base_ext(const boost::system::error_code& success_ec, + socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, func_type complete_func) + : reactor_op_ext(success_ec, &reactive_socket_recvfrom_op_base_ext::do_perform, complete_func), + socket_(socket), + protocol_type_(protocol_type), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvfrom_op_base_ext* o( + static_cast<reactive_socket_recvfrom_op_base_ext*>(base)); + + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); + + std::size_t addr_len = o->sender_endpoint_.capacity(); + status result = socket_ops::non_blocking_recvfrom(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->sender_endpoint_.data(), &addr_len, + o->ec_, o->bytes_transferred_, o->da_) ? done : not_done; + + if (result && !o->ec_) + o->sender_endpoint_.resize(addr_len); + + BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvfrom", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + int protocol_type_; + MutableBufferSequence buffers_; + Endpoint& sender_endpoint_; + socket_base::message_flags flags_; +}; + +template <typename MutableBufferSequence, typename Endpoint, + typename Handler, typename IoExecutor> +class reactive_socket_recvfrom_op_ext : + public reactive_socket_recvfrom_op_base_ext<MutableBufferSequence, Endpoint> +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvfrom_op_ext); + + reactive_socket_recvfrom_op_ext(const boost::system::error_code& success_ec, + socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + : reactive_socket_recvfrom_op_base_ext<MutableBufferSequence, Endpoint>( + success_ec, socket, protocol_type, buffers, endpoint, flags, + &reactive_socket_recvfrom_op_ext::do_complete), + handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const boost::system::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvfrom_op_ext* o( + static_cast<reactive_socket_recvfrom_op_ext*>(base)); + ptr p = { boost::asio::detail::addressof(o->handler_), o, o }; + + BOOST_ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work<Handler, IoExecutor> w( + BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder3<Handler, boost::system::error_code, std::size_t, boost::asio::ip::address> + handler(o->handler_, o->ec_, o->bytes_transferred_, o->da_); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_, handler.arg3_)); + w.complete(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work<Handler, IoExecutor> work_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvfrom_op_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvfrom_op_ext_local.hpp new file mode 100644 index 0000000..f9fef2b --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvfrom_op_ext_local.hpp @@ -0,0 +1,148 @@ +// +// detail/reactive_socket_recvfrom_op_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactor_op_ext_local.hpp> +#include <boost/asio/detail/socket_ops_ext_local.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename MutableBufferSequence, typename Endpoint> +class reactive_socket_recvfrom_op_base_ext_local : public reactor_op_ext_local +{ +public: + reactive_socket_recvfrom_op_base_ext_local(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, func_type complete_func) + : reactor_op_ext_local(&reactive_socket_recvfrom_op_base_ext_local::do_perform, complete_func), + socket_(socket), + protocol_type_(protocol_type), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvfrom_op_base_ext_local* o( + static_cast<reactive_socket_recvfrom_op_base_ext_local*>(base)); + + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); + + std::size_t addr_len = o->sender_endpoint_.capacity(); + status result = socket_ops::non_blocking_recvfrom(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->sender_endpoint_.data(), &addr_len, + o->ec_, o->bytes_transferred_, o->uid_, o->gid_) ? done : not_done; + + if (result && !o->ec_) + o->sender_endpoint_.resize(addr_len); + + BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvfrom", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + int protocol_type_; + MutableBufferSequence buffers_; + Endpoint& sender_endpoint_; + socket_base::message_flags flags_; +}; + +template <typename MutableBufferSequence, typename Endpoint, + typename Handler, typename IoExecutor> +class reactive_socket_recvfrom_op_ext_local : + public reactive_socket_recvfrom_op_base_ext_local<MutableBufferSequence, Endpoint> +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvfrom_op_ext_local); + + reactive_socket_recvfrom_op_ext_local(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + : reactive_socket_recvfrom_op_base_ext_local<MutableBufferSequence, Endpoint>( + socket, protocol_type, buffers, endpoint, flags, + &reactive_socket_recvfrom_op_ext_local::do_complete), + handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const boost::system::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvfrom_op_ext_local* o( + static_cast<reactive_socket_recvfrom_op_ext_local*>(base)); + ptr p = { boost::asio::detail::addressof(o->handler_), o, o }; + + BOOST_ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work<Handler, IoExecutor> w( + BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder4<Handler, boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t> + handler(o->handler_, o->ec_, o->bytes_transferred_, o->uid_, o->gid_); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_, handler.arg3_, handler.arg4_)); + w.complete(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work<Handler, IoExecutor> work_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvmsg_op_ext.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvmsg_op_ext.hpp new file mode 100644 index 0000000..bd315dc --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvmsg_op_ext.hpp @@ -0,0 +1,144 @@ +// +// detail/reactive_socket_recvmsg_op_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_EXT_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/handler_alloc_helpers.hpp> +#include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/handler_work.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactor_op_ext.hpp> +#include <boost/asio/detail/socket_ops_ext.hpp> +#include <boost/asio/socket_base.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename MutableBufferSequence> +class reactive_socket_recvmsg_op_base_ext : public reactor_op_ext +{ +public: + reactive_socket_recvmsg_op_base_ext(const boost::system::error_code& success_ec, + socket_type socket, const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, func_type complete_func) + : reactor_op_ext(&reactive_socket_recvmsg_op_base_ext::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + in_flags_(in_flags), + out_flags_(out_flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvmsg_op_base_ext* o( + static_cast<reactive_socket_recvmsg_op_base_ext*>(base)); + + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); + + status result = socket_ops::non_blocking_recvmsg(o->socket_, + bufs.buffers(), bufs.count(), + o->in_flags_, o->out_flags_, + o->ec_, o->bytes_transferred_) ? done : not_done; + + BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvmsg", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + MutableBufferSequence buffers_; + socket_base::message_flags in_flags_; + socket_base::message_flags& out_flags_; +}; + +template <typename MutableBufferSequence, typename Handler, typename IoExecutor> +class reactive_socket_recvmsg_op_ext : + public reactive_socket_recvmsg_op_base_ext<MutableBufferSequence> +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvmsg_op_ext); + + reactive_socket_recvmsg_op_ext(const boost::system::error_code& success_ec, + socket_type socket, const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + : reactive_socket_recvmsg_op_base_ext<MutableBufferSequence>(socket, buffers, + in_flags, out_flags, &reactive_socket_recvmsg_op_ext::do_complete), + handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const boost::system::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvmsg_op_ext* o( + static_cast<reactive_socket_recvmsg_op_ext*>(base)); + ptr p = { boost::asio::detail::addressof(o->handler_), o, o }; + + BOOST_ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work<Handler, IoExecutor> w( + BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder3<Handler, boost::system::error_code, std::size_t, boost::asio::ip::address> + handler(o->handler_, o->ec_, o->bytes_transferred_, o->da_); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_, handler.arg3_)); + w.complete(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work<Handler, IoExecutor> work_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvmsg_op_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvmsg_op_ext_local.hpp new file mode 100644 index 0000000..8148690 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_recvmsg_op_ext_local.hpp @@ -0,0 +1,145 @@ +// +// detail/reactive_socket_recvmsg_op_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/detail/bind_handler.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/fenced_block.hpp> +#include <boost/asio/detail/handler_alloc_helpers.hpp> +#include <boost/asio/detail/handler_invoke_helpers.hpp> +#include <boost/asio/detail/handler_work.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactor_op_ext_local.hpp> +#include <boost/asio/detail/socket_ops_ext_local.hpp> +#include <boost/asio/socket_base.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename MutableBufferSequence> +class reactive_socket_recvmsg_op_base_ext_local : public reactor_op_ext_local +{ +public: + reactive_socket_recvmsg_op_base_ext_local(const boost::system::error_code& success_ec, + socket_type socket, const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, func_type complete_func) + : reactor_op_ext_local(success_ec, &reactive_socket_recvmsg_op_base_ext_local::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + in_flags_(in_flags), + out_flags_(out_flags) + { + } + + static status do_perform(reactor_op* base) + { + reactive_socket_recvmsg_op_base_ext_local* o( + static_cast<reactive_socket_recvmsg_op_base_ext_local*>(base)); + + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(o->buffers_); + + status result = socket_ops::non_blocking_recvmsg(o->socket_, + bufs.buffers(), bufs.count(), + o->in_flags_, o->out_flags_, + o->ec_, o->bytes_transferred_) ? done : not_done; + + BOOST_ASIO_HANDLER_REACTOR_OPERATION((*o, "non_blocking_recvmsg", + o->ec_, o->bytes_transferred_)); + + return result; + } + +private: + socket_type socket_; + MutableBufferSequence buffers_; + socket_base::message_flags in_flags_; + socket_base::message_flags& out_flags_; +}; + +template <typename MutableBufferSequence, typename Handler, typename IoExecutor> +class reactive_socket_recvmsg_op_ext_local : + public reactive_socket_recvmsg_op_base_ext_local<MutableBufferSequence> +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvmsg_op_ext_local); + + reactive_socket_recvmsg_op_ext_local(const boost::system::error_code& success_ec, + socket_type socket, const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + : reactive_socket_recvmsg_op_base_ext_local<MutableBufferSequence>(success_ec, + socket, buffers, in_flags, out_flags, + &reactive_socket_recvmsg_op_ext_local::do_complete), + handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)), + work_(handler_, io_ex) + { + } + + static void do_complete(void* owner, operation* base, + const boost::system::error_code& /*ec*/, + std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvmsg_op_ext_local* o( + static_cast<reactive_socket_recvmsg_op_ext_local*>(base)); + ptr p = { boost::asio::detail::addressof(o->handler_), o, o }; + + BOOST_ASIO_HANDLER_COMPLETION((*o)); + + // Take ownership of the operation's outstanding work. + handler_work<Handler, IoExecutor> w( + BOOST_ASIO_MOVE_CAST2(handler_work<Handler, IoExecutor>)( + o->work_)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder4<Handler, boost::system::error_code, std::size_t, std::uint32_t, std::uint32_t> + handler(o->handler_, o->ec_, o->bytes_transferred_, o->uid_, o->gid_); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + fenced_block b(fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_, handler.arg3_, handler.arg4_)); + w.complete(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + +private: + Handler handler_; + handler_work<Handler, IoExecutor> work_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_RECVMSG_OP_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_base_ext.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_base_ext.hpp new file mode 100644 index 0000000..2e68a87 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_base_ext.hpp @@ -0,0 +1,524 @@ +// +// detail/reactive_socket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_EXT_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#if !defined(BOOST_ASIO_HAS_IOCP) \ + && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#include <boost/asio/buffer.hpp> +#include <boost/asio/error.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/socket_base.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactive_null_buffers_op.hpp> +#include <boost/asio/detail/reactive_socket_recv_op_ext.hpp> +#include <boost/asio/detail/reactive_socket_recvmsg_op_ext.hpp> +#include <boost/asio/detail/reactive_socket_send_op.hpp> +#include <boost/asio/detail/reactive_wait_op.hpp> +#include <boost/asio/detail/reactor.hpp> +#include <boost/asio/detail/reactor_op.hpp> +#include <boost/asio/detail/socket_holder.hpp> +#include <boost/asio/detail/socket_ops_ext.hpp> +#include <boost/asio/detail/socket_types.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class reactive_socket_service_base_ext +{ +public: + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + BOOST_ASIO_DECL reactive_socket_service_base_ext(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + BOOST_ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + BOOST_ASIO_DECL void construct(base_implementation_type& impl); + + // Move-construct a new socket implementation. + BOOST_ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl); + + // Move-assign from another socket implementation. + BOOST_ASIO_DECL void base_move_assign(base_implementation_type& impl, + reactive_socket_service_base_ext& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + BOOST_ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + BOOST_ASIO_DECL boost::system::error_code close( + base_implementation_type& impl, boost::system::error_code& ec); + + // Release ownership of the socket. + BOOST_ASIO_DECL socket_type release( + base_implementation_type& impl, boost::system::error_code& ec); + + // Get the native socket representation. + native_handle_type native_handle(base_implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + BOOST_ASIO_DECL boost::system::error_code cancel( + base_implementation_type& impl, boost::system::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + boost::system::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + boost::system::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + boost::system::error_code listen(base_implementation_type& impl, + int backlog, boost::system::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template <typename IO_Control_Command> + boost::system::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, boost::system::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast<ioctl_arg_type*>(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the socket. + boost::system::error_code non_blocking(base_implementation_type& impl, + bool mode, boost::system::error_code& ec) + { + socket_ops::set_user_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native socket implementation. + boost::system::error_code native_non_blocking(base_implementation_type& impl, + bool mode, boost::system::error_code& ec) + { + socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Wait for the socket to become ready to read, ready to write, or to have + // pending error conditions. + boost::system::error_code wait(base_implementation_type& impl, + socket_base::wait_type w, boost::system::error_code& ec) + { + switch (w) + { + case socket_base::wait_read: + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_write: + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_error: + socket_ops::poll_error(impl.socket_, impl.state_, -1, ec); + break; + default: + ec = boost::asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the socket to become ready to read, ready to + // write, or to have pending error conditions. + template <typename Handler, typename IoExecutor> + void async_wait(base_implementation_type& impl, + socket_base::wait_type w, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_wait_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_wait")); + + int op_type; + switch (w) + { + case socket_base::wait_read: + op_type = reactor::read_op; + break; + case socket_base::wait_write: + op_type = reactor::write_op; + break; + case socket_base::wait_error: + op_type = reactor::except_op; + break; + default: + p.p->ec_ = boost::asio::error::invalid_argument; + reactor_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + return; + } + + start_op(impl, op_type, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Send the given data to the peer. + template <typename ConstBufferSequence> + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler, typename IoExecutor> + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_send_op< + ConstBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, + buffers, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::all_empty(buffers))); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template <typename Handler, typename IoExecutor> + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template <typename MutableBufferSequence> + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template <typename MutableBufferSequence, + typename Handler, typename IoExecutor> + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recv_op_ext< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, + buffers, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (flags & socket_base::message_out_of_band) == 0, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::all_empty(buffers))); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template <typename Handler, typename IoExecutor> + void async_receive(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive(null_buffers)")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template <typename MutableBufferSequence> + size_t receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + return socket_ops::sync_recvmsg(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), in_flags, out_flags, ec); + } + + // Wait until data can be received without blocking. + size_t receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags& out_flags, boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template <typename MutableBufferSequence, + typename Handler, typename IoExecutor> + void async_receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvmsg_op_ext< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, buffers, + in_flags, out_flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags")); + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (in_flags & socket_base::message_out_of_band) == 0, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template <typename Handler, typename IoExecutor> + void async_receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags(null_buffers)")); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + +protected: + // Open a new socket implementation. + BOOST_ASIO_DECL boost::system::error_code do_open( + base_implementation_type& impl, int af, + int type, int protocol, boost::system::error_code& ec); + + // Assign a native socket to a socket implementation. + BOOST_ASIO_DECL boost::system::error_code do_assign( + base_implementation_type& impl, int type, + const native_handle_type& native_socket, boost::system::error_code& ec); + + // Start the asynchronous read or write operation. + BOOST_ASIO_DECL void start_op(base_implementation_type& impl, int op_type, + reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop); + + // Start the asynchronous accept operation. + BOOST_ASIO_DECL void start_accept_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open); + + // Start the asynchronous connect operation. + BOOST_ASIO_DECL void start_connect_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen); + + // The selector that performs event demultiplexing for the service. + reactor& reactor_; + + // Cached success value to avoid accessing category singleton. + const boost::system::error_code success_ec_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#if defined(BOOST_ASIO_HEADER_ONLY) +# include <boost/asio/detail/impl/reactive_socket_service_base_ext.ipp> +#endif // defined(BOOST_ASIO_HEADER_ONLY) + +#endif // !defined(BOOST_ASIO_HAS_IOCP) + // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_base_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_base_ext_local.hpp new file mode 100644 index 0000000..a71fce5 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_base_ext_local.hpp @@ -0,0 +1,524 @@ +// +// detail/reactive_socket_service_base_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#if !defined(BOOST_ASIO_HAS_IOCP) \ + && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#include <boost/asio/buffer.hpp> +#include <boost/asio/error.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/socket_base.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/reactive_null_buffers_op.hpp> +#include <boost/asio/detail/reactive_socket_recv_op_ext_local.hpp> +#include <boost/asio/detail/reactive_socket_recvmsg_op_ext_local.hpp> +#include <boost/asio/detail/reactive_socket_send_op.hpp> +#include <boost/asio/detail/reactive_wait_op.hpp> +#include <boost/asio/detail/reactor.hpp> +#include <boost/asio/detail/reactor_op.hpp> +#include <boost/asio/detail/socket_holder.hpp> +#include <boost/asio/detail/socket_ops_ext_local.hpp> +#include <boost/asio/detail/socket_types.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class reactive_socket_service_base_ext_local +{ +public: + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + BOOST_ASIO_DECL reactive_socket_service_base_ext_local(execution_context& context); + + // Destroy all user-defined handler objects owned by the service. + BOOST_ASIO_DECL void base_shutdown(); + + // Construct a new socket implementation. + BOOST_ASIO_DECL void construct(base_implementation_type& impl); + + // Move-construct a new socket implementation. + BOOST_ASIO_DECL void base_move_construct(base_implementation_type& impl, + base_implementation_type& other_impl); + + // Move-assign from another socket implementation. + BOOST_ASIO_DECL void base_move_assign(base_implementation_type& impl, + reactive_socket_service_base_ext_local& other_service, + base_implementation_type& other_impl); + + // Destroy a socket implementation. + BOOST_ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + BOOST_ASIO_DECL boost::system::error_code close( + base_implementation_type& impl, boost::system::error_code& ec); + + // Release ownership of the socket. + BOOST_ASIO_DECL socket_type release( + base_implementation_type& impl, boost::system::error_code& ec); + + // Get the native socket representation. + native_handle_type native_handle(base_implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + BOOST_ASIO_DECL boost::system::error_code cancel( + base_implementation_type& impl, boost::system::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + boost::system::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + boost::system::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + boost::system::error_code listen(base_implementation_type& impl, + int backlog, boost::system::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template <typename IO_Control_Command> + boost::system::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, boost::system::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast<ioctl_arg_type*>(command.data()), ec); + return ec; + } + + // Gets the non-blocking mode of the socket. + bool non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::user_set_non_blocking) != 0; + } + + // Sets the non-blocking mode of the socket. + boost::system::error_code non_blocking(base_implementation_type& impl, + bool mode, boost::system::error_code& ec) + { + socket_ops::set_user_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Gets the non-blocking mode of the native socket implementation. + bool native_non_blocking(const base_implementation_type& impl) const + { + return (impl.state_ & socket_ops::internal_non_blocking) != 0; + } + + // Sets the non-blocking mode of the native socket implementation. + boost::system::error_code native_non_blocking(base_implementation_type& impl, + bool mode, boost::system::error_code& ec) + { + socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, mode, ec); + return ec; + } + + // Wait for the socket to become ready to read, ready to write, or to have + // pending error conditions. + boost::system::error_code wait(base_implementation_type& impl, + socket_base::wait_type w, boost::system::error_code& ec) + { + switch (w) + { + case socket_base::wait_read: + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_write: + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + break; + case socket_base::wait_error: + socket_ops::poll_error(impl.socket_, impl.state_, -1, ec); + break; + default: + ec = boost::asio::error::invalid_argument; + break; + } + + return ec; + } + + // Asynchronously wait for the socket to become ready to read, ready to + // write, or to have pending error conditions. + template <typename Handler, typename IoExecutor> + void async_wait(base_implementation_type& impl, + socket_base::wait_type w, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_wait_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_wait")); + + int op_type; + switch (w) + { + case socket_base::wait_read: + op_type = reactor::read_op; + break; + case socket_base::wait_write: + op_type = reactor::write_op; + break; + case socket_base::wait_error: + op_type = reactor::except_op; + break; + default: + p.p->ec_ = boost::asio::error::invalid_argument; + reactor_.post_immediate_completion(p.p, is_continuation); + p.v = p.p = 0; + return; + } + + start_op(impl, op_type, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Send the given data to the peer. + template <typename ConstBufferSequence> + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler, typename IoExecutor> + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_send_op< + ConstBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, + buffers, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence>::all_empty(buffers))); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template <typename Handler, typename IoExecutor> + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template <typename MutableBufferSequence> + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template <typename MutableBufferSequence, + typename Handler, typename IoExecutor> + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recv_op_ext_local< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, + buffers, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (flags & socket_base::message_out_of_band) == 0, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence>::all_empty(buffers))); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template <typename Handler, typename IoExecutor> + void async_receive(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive(null_buffers)")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive some data with associated flags. Returns the number of bytes + // received. + template <typename MutableBufferSequence> + size_t receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + return socket_ops::sync_recvmsg(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), in_flags, out_flags, ec); + } + + // Wait until data can be received without blocking. + size_t receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags, + socket_base::message_flags& out_flags, boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template <typename MutableBufferSequence, + typename Handler, typename IoExecutor> + void async_receive_with_flags(base_implementation_type& impl, + const MutableBufferSequence& buffers, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvmsg_op_ext_local< + MutableBufferSequence, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, buffers, + in_flags, out_flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags")); + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, + (in_flags & socket_base::message_out_of_band) == 0, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template <typename Handler, typename IoExecutor> + void async_receive_with_flags(base_implementation_type& impl, + const null_buffers&, socket_base::message_flags in_flags, + socket_base::message_flags& out_flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_with_flags(null_buffers)")); + + // Clear out_flags, since we cannot give it any other sensible value when + // performing a null_buffers operation. + out_flags = 0; + + start_op(impl, + (in_flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + +protected: + // Open a new socket implementation. + BOOST_ASIO_DECL boost::system::error_code do_open( + base_implementation_type& impl, int af, + int type, int protocol, boost::system::error_code& ec); + + // Assign a native socket to a socket implementation. + BOOST_ASIO_DECL boost::system::error_code do_assign( + base_implementation_type& impl, int type, + const native_handle_type& native_socket, boost::system::error_code& ec); + + // Start the asynchronous read or write operation. + BOOST_ASIO_DECL void start_op(base_implementation_type& impl, int op_type, + reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop); + + // Start the asynchronous accept operation. + BOOST_ASIO_DECL void start_accept_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, bool peer_is_open); + + // Start the asynchronous connect operation. + BOOST_ASIO_DECL void start_connect_op(base_implementation_type& impl, + reactor_op* op, bool is_continuation, + const socket_addr_type* addr, size_t addrlen); + + // The selector that performs event demultiplexing for the service. + reactor& reactor_; + + // Cached success value to avoid accessing category singleton. + const boost::system::error_code success_ec_; +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#if defined(BOOST_ASIO_HEADER_ONLY) +# include <boost/asio/detail/impl/reactive_socket_service_base_ext_local.ipp> +#endif // defined(BOOST_ASIO_HEADER_ONLY) + +#endif // !defined(BOOST_ASIO_HAS_IOCP) + // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_ext.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_ext.hpp new file mode 100644 index 0000000..7254ccd --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_ext.hpp @@ -0,0 +1,508 @@ +// +// detail/reactive_socket_service_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_EXT_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#if !defined(BOOST_ASIO_HAS_IOCP) + +#include <boost/asio/buffer.hpp> +#include <boost/asio/error.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/socket_base.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/reactive_null_buffers_op.hpp> +#include <boost/asio/detail/reactive_socket_accept_op.hpp> +#include <boost/asio/detail/reactive_socket_connect_op.hpp> +#include <boost/asio/detail/reactive_socket_recvfrom_op_ext.hpp> +#include <boost/asio/detail/reactive_socket_sendto_op.hpp> +#include <boost/asio/detail/reactive_socket_service_base_ext.hpp> +#include <boost/asio/detail/reactor.hpp> +#include <boost/asio/detail/reactor_op.hpp> +#include <boost/asio/detail/socket_holder.hpp> +#include <boost/asio/detail/socket_ops.hpp> +#include <boost/asio/detail/socket_types.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename Protocol> +class reactive_socket_service_ext : + public execution_context_service_base<reactive_socket_service_ext<Protocol> >, + public reactive_socket_service_base_ext +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct implementation_type : + reactive_socket_service_base_ext::base_implementation_type + { + // Default constructor. + implementation_type() + : protocol_(endpoint_type().protocol()) + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // Constructor. + reactive_socket_service_ext(execution_context& context) + : execution_context_service_base< + reactive_socket_service_ext<Protocol> >(context), + reactive_socket_service_base_ext(context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + reactive_socket_service_base_ext& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-construct a new socket implementation from another protocol type. + template <typename Protocol1> + void converting_move_construct(implementation_type& impl, + reactive_socket_service_ext<Protocol1>&, + typename reactive_socket_service_ext< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + } + + // Open a new socket implementation. + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, boost::system::error_code& ec) + { + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) + impl.protocol_ = protocol; + return ec; + } + + // Assign a native socket to a socket implementation. + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + boost::system::error_code& ec) + { + if (!do_assign(impl, protocol.type(), native_socket, ec)) + impl.protocol_ = protocol; + return ec; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type& impl) + { + return impl.socket_; + } + + // Bind the socket to the specified local endpoint. + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, boost::system::error_code& ec) + { + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Set a socket option. + template <typename Option> + boost::system::error_code set_option(implementation_type& impl, + const Option& option, boost::system::error_code& ec) + { + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + + // Set a socket option. + template <typename Option> + boost::system::error_code get_option(const implementation_type& impl, + Option& option, boost::system::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, + endpoint.data(), &addr_len, false, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Disable sends or receives on the socket. + boost::system::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, boost::system::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template <typename ConstBufferSequence> + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler, typename IoExecutor> + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_sendto_op<ConstBufferSequence, + endpoint_type, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, buffers, + destination, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template <typename Handler, typename IoExecutor> + void async_send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template <typename MutableBufferSequence> + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); + + if (!ec) + sender_endpoint.resize(addr_len); + + return bytes_recvd; + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags, + boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template <typename MutableBufferSequence, + typename Handler, typename IoExecutor> + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvfrom_op_ext<MutableBufferSequence, + endpoint_type, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + int protocol = impl.protocol_.type(); + p.p = new (p.v) op(success_ec_, impl.socket_, protocol, buffers, + sender_endpoint, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template <typename Handler, typename IoExecutor> + void async_receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from(null_buffers)")); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Accept a new connection. + template <typename Socket> + boost::system::error_code accept(implementation_type& impl, + Socket& peer, endpoint_type* peer_endpoint, boost::system::error_code& ec) + { + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = boost::asio::error::already_open; + return ec; + } + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return ec; + } + + // Start an asynchronous accept. The peer and peer_endpoint objects must be + // valid until the accept's handler is invoked. + template <typename Socket, typename Handler, typename IoExecutor> + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_accept_op<Socket, Protocol, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, peer, + impl.protocol_, peer_endpoint, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, peer.is_open()); + p.v = p.p = 0; + } + +#if defined(BOOST_ASIO_HAS_MOVE) + // Start an asynchronous accept. The peer_endpoint object must be valid until + // the accept's handler is invoked. + template <typename PeerIoExecutor, typename Handler, typename IoExecutor> + void async_move_accept(implementation_type& impl, + const PeerIoExecutor& peer_io_ex, endpoint_type* peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_move_accept_op<Protocol, + PeerIoExecutor, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, peer_io_ex, impl.socket_, impl.state_, + impl.protocol_, peer_endpoint, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, false); + p.v = p.p = 0; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) + + // Connect the socket to the specified endpoint. + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, boost::system::error_code& ec) + { + socket_ops::sync_connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + // Start an asynchronous connect. + template <typename Handler, typename IoExecutor> + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_connect_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_connect")); + + start_connect_op(impl, p.p, is_continuation, + peer_endpoint.data(), peer_endpoint.size()); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // !defined(BOOST_ASIO_HAS_IOCP) + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_ext_local.hpp new file mode 100644 index 0000000..8ac3265 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactive_socket_service_ext_local.hpp @@ -0,0 +1,508 @@ +// +// detail/reactive_socket_service_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#if !defined(BOOST_ASIO_HAS_IOCP) + +#include <boost/asio/buffer.hpp> +#include <boost/asio/error.hpp> +#include <boost/asio/execution_context.hpp> +#include <boost/asio/socket_base.hpp> +#include <boost/asio/detail/buffer_sequence_adapter.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/noncopyable.hpp> +#include <boost/asio/detail/reactive_null_buffers_op.hpp> +#include <boost/asio/detail/reactive_socket_accept_op.hpp> +#include <boost/asio/detail/reactive_socket_connect_op.hpp> +#include <boost/asio/detail/reactive_socket_recvfrom_op_ext_local.hpp> +#include <boost/asio/detail/reactive_socket_sendto_op.hpp> +#include <boost/asio/detail/reactive_socket_service_base_ext_local.hpp> +#include <boost/asio/detail/reactor.hpp> +#include <boost/asio/detail/reactor_op.hpp> +#include <boost/asio/detail/socket_holder.hpp> +#include <boost/asio/detail/socket_ops.hpp> +#include <boost/asio/detail/socket_types.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { + +template <typename Protocol> +class reactive_socket_service_ext_local : + public execution_context_service_base<reactive_socket_service_ext_local<Protocol> >, + public reactive_socket_service_base_ext_local +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef socket_type native_handle_type; + + // The implementation type of the socket. + struct implementation_type : + reactive_socket_service_base_ext_local::base_implementation_type + { + // Default constructor. + implementation_type() + : protocol_(endpoint_type().protocol()) + { + } + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // Constructor. + reactive_socket_service_ext_local(execution_context& context) + : execution_context_service_base< + reactive_socket_service_ext_local<Protocol> >(context), + reactive_socket_service_base_ext_local(context) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown() + { + this->base_shutdown(); + } + + // Move-construct a new socket implementation. + void move_construct(implementation_type& impl, + implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-assign from another socket implementation. + void move_assign(implementation_type& impl, + reactive_socket_service_base_ext_local& other_service, + implementation_type& other_impl) + { + this->base_move_assign(impl, other_service, other_impl); + + impl.protocol_ = other_impl.protocol_; + other_impl.protocol_ = endpoint_type().protocol(); + } + + // Move-construct a new socket implementation from another protocol type. + template <typename Protocol1> + void converting_move_construct(implementation_type& impl, + reactive_socket_service_ext_local<Protocol1>&, + typename reactive_socket_service_ext_local< + Protocol1>::implementation_type& other_impl) + { + this->base_move_construct(impl, other_impl); + + impl.protocol_ = protocol_type(other_impl.protocol_); + other_impl.protocol_ = typename Protocol1::endpoint().protocol(); + } + + // Open a new socket implementation. + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, boost::system::error_code& ec) + { + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) + impl.protocol_ = protocol; + return ec; + } + + // Assign a native socket to a socket implementation. + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_handle_type& native_socket, + boost::system::error_code& ec) + { + if (!do_assign(impl, protocol.type(), native_socket, ec)) + impl.protocol_ = protocol; + return ec; + } + + // Get the native socket representation. + native_handle_type native_handle(implementation_type& impl) + { + return impl.socket_; + } + + // Bind the socket to the specified local endpoint. + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, boost::system::error_code& ec) + { + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Set a socket option. + template <typename Option> + boost::system::error_code set_option(implementation_type& impl, + const Option& option, boost::system::error_code& ec) + { + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + + // Set a socket option. + template <typename Option> + boost::system::error_code get_option(const implementation_type& impl, + Option& option, boost::system::error_code& ec) const + { + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + endpoint_type endpoint; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, + endpoint.data(), &addr_len, false, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Disable sends or receives on the socket. + boost::system::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, boost::system::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template <typename ConstBufferSequence> + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::const_buffer, + ConstBufferSequence> bufs(buffers); + + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); + } + + // Wait until data can be sent without blocking. + size_t send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template <typename ConstBufferSequence, typename Handler, typename IoExecutor> + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_sendto_op<ConstBufferSequence, + endpoint_type, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, buffers, + destination, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to")); + + start_op(impl, reactor::write_op, p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template <typename Handler, typename IoExecutor> + void async_send_to(implementation_type& impl, const null_buffers&, + const endpoint_type&, socket_base::message_flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_send_to(null_buffers)")); + + start_op(impl, reactor::write_op, p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template <typename MutableBufferSequence> + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + boost::system::error_code& ec) + { + buffer_sequence_adapter<boost::asio::mutable_buffer, + MutableBufferSequence> bufs(buffers); + + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); + + if (!ec) + sender_endpoint.resize(addr_len); + + return bytes_recvd; + } + + // Wait until data can be received without blocking. + size_t receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags, + boost::system::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template <typename MutableBufferSequence, + typename Handler, typename IoExecutor> + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler& handler, + const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recvfrom_op_ext_local<MutableBufferSequence, + endpoint_type, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + int protocol = impl.protocol_.type(); + p.p = new (p.v) op(success_ec_, impl.socket_, protocol, buffers, + sender_endpoint, flags, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from")); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, true, false); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template <typename Handler, typename IoExecutor> + void async_receive_from(implementation_type& impl, const null_buffers&, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_receive_from(null_buffers)")); + + // Reset endpoint since it can be given no sensible value at this time. + sender_endpoint = endpoint_type(); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, is_continuation, false, false); + p.v = p.p = 0; + } + + // Accept a new connection. + template <typename Socket> + boost::system::error_code accept(implementation_type& impl, + Socket& peer, endpoint_type* peer_endpoint, boost::system::error_code& ec) + { + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = boost::asio::error::already_open; + return ec; + } + + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + return ec; + } + + // Start an asynchronous accept. The peer and peer_endpoint objects must be + // valid until the accept's handler is invoked. + template <typename Socket, typename Handler, typename IoExecutor> + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_accept_op<Socket, Protocol, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, peer, + impl.protocol_, peer_endpoint, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, peer.is_open()); + p.v = p.p = 0; + } + +#if defined(BOOST_ASIO_HAS_MOVE) + // Start an asynchronous accept. The peer_endpoint object must be valid until + // the accept's handler is invoked. + template <typename PeerIoExecutor, typename Handler, typename IoExecutor> + void async_move_accept(implementation_type& impl, + const PeerIoExecutor& peer_io_ex, endpoint_type* peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_move_accept_op<Protocol, + PeerIoExecutor, Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, peer_io_ex, impl.socket_, impl.state_, + impl.protocol_, peer_endpoint, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_accept")); + + start_accept_op(impl, p.p, is_continuation, false); + p.v = p.p = 0; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) + + // Connect the socket to the specified endpoint. + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, boost::system::error_code& ec) + { + socket_ops::sync_connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + // Start an asynchronous connect. + template <typename Handler, typename IoExecutor> + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + Handler& handler, const IoExecutor& io_ex) + { + bool is_continuation = + boost_asio_handler_cont_helpers::is_continuation(handler); + + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_connect_op<Handler, IoExecutor> op; + typename op::ptr p = { boost::asio::detail::addressof(handler), + op::ptr::allocate(handler), 0 }; + p.p = new (p.v) op(success_ec_, impl.socket_, handler, io_ex); + + BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket", + &impl, impl.socket_, "async_connect")); + + start_connect_op(impl, p.p, is_continuation, + peer_endpoint.data(), peer_endpoint.size()); + p.v = p.p = 0; + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // !defined(BOOST_ASIO_HAS_IOCP) + +#endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactor_op_ext.hpp b/implementation/helper/1.74/boost/asio/detail/reactor_op_ext.hpp new file mode 100644 index 0000000..697cd9f --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactor_op_ext.hpp @@ -0,0 +1,43 @@ +// +// detail/reactor_op_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTOR_OP_EXT_HPP +#define BOOST_ASIO_DETAIL_REACTOR_OP_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/reactor_op.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class reactor_op_ext + : public reactor_op +{ +public: + // The destination address + boost::asio::ip::address da_; + + reactor_op_ext(const boost::system::error_code& success_ec, + perform_func_type perform_func, func_type complete_func) + : reactor_op(success_ec, perform_func, complete_func) + { + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // BOOST_ASIO_DETAIL_REACTOR_OP_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/reactor_op_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/reactor_op_ext_local.hpp new file mode 100644 index 0000000..3d9ae5f --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/reactor_op_ext_local.hpp @@ -0,0 +1,44 @@ +// +// detail/reactor_op_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_REACTOR_OP_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_REACTOR_OP_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/reactor_op.hpp> + +namespace boost { +namespace asio { +namespace detail { + +class reactor_op_ext_local + : public reactor_op +{ +public: + // Credentials + std::uint32_t uid_; + std::uint32_t gid_; + + reactor_op_ext_local(const boost::system::error_code& success_ec, + perform_func_type perform_func, func_type complete_func) + : reactor_op(success_ec, perform_func, complete_func) + { + } +}; + +} // namespace detail +} // namespace asio +} // namespace boost + +#endif // BOOST_ASIO_DETAIL_REACTOR_OP_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/socket_ops_ext.hpp b/implementation/helper/1.74/boost/asio/detail/socket_ops_ext.hpp new file mode 100644 index 0000000..9285fed --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/socket_ops_ext.hpp @@ -0,0 +1,62 @@ +// +// detail/socket_ops_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_HPP +#define BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/socket_ops.hpp> + +namespace boost { +namespace asio { +namespace detail { +namespace socket_ops { + +BOOST_ASIO_DECL signed_size_type recvfrom(socket_type s, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, boost::system::error_code& ec, + boost::asio::ip::address& da); + +BOOST_ASIO_DECL size_t sync_recvfrom(socket_type s, state_type state, + buf* bufs, size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, boost::system::error_code& ec, boost::asio::ip::address& da); + +#if defined(BOOST_ASIO_HAS_IOCP) + +BOOST_ASIO_DECL void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + boost::system::error_code& ec, + boost::asio::ip::address& da); + +#else // defined(BOOST_ASIO_HAS_IOCP) + +BOOST_ASIO_DECL bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + boost::system::error_code& ec, size_t& bytes_transferred, + boost::asio::ip::address& da); + +#endif // defined(BOOST_ASIO_HAS_IOCP) + +} // namespace socket_ops +} // namespace detail +} // namespace asio +} // namespace boost + + +#if defined(BOOST_ASIO_HEADER_ONLY) +# include <boost/asio/detail/impl/socket_ops_ext.ipp> +#endif // defined(BOOST_ASIO_HEADER_ONLY) + +#endif // BOOST_EXT_ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/implementation/helper/1.74/boost/asio/detail/socket_ops_ext_local.hpp b/implementation/helper/1.74/boost/asio/detail/socket_ops_ext_local.hpp new file mode 100644 index 0000000..a937bb6 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/detail/socket_ops_ext_local.hpp @@ -0,0 +1,95 @@ +// +// detail/socket_ops_ext_local.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_LOCAL_HPP +#define BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_LOCAL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#include <boost/system/error_code.hpp> +#include <boost/asio/detail/memory.hpp> +#include <boost/asio/detail/socket_types.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace detail { +namespace socket_ops { + +BOOST_ASIO_DECL signed_size_type recv(socket_type s, buf* bufs, + size_t count, int flags, boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid); + +BOOST_ASIO_DECL size_t sync_recv(socket_type s, state_type state, buf* bufs, + size_t count, int flags, bool all_empty, boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid); + +#if defined(BOOST_ASIO_HAS_IOCP) + +BOOST_ASIO_DECL void complete_iocp_recv(state_type state, + const weak_cancel_token_type& cancel_token, bool all_empty, + boost::system::error_code& ec, size_t bytes_transferred, + std::uint32_t& uid, std::uint32_t& gid); + +#else // defined(BOOST_ASIO_HAS_IOCP) + +BOOST_ASIO_DECL bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + boost::system::error_code& ec, size_t& bytes_transferred, + std::uint32_t& uid, std::uint32_t& gid); + +#endif // defined(BOOST_ASIO_HAS_IOCP) + +BOOST_ASIO_DECL signed_size_type recvfrom(socket_type s, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid); + +BOOST_ASIO_DECL size_t sync_recvfrom(socket_type s, state_type state, + buf* bufs, size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid); + +#if defined(BOOST_ASIO_HAS_IOCP) + +BOOST_ASIO_DECL void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + boost::system::error_code& ec, + std::uint32_t& uid, std::uint32_t& gid); + +#else // defined(BOOST_ASIO_HAS_IOCP) + +BOOST_ASIO_DECL bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + boost::system::error_code& ec, size_t& bytes_transferred, + std::uint32_t& uid, std::uint32_t& gid); + +#endif // defined(BOOST_ASIO_HAS_IOCP) + + +} // namespace socket_ops +} // namespace detail +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#if defined(BOOST_ASIO_HEADER_ONLY) +# include <boost/asio/detail/impl/socket_ops_ext_local.ipp> +#endif // defined(BOOST_ASIO_HEADER_ONLY) + +#endif // BOOST_ASIO_DETAIL_SOCKET_OPS_EXT_LOCAL_HPP diff --git a/implementation/helper/1.74/boost/asio/ip/udp_ext.hpp b/implementation/helper/1.74/boost/asio/ip/udp_ext.hpp new file mode 100644 index 0000000..6ce2ac4 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/ip/udp_ext.hpp @@ -0,0 +1,115 @@ +// +// ip/udp_ext.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (C) 2016-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_boost or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_IP_UDP_EXT_HPP +#define BOOST_ASIO_IP_UDP_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> +#include <boost/asio/basic_datagram_socket_ext.hpp> +#include <boost/asio/detail/socket_types.hpp> +#include <boost/asio/ip/basic_endpoint.hpp> +#include <boost/asio/ip/basic_resolver.hpp> +#include <boost/asio/ip/basic_resolver_iterator.hpp> +#include <boost/asio/ip/basic_resolver_query.hpp> +#include <boost/asio/ip/udp.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for UDP. +/** + * The boost::asio::ip::udp_ext class contains flags necessary for UDP sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol, InternetProtocol. + */ +class udp_ext +{ +public: + /// The type of a UDP endpoint. + typedef basic_endpoint<udp> endpoint; + + /// Construct to represent the IPv4 UDP protocol. + static udp_ext v4() + { + return udp_ext(BOOST_ASIO_OS_DEF(AF_INET)); + } + + /// Construct to represent the IPv6 UDP protocol. + static udp_ext v6() + { + return udp_ext(BOOST_ASIO_OS_DEF(AF_INET6)); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return BOOST_ASIO_OS_DEF(SOCK_DGRAM); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return BOOST_ASIO_OS_DEF(IPPROTO_UDP); + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The UDP socket type. + typedef basic_datagram_socket_ext<udp> socket; + + /// The UDP resolver type. + typedef basic_resolver<udp> resolver; + + /// Compare two protocols for equality. + friend bool operator==(const udp_ext& p1, const udp_ext& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const udp_ext& p1, const udp_ext& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit udp_ext(int protocol_family) + : family_(protocol_family) + { + } + + int family_; +}; + +} // namespace ip +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // BOOST_ASIO_IP_UDP_EXT_HPP diff --git a/implementation/helper/1.74/boost/asio/local/stream_protocol_ext.hpp b/implementation/helper/1.74/boost/asio/local/stream_protocol_ext.hpp new file mode 100644 index 0000000..7c57c62 --- /dev/null +++ b/implementation/helper/1.74/boost/asio/local/stream_protocol_ext.hpp @@ -0,0 +1,93 @@ +// +// local/stream_protocol_ext.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_LOCAL_STREAM_PROTOCOL_EXT_HPP +#define BOOST_ASIO_LOCAL_STREAM_PROTOCOL_EXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include <boost/asio/detail/config.hpp> + +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) + +#include <boost/asio/basic_socket_acceptor_ext.hpp> +#include <boost/asio/basic_socket_iostream.hpp> +#include <boost/asio/basic_stream_socket_ext.hpp> +#include <boost/asio/detail/socket_types.hpp> +#include <boost/asio/local/basic_endpoint.hpp> + +#include <boost/asio/detail/push_options.hpp> + +namespace boost { +namespace asio { +namespace local { + +/// Encapsulates the flags needed for stream-oriented UNIX sockets. +/** + * The boost::asio::local::stream_protocol class contains flags necessary for + * stream-oriented UNIX domain sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class stream_protocol_ext +{ +public: + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_STREAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return 0; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return AF_UNIX; + } + + /// The type of a UNIX domain endpoint. + typedef basic_endpoint<stream_protocol_ext> endpoint; + + /// The UNIX domain socket type. + typedef basic_stream_socket_ext<stream_protocol_ext> socket; + + /// The UNIX domain acceptor type. + typedef basic_socket_acceptor_ext<stream_protocol_ext> acceptor; + +#if !defined(BOOST_ASIO_NO_IOSTREAM) + /// The UNIX domain iostream type. + typedef basic_socket_iostream<stream_protocol_ext> iostream; +#endif // !defined(BOOST_ASIO_NO_IOSTREAM) +}; + +} // namespace local +} // namespace asio +} // namespace boost + +#include <boost/asio/detail/pop_options.hpp> + +#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + // || defined(GENERATING_DOCUMENTATION) + +#endif // BOOST_ASIO_LOCAL_STREAM_PROTOCOL_EXT_HPP diff --git a/implementation/routing/include/routing_manager_impl.hpp b/implementation/routing/include/routing_manager_impl.hpp index 767d60e..b5f0ea0 100644 --- a/implementation/routing/include/routing_manager_impl.hpp +++ b/implementation/routing/include/routing_manager_impl.hpp @@ -168,6 +168,7 @@ public: void on_subscribe_ack_with_multicast( service_t _service, instance_t _instance, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_address, uint16_t _port); void on_unsubscribe_ack(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, @@ -282,15 +283,18 @@ public: bool _reliable); void on_resend_provided_events_response(pending_remote_offer_id_t _id); - bool update_security_policy_configuration(uint32_t _uid, uint32_t _gid, const std::shared_ptr<policy>& _policy, - const std::shared_ptr<payload>& _payload, const security_update_handler_t& _handler); - bool remove_security_policy_configuration(uint32_t _uid, uint32_t _gid, const security_update_handler_t& _handler); client_t find_local_client(service_t _service, instance_t _instance); - void on_security_update_response(pending_security_update_id_t _id, client_t _client); std::set<client_t> find_local_clients(service_t _service, instance_t _instance); bool is_subscribe_to_any_event_allowed(credentials_t _credentials, client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup); + bool update_security_policy_configuration(uint32_t _uid, uint32_t _gid, + const std::shared_ptr<policy> &_policy, + const std::shared_ptr<payload> &_payload, + const security_update_handler_t &_handler); + bool remove_security_policy_configuration(uint32_t _uid, uint32_t _gid, + const security_update_handler_t &_handler); + private: bool offer_service(client_t _client, service_t _service, instance_t _instance, @@ -400,29 +404,16 @@ private: std::pair<service_t, instance_t> pending_remote_offer_remove( pending_remote_offer_id_t _id); - void on_security_update_timeout( - const boost::system::error_code& _error, - pending_security_update_id_t _id, - std::shared_ptr<boost::asio::steady_timer> _timer); - - pending_security_update_id_t pending_security_update_add( - const std::unordered_set<client_t>& _clients); - - std::unordered_set<client_t> pending_security_update_get( - pending_security_update_id_t _id); - - bool pending_security_update_remove( - pending_security_update_id_t _id, client_t _client); - - bool is_pending_security_update_finished( - pending_security_update_id_t _id); - bool insert_offer_command(service_t _service, instance_t _instance, uint8_t _command, client_t _client, major_version_t _major, minor_version_t _minor); bool erase_offer_command(service_t _service, instance_t _instance); bool is_last_stop_callback(const uint32_t _callback_id); + bool insert_event_statistics(service_t _service, instance_t _instance, + method_t _method, length_t _length); + void statistics_log_timer_cbk(boost::system::error_code const & _error); + private: std::shared_ptr<routing_manager_stub> stub_; std::shared_ptr<sd::service_discovery> discovery_; @@ -488,21 +479,20 @@ private: std::mutex last_resume_mutex_; std::chrono::steady_clock::time_point last_resume_; - std::mutex pending_security_updates_mutex_; - pending_security_update_id_t pending_security_update_id_; - std::map<pending_security_update_id_t, std::unordered_set<client_t>> pending_security_updates_; - - std::recursive_mutex security_update_handlers_mutex_; - std::map<pending_security_update_id_t, security_update_handler_t> security_update_handlers_; - - std::mutex security_update_timers_mutex_; - std::map<pending_security_update_id_t, std::shared_ptr<boost::asio::steady_timer>> security_update_timers_; - std::mutex offer_serialization_mutex_; std::map<std::pair<service_t, instance_t>, std::deque<std::tuple<uint8_t, client_t, major_version_t, minor_version_t>>> offer_commands_; std::mutex callback_counts_mutex_; std::map<uint32_t, uint16_t> callback_counts_; + + std::mutex statistics_log_timer_mutex_; + boost::asio::steady_timer statistics_log_timer_; + + std::mutex message_statistics_mutex_; + std::map<std::tuple<service_t, instance_t, method_t>, + msg_statistic_t> message_statistics_; + std::tuple<service_t, instance_t, method_t> message_to_discard_; + uint32_t ignored_statistics_counter_; }; } // namespace vsomeip_v3 diff --git a/implementation/routing/include/routing_manager_proxy.hpp b/implementation/routing/include/routing_manager_proxy.hpp index 346e733..fd546a0 100644 --- a/implementation/routing/include/routing_manager_proxy.hpp +++ b/implementation/routing/include/routing_manager_proxy.hpp @@ -256,6 +256,8 @@ private: const bool client_side_logging_; const std::set<std::tuple<service_t, instance_t> > client_side_logging_filter_; + + std::mutex stop_mutex_; }; } // namespace vsomeip_v3 diff --git a/implementation/routing/include/routing_manager_stub.hpp b/implementation/routing/include/routing_manager_stub.hpp index 4a37644..4c2ff01 100644 --- a/implementation/routing/include/routing_manager_stub.hpp +++ b/implementation/routing/include/routing_manager_stub.hpp @@ -19,6 +19,8 @@ #include <boost/asio/io_service.hpp> #include <boost/asio/steady_timer.hpp> +#include <vsomeip/handler.hpp> + #include "../../endpoints/include/endpoint_host.hpp" #include "../include/routing_host.hpp" @@ -27,6 +29,7 @@ namespace vsomeip_v3 { class configuration; +struct policy; class routing_manager_stub_host; class routing_manager_stub: public routing_host, @@ -87,19 +90,31 @@ public: bool send_provided_event_resend_request(client_t _client, pending_remote_offer_id_t _id); - bool is_policy_cached(uint32_t _uid); + bool update_security_policy_configuration(uint32_t _uid, uint32_t _gid, + const std::shared_ptr<policy> &_policy, + const std::shared_ptr<payload> &_payload, + const security_update_handler_t &_handler); + bool remove_security_policy_configuration(uint32_t _uid, uint32_t _gid, + const security_update_handler_t &_handler); + void on_security_update_response(pending_security_update_id_t _id, + client_t _client); void policy_cache_add(uint32_t _uid, const std::shared_ptr<payload>& _payload); - void policy_cache_remove(uint32_t _uid); + bool is_policy_cached(uint32_t _uid); - bool send_update_security_policy_request(client_t _client, pending_security_update_id_t _update_id, - uint32_t _uid, const std::shared_ptr<payload>& _payload); - bool send_remove_security_policy_request(client_t _client, pending_security_update_id_t _update_id, - uint32_t _uid, uint32_t _gid); + bool send_update_security_policy_request(client_t _client, + pending_security_update_id_t _update_id, uint32_t _uid, + const std::shared_ptr<payload>& _payload); + bool send_remove_security_policy_request(client_t _client, + pending_security_update_id_t _update_id, uint32_t _uid, uint32_t _gid); bool send_cached_security_policies(client_t _client); + bool add_requester_policies(uid_t _uid, gid_t _gid, + const std::set<std::shared_ptr<policy> > &_policies); + void remove_requester_policies(uid_t _uid, gid_t _gid); + private: void broadcast(const std::vector<byte_t> &_command) const; @@ -150,6 +165,34 @@ private: void on_client_id_timer_expired(boost::system::error_code const &_error); + void get_requester_policies(uid_t _uid, gid_t _gid, + std::set<std::shared_ptr<policy> > &_policies) const; + bool send_requester_policies(const std::unordered_set<client_t> &_clients, + const std::set<std::shared_ptr<policy> > &_policies); + + void on_security_update_timeout( + const boost::system::error_code &_error, + pending_security_update_id_t _id, + std::shared_ptr<boost::asio::steady_timer> _timer); + + pending_security_update_id_t pending_security_update_add( + const std::unordered_set<client_t> &_clients); + + std::unordered_set<client_t> pending_security_update_get( + pending_security_update_id_t _id); + + bool pending_security_update_remove( + pending_security_update_id_t _id, client_t _client); + + bool is_pending_security_update_finished( + pending_security_update_id_t _id); + + void add_pending_security_update_handler( + pending_security_update_id_t _id, + security_update_handler_t _handler); + void add_pending_security_update_timer( + pending_security_update_id_t _id); + private: routing_manager_stub_host *host_; boost::asio::io_service &io_; @@ -190,10 +233,25 @@ private: std::map<client_t, std::vector<byte_t>> offered_services_info_; std::map<client_t, std::vector<byte_t>> client_credentials_info_; + std::mutex pending_security_updates_mutex_; + pending_security_update_id_t pending_security_update_id_; + std::map<pending_security_update_id_t, std::unordered_set<client_t>> pending_security_updates_; + + std::recursive_mutex security_update_handlers_mutex_; + std::map<pending_security_update_id_t, security_update_handler_t> security_update_handlers_; + + std::mutex security_update_timers_mutex_; + std::map<pending_security_update_id_t, std::shared_ptr<boost::asio::steady_timer>> security_update_timers_; std::mutex updated_security_policies_mutex_; std::map<uint32_t, std::shared_ptr<payload>> updated_security_policies_; + mutable std::mutex requester_policies_mutex_; + std::map<uint32_t, + std::map<uint32_t, + std::set<std::shared_ptr<policy> > + > + > requester_policies_; }; } // namespace vsomeip_v3 diff --git a/implementation/routing/include/routing_manager_stub_host.hpp b/implementation/routing/include/routing_manager_stub_host.hpp index b89134d..4707948 100644 --- a/implementation/routing/include/routing_manager_stub_host.hpp +++ b/implementation/routing/include/routing_manager_stub_host.hpp @@ -7,8 +7,8 @@ #define VSOMEIP_V3_ROUTING_MANAGER_STUB_HOST_ #include <boost/asio/io_service.hpp> -#include <vsomeip/handler.hpp> +#include <vsomeip/handler.hpp> #include "types.hpp" namespace vsomeip_v3 { @@ -97,8 +97,6 @@ public: virtual void on_resend_provided_events_response(pending_remote_offer_id_t _id) = 0; - virtual void on_security_update_response(pending_security_update_id_t _id, client_t _client) = 0; - virtual client_t find_local_client(service_t _service, instance_t _instance) = 0; virtual std::set<client_t> find_local_clients(service_t _service, instance_t _instance) = 0; diff --git a/implementation/routing/include/types.hpp b/implementation/routing/include/types.hpp index 389b226..c301b33 100644 --- a/implementation/routing/include/types.hpp +++ b/implementation/routing/include/types.hpp @@ -50,6 +50,11 @@ enum class remote_subscription_state_e : std::uint8_t { typedef std::uint16_t remote_subscription_id_t; typedef std::uint32_t pending_remote_offer_id_t; +struct msg_statistic_t { + uint32_t counter_; + length_t avg_length_; +}; + } // namespace vsomeip_v3 diff --git a/implementation/routing/src/routing_manager_base.cpp b/implementation/routing/src/routing_manager_base.cpp index 5b1149e..e187d93 100644 --- a/implementation/routing/src/routing_manager_base.cpp +++ b/implementation/routing/src/routing_manager_base.cpp @@ -176,7 +176,10 @@ void routing_manager_base::release_service(client_t _client, if (found_service != local_services_history_.end()) { auto found_instance = found_service->second.find(_instance); if (found_instance != found_service->second.end()) { - local_services_history_.erase(_service); + found_service->second.erase(_instance); + if (found_service->second.empty()) { + local_services_history_.erase(_service); + } } } } @@ -1136,10 +1139,11 @@ bool routing_manager_base::send_local_notification(client_t _client, _data[VSOMEIP_METHOD_POS_MAX]); service_t its_service = VSOMEIP_BYTES_TO_WORD( _data[VSOMEIP_SERVICE_POS_MIN], _data[VSOMEIP_SERVICE_POS_MAX]); + std::shared_ptr<event> its_event = find_event(its_service, _instance, its_method); if (its_event && !its_event->is_shadow()) { - for (auto its_client : its_event->get_subscribers()) { + // local if (its_client == VSOMEIP_ROUTING_CLIENT) { has_remote = true; @@ -1150,6 +1154,7 @@ bool routing_manager_base::send_local_notification(client_t _client, has_local = true; } #endif + std::shared_ptr<endpoint> its_local_target = ep_mgr_->find_local(its_client); if (its_local_target) { send_local(its_local_target, _client, _data, _size, diff --git a/implementation/routing/src/routing_manager_impl.cpp b/implementation/routing/src/routing_manager_impl.cpp index f141c57..74e556c 100644 --- a/implementation/routing/src/routing_manager_impl.cpp +++ b/implementation/routing/src/routing_manager_impl.cpp @@ -83,7 +83,8 @@ routing_manager_impl::routing_manager_impl(routing_manager_host *_host) : ep_mgr_impl_(std::make_shared<endpoint_manager_impl>(this, io_, configuration_)), pending_remote_offer_id_(0), last_resume_(std::chrono::steady_clock::now().min()), - pending_security_update_id_(0) + statistics_log_timer_(_host->get_io()), + ignored_statistics_counter_(0) { } @@ -212,6 +213,15 @@ void routing_manager_impl::start() { std::bind(&routing_manager_impl::status_log_timer_cbk, this, std::placeholders::_1)); } + + if (configuration_->log_statistics()) { + std::lock_guard<std::mutex> its_lock(statistics_log_timer_mutex_); + boost::system::error_code ec; + statistics_log_timer_.expires_from_now(std::chrono::seconds(0), ec); + statistics_log_timer_.async_wait( + std::bind(&routing_manager_impl::statistics_log_timer_cbk, this, + std::placeholders::_1)); + } } void routing_manager_impl::stop() { @@ -255,13 +265,20 @@ void routing_manager_impl::stop() { boost::system::error_code ec; status_log_timer_.cancel(ec); } + + { + std::lock_guard<std::mutex> its_lock(statistics_log_timer_mutex_); + boost::system::error_code ec; + statistics_log_timer_.cancel(ec); + } + host_->on_state(state_type_e::ST_DEREGISTERED); if (discovery_) discovery_->stop(); stub_->stop(); - for (auto client: ep_mgr_->get_connected_clients()) { + for (const auto client : ep_mgr_->get_connected_clients()) { if (client != VSOMEIP_ROUTING_CLIENT) { remove_local(client, true); } @@ -396,11 +413,14 @@ bool routing_manager_impl::offer_service(client_t _client, && ps.major_ == _major) { insert_subscription(ps.service_, ps.instance_, ps.eventgroup_, ps.event_, client_, &its_already_subscribed_events); +#if 0 VSOMEIP_ERROR << __func__ << ": event=" << std::hex << ps.service_ << "." << std::hex << ps.instance_ << "." - << std::hex << ps.event_; } + << std::hex << ps.event_; +#endif + } } send_pending_subscriptions(_service, _instance, _major); @@ -784,7 +804,8 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, // TODO: Find out how to handle session id here is_sent = deliver_message(_data, _size, _instance, _reliable, VSOMEIP_ROUTING_CLIENT, _credentials, _status_check); } else { - e2e_buffer outputBuffer; + e2e_buffer its_buffer; + if (e2e_provider_) { if ( !is_service_discovery) { service_t its_service = VSOMEIP_BYTES_TO_WORD( @@ -793,12 +814,18 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, _data[VSOMEIP_METHOD_POS_MIN], _data[VSOMEIP_METHOD_POS_MAX]); #ifndef ANDROID if (e2e_provider_->is_protected({its_service, its_method})) { - outputBuffer.assign(_data, _data + VSOMEIP_PAYLOAD_POS); - e2e_buffer inputBuffer(_data + VSOMEIP_PAYLOAD_POS, _data +_size); - e2e_provider_->protect({its_service, its_method}, inputBuffer); - outputBuffer.resize(inputBuffer.size() + VSOMEIP_PAYLOAD_POS); - std::copy(inputBuffer.begin(), inputBuffer.end(), outputBuffer.begin() + VSOMEIP_PAYLOAD_POS); - _data = outputBuffer.data(); + // Find out where the protected area starts + size_t its_base = e2e_provider_->get_protection_base({its_service, its_method}); + + // Build a corresponding buffer + its_buffer.assign(_data + its_base, _data + _size); + + e2e_provider_->protect({ its_service, its_method }, its_buffer, _instance); + + // Prepend header + its_buffer.insert(its_buffer.begin(), _data, _data + its_base); + + _data = its_buffer.data(); } #endif } @@ -979,7 +1006,7 @@ bool routing_manager_impl::send_to( if (its_serializer->serialize(_message.get())) { const byte_t *its_data = its_serializer->get_data(); length_t its_size = its_serializer->get_size(); - e2e_buffer its_output_buffer; + e2e_buffer its_buffer; if (e2e_provider_) { service_t its_service = VSOMEIP_BYTES_TO_WORD( its_data[VSOMEIP_SERVICE_POS_MIN], @@ -988,14 +1015,12 @@ bool routing_manager_impl::send_to( its_data[VSOMEIP_METHOD_POS_MIN], its_data[VSOMEIP_METHOD_POS_MAX]); #ifndef ANDROID - if(e2e_provider_->is_protected({its_service, its_method})) { - its_output_buffer.assign(its_data, its_data + VSOMEIP_PAYLOAD_POS); - e2e_buffer its_input_buffer(its_data + VSOMEIP_PAYLOAD_POS, its_data + its_size); - e2e_provider_->protect({its_service, its_method}, its_input_buffer); - its_output_buffer.resize(its_input_buffer.size() + VSOMEIP_PAYLOAD_POS); - std::copy(its_input_buffer.begin(), its_input_buffer.end(), - its_output_buffer.begin() + VSOMEIP_PAYLOAD_POS); - its_data = its_output_buffer.data(); + if (e2e_provider_->is_protected({its_service, its_method})) { + auto its_base = e2e_provider_->get_protection_base({its_service, its_method}); + its_buffer.assign(its_data + its_base, its_data + its_size); + e2e_provider_->protect({its_service, its_method}, its_buffer, _message->get_instance()); + its_buffer.insert(its_buffer.begin(), its_data, its_data + its_base); + its_data = its_buffer.data(); } #endif } @@ -1326,7 +1351,11 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, } } } else { - its_instance = ep_mgr_impl_->find_instance(its_service, _receiver); + if(_destination.is_multicast()) { + its_instance = ep_mgr_impl_->find_instance_multicast(its_service, _remote_address); + } else { + its_instance = ep_mgr_impl_->find_instance(its_service, _receiver); + } if (its_instance == 0xFFFF) { its_method = VSOMEIP_BYTES_TO_WORD( _data[VSOMEIP_METHOD_POS_MIN], @@ -1403,17 +1432,19 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, _data[VSOMEIP_METHOD_POS_MIN], _data[VSOMEIP_METHOD_POS_MAX]); #ifndef ANDROID - if( e2e_provider_->is_checked({its_service, its_method})) { - e2e_buffer inputBuffer(_data + VSOMEIP_PAYLOAD_POS, _data + _size); - e2e_provider_->check({its_service, its_method}, inputBuffer, its_check_status); - - if ( its_check_status != e2e::profile_interface::generic_check_status::E2E_OK ) { - VSOMEIP_INFO << std::hex << "E2E protection: CRC check failed for service: " << its_service << " method: " << its_method; + if (e2e_provider_->is_checked({its_service, its_method})) { + auto its_base = e2e_provider_->get_protection_base({its_service, its_method}); + e2e_buffer its_buffer(_data + its_base, _data + _size); + e2e_provider_->check({its_service, its_method}, + its_buffer, its_instance, its_check_status); + + if (its_check_status != e2e::profile_interface::generic_check_status::E2E_OK) { + VSOMEIP_INFO << "E2E protection: CRC check failed for service: " + << std::hex << its_service << " method: " << its_method; } } #endif } - // Common way of message handling #ifdef USE_DLT is_forwarded = @@ -1888,6 +1919,13 @@ bool routing_manager_impl::deliver_notification( } } + // incoming events statistics + (void) insert_event_statistics( + _service, + _instance, + its_event_id, + utility::get_payload_size(_data, _length)); + if (its_event->get_type() != event_type_e::ET_SELECTIVE_EVENT) { for (const auto its_local_client : its_event->get_subscribers()) { if (its_local_client == host_->get_client()) { @@ -2035,11 +2073,12 @@ void routing_manager_impl::init_service_info( const bool is_someip = configuration_->is_someip(_service, _instance); uint16_t its_reliable_port = configuration_->get_reliable_port( _service, _instance); + bool _is_found(false); if (ILLEGAL_PORT != its_reliable_port) { std::shared_ptr<endpoint> its_reliable_endpoint = ep_mgr_impl_->find_or_create_server_endpoint( its_reliable_port, true, is_someip, _service, - _instance); + _instance, _is_found); if (its_reliable_endpoint) { its_info->set_endpoint(its_reliable_endpoint, true); } @@ -2050,7 +2089,7 @@ void routing_manager_impl::init_service_info( std::shared_ptr<endpoint> its_unreliable_endpoint = ep_mgr_impl_->find_or_create_server_endpoint( its_unreliable_port, false, is_someip, _service, - _instance); + _instance, _is_found); if (its_unreliable_endpoint) { its_info->set_endpoint(its_unreliable_endpoint, false); } @@ -2570,10 +2609,15 @@ routing_manager_impl::expire_subscriptions( // Note: get_remote_subscription delivers a copied // set of subscriptions. Thus, its is possible to // to remove them within the loop. - const auto its_ep_definition = - (_reliable) ? its_subscription->get_reliable() : + auto its_ep_definition = (_reliable) ? + its_subscription->get_reliable() : its_subscription->get_unreliable(); + if (!its_ep_definition && expire_all) + its_ep_definition = (!_reliable) ? + its_subscription->get_reliable() : + its_subscription->get_unreliable(); + if (its_ep_definition && its_ep_definition->get_address() == _address && (expire_all || @@ -2583,7 +2627,7 @@ routing_manager_impl::expire_subscriptions( // TODO: Check whether subscriptions to different hosts are valid. // IF yes, we probably need to simply reset the corresponding // endpoint instead of removing the subscription... - VSOMEIP_ERROR << __func__ + VSOMEIP_INFO << __func__ << ": removing subscription to " << std::hex << its_info->get_service() << "." << std::hex << its_info->get_instance() << "." @@ -2591,21 +2635,23 @@ routing_manager_impl::expire_subscriptions( << " from target " << its_ep_definition->get_address() << ":" << std::dec << its_ep_definition->get_port() - << " reliable=" << _reliable; + << " reliable=" + << std::boolalpha << its_ep_definition->is_reliable(); if (expire_all) { - const auto its_ep_definition2 = - (!_reliable) ? its_subscription->get_reliable() : - its_subscription->get_unreliable(); - if (its_ep_definition2) { - VSOMEIP_ERROR << __func__ + its_ep_definition = (!its_ep_definition->is_reliable()) ? + its_subscription->get_reliable() : + its_subscription->get_unreliable(); + if (its_ep_definition) { + VSOMEIP_INFO << __func__ << ": removing subscription to " << std::hex << its_info->get_service() << "." << std::hex << its_info->get_instance() << "." << std::hex << its_info->get_eventgroup() << " from target " - << its_ep_definition2->get_address() << ":" - << std::dec << its_ep_definition2->get_port() - << " reliable=" << !_reliable; + << its_ep_definition->get_address() << ":" + << std::dec << its_ep_definition->get_port() + << " reliable=" + << std::boolalpha << its_ep_definition->is_reliable(); } } on_remote_unsubscribe(its_subscription); @@ -2789,9 +2835,10 @@ void routing_manager_impl::on_remote_unsubscribe( void routing_manager_impl::on_subscribe_ack_with_multicast( service_t _service, instance_t _instance, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_address, uint16_t _port) { ep_mgr_impl_->find_or_create_multicast_endpoint(_service, - _instance, _address, _port); + _instance, _sender, _address, _port); } void routing_manager_impl::on_subscribe_ack(client_t _client, @@ -4164,307 +4211,6 @@ void routing_manager_impl::on_resend_provided_events_response( } } -void routing_manager_impl::on_security_update_timeout( - const boost::system::error_code& _error, - pending_security_update_id_t _id, - std::shared_ptr<boost::asio::steady_timer> _timer) { - (void)_timer; - if (_error) { - // timer was cancelled - return; - } - security_update_state_e its_state = security_update_state_e::SU_UNKNOWN_USER_ID; - std::unordered_set<client_t> its_missing_clients = pending_security_update_get(_id); - { - // erase timer - std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); - security_update_timers_.erase(_id); - } - { - // print missing responses and check if some clients did not respond because they already disconnected - if (!its_missing_clients.empty()) { - for (auto its_client : its_missing_clients) { - VSOMEIP_INFO << __func__ << ": Client 0x" << std::hex << its_client - << " did not respond to the policy update / removal with ID: 0x" << std::hex << _id; - if (!find_local(its_client)) { - VSOMEIP_INFO << __func__ << ": Client 0x" << std::hex << its_client - << " is not connected anymore, do not expect answer for policy update / removal with ID: 0x" - << std::hex << _id; - pending_security_update_remove(_id, its_client); - } - } - } - - its_missing_clients = pending_security_update_get(_id); - if (its_missing_clients.empty()) { - VSOMEIP_INFO << __func__ << ": Received all responses for " - "security update/removal ID: 0x" << std::hex << _id; - its_state = security_update_state_e::SU_SUCCESS; - } - { - // erase pending security update - std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); - pending_security_updates_.erase(_id); - } - - // call handler with error on timeout or with SUCCESS if missing clients are not connected - std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); - const auto found_handler = security_update_handlers_.find(_id); - if (found_handler != security_update_handlers_.end()) { - found_handler->second(its_state); - security_update_handlers_.erase(found_handler); - } else { - VSOMEIP_WARNING << __func__ << ": Callback not found for security update / removal with ID: 0x" - << std::hex << _id; - } - } -} - -bool routing_manager_impl::update_security_policy_configuration( - uint32_t _uid, uint32_t _gid, - const std::shared_ptr<policy>& _policy, - const std::shared_ptr<payload>& _payload, - const security_update_handler_t& _handler) { - bool ret(true); - // cache security policy payload for later distribution to new registering clients - stub_->policy_cache_add(_uid, _payload); - - // update security policy from configuration - security::get()->update_security_policy(_uid, _gid, _policy); - - // determine currently connected clients - std::unordered_set<client_t> its_clients_to_inform = ep_mgr_impl_->get_connected_clients(); - - // add handler - pending_security_update_id_t its_id; - if (!its_clients_to_inform.empty()) { - its_id = pending_security_update_add(its_clients_to_inform); - { - std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); - security_update_handlers_[its_id] = _handler; - } - - { - std::shared_ptr<boost::asio::steady_timer> its_timer = - std::make_shared<boost::asio::steady_timer>(io_); - boost::system::error_code ec; - its_timer->expires_from_now(std::chrono::milliseconds(3000), ec); - if (!ec) { - its_timer->async_wait( - std::bind( - &routing_manager_impl::on_security_update_timeout, - std::static_pointer_cast<routing_manager_impl>( - shared_from_this()), - std::placeholders::_1, its_id, its_timer)); - } else { - VSOMEIP_ERROR << __func__ << ": timer creation: " << ec.message(); - } - std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); - security_update_timers_[its_id] = its_timer; - } - - // trigger all currently connected clients to update the security policy - uint32_t sent_counter(0); - uint32_t its_tranche = - uint32_t(its_clients_to_inform.size() >= 10 ? (its_clients_to_inform.size() / 10) : 1); - VSOMEIP_INFO << __func__ << ": Informing [" << std::dec << its_clients_to_inform.size() - << "] currently connected clients about policy update for UID: " - << std::dec << _uid << " with update ID: 0x" << std::hex << its_id; - for (auto its_client : its_clients_to_inform) { - if (!stub_->send_update_security_policy_request(its_client, its_id, _uid, _payload)) { - VSOMEIP_INFO << __func__ << ": Couldn't send update security policy " - << "request to client 0x" << std::hex << std::setw(4) - << std::setfill('0') << its_client << " policy UID: " - << std::hex << std::setw(4) << std::setfill('0') << _uid << " GID: " - << std::hex << std::setw(4) << std::setfill('0') << _gid - << " with update ID: 0x" << std::hex << its_id - << " as client already disconnected"; - // remove client from expected answer list - pending_security_update_remove(its_id, its_client); - } - sent_counter++; - // Prevent burst - if (sent_counter % its_tranche == 0) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - } else { - // if routing manager has no client call the handler directly - _handler(security_update_state_e::SU_SUCCESS); - } - return ret; -} - -bool routing_manager_impl::remove_security_policy_configuration( - uint32_t _uid, uint32_t _gid, const security_update_handler_t& _handler) { - bool ret(true); - - // remove security policy from configuration (only if there was a updateACL call before) - if (stub_->is_policy_cached(_uid)) { - if (!security::get()->remove_security_policy(_uid, _gid)) { - _handler(security_update_state_e::SU_UNKNOWN_USER_ID); - ret = false; - } else { - // remove policy from cache to prevent sending it to registering clients - stub_->policy_cache_remove(_uid); - - // add handler - pending_security_update_id_t its_id; - - // determine currently connected clients - std::unordered_set<client_t> its_clients_to_inform = ep_mgr_impl_->get_connected_clients(); - - if (!its_clients_to_inform.empty()) { - its_id = pending_security_update_add(its_clients_to_inform); - { - std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); - security_update_handlers_[its_id] = _handler; - } - - { - std::shared_ptr<boost::asio::steady_timer> its_timer = - std::make_shared<boost::asio::steady_timer>(io_); - boost::system::error_code ec; - its_timer->expires_from_now(std::chrono::milliseconds(3000), ec); - if (!ec) { - its_timer->async_wait( - std::bind( - &routing_manager_impl::on_security_update_timeout, - std::static_pointer_cast<routing_manager_impl>( - shared_from_this()), - std::placeholders::_1, its_id, its_timer)); - } else { - VSOMEIP_ERROR << __func__ << ": timer creation: " << ec.message(); - } - std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); - security_update_timers_[its_id] = its_timer; - } - - // trigger all clients to remove the security policy - uint32_t sent_counter(0); - uint32_t its_tranche = - uint32_t(its_clients_to_inform.size() >= 10 ? (its_clients_to_inform.size() / 10) : 1); - VSOMEIP_INFO << __func__ << ": Informing [" << std::dec << its_clients_to_inform.size() - << "] currently connected clients about policy removal for UID: " - << std::dec << _uid << " with update ID: " << its_id; - for (auto its_client : its_clients_to_inform) { - if (!stub_->send_remove_security_policy_request(its_client, its_id, _uid, _gid)) { - VSOMEIP_INFO << __func__ << ": Couldn't send remove security policy " - << "request to client 0x" << std::hex << std::setw(4) - << std::setfill('0') << its_client << " policy UID: " - << std::hex << std::setw(4) << std::setfill('0') << _uid << " GID: " - << std::hex << std::setw(4) << std::setfill('0') << _gid - << " with update ID: 0x" << std::hex << its_id - << " as client already disconnected"; - // remove client from expected answer list - pending_security_update_remove(its_id, its_client); - } - sent_counter++; - // Prevent burst - if (sent_counter % its_tranche == 0) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - } else { - // if routing manager has no client call the handler directly - _handler(security_update_state_e::SU_SUCCESS); - } - } - } - else { - _handler(security_update_state_e::SU_UNKNOWN_USER_ID); - ret = false; - } - return ret; -} - -pending_security_update_id_t routing_manager_impl::pending_security_update_add( - const std::unordered_set<client_t>& _clients) { - std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); - if (++pending_security_update_id_ == 0) { - pending_security_update_id_++; - } - pending_security_updates_[pending_security_update_id_] = _clients; - return pending_security_update_id_; -} - -std::unordered_set<client_t> routing_manager_impl::pending_security_update_get( - pending_security_update_id_t _id) { - std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); - std::unordered_set<client_t> its_missing_clients; - auto found_si = pending_security_updates_.find(_id); - if (found_si != pending_security_updates_.end()) { - its_missing_clients = pending_security_updates_[_id]; - } - return its_missing_clients; -} - -bool routing_manager_impl::pending_security_update_remove( - pending_security_update_id_t _id, client_t _client) { - std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); - auto found_si = pending_security_updates_.find(_id); - if (found_si != pending_security_updates_.end()) { - if (found_si->second.erase(_client)) { - return true; - } - } - return false; -} - -bool routing_manager_impl::is_pending_security_update_finished( - pending_security_update_id_t _id) { - std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); - bool ret(false); - auto found_si = pending_security_updates_.find(_id); - if (found_si != pending_security_updates_.end()) { - if (!found_si->second.size()) { - ret = true; - } - } - if (ret) { - pending_security_updates_.erase(_id); - } - return ret; -} - -void routing_manager_impl::on_security_update_response( - pending_security_update_id_t _id, client_t _client) { - if (pending_security_update_remove(_id, _client)) { - if (is_pending_security_update_finished(_id)) { - // cancel timeout timer - { - std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); - auto found_timer = security_update_timers_.find(_id); - if (found_timer != security_update_timers_.end()) { - boost::system::error_code ec; - found_timer->second->cancel(ec); - security_update_timers_.erase(found_timer); - } else { - VSOMEIP_WARNING << __func__ << ": Received all responses " - "for security update/removal ID: 0x" - << std::hex << _id << " but timeout already happened"; - } - } - - // call handler - { - std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); - auto found_handler = security_update_handlers_.find(_id); - if (found_handler != security_update_handlers_.end()) { - found_handler->second(security_update_state_e::SU_SUCCESS); - security_update_handlers_.erase(found_handler); - VSOMEIP_INFO << __func__ << ": Received all responses for " - "security update/removal ID: 0x" << std::hex << _id; - } else { - VSOMEIP_WARNING << __func__ << ": Received all responses " - "for security update/removal ID: 0x" - << std::hex << _id << " but didn't find handler"; - } - } - } - } -} - void routing_manager_impl::print_stub_status() const { stub_->print_endpoint_status(); } @@ -4557,4 +4303,132 @@ routing_manager_impl::send_unsubscription(client_t _offering_client, } } +bool +routing_manager_impl::update_security_policy_configuration( + uint32_t _uid, uint32_t _gid, + const std::shared_ptr<policy> &_policy, + const std::shared_ptr<payload> &_payload, + const security_update_handler_t &_handler) { + + if (stub_) + return stub_->update_security_policy_configuration(_uid, _gid, + _policy, _payload, _handler); + + return (false); +} + +bool +routing_manager_impl::remove_security_policy_configuration( + uint32_t _uid, uint32_t _gid, + const security_update_handler_t &_handler) { + + if (stub_) + return stub_->remove_security_policy_configuration(_uid, _gid, + _handler); + + return (false); +} + +bool routing_manager_impl::insert_event_statistics(service_t _service, instance_t _instance, + method_t _method, length_t _length) { + + static uint32_t its_max_messages = configuration_->get_statistics_max_messages(); + std::lock_guard<std::mutex> its_lock(message_statistics_mutex_); + const auto its_tuple = std::make_tuple(_service, _instance, _method); + const auto its_main_s = message_statistics_.find(its_tuple); + if (its_main_s != message_statistics_.end()) { + // increase counter and calculate moving avergae for payload length + its_main_s->second.avg_length_ = + (its_main_s->second.avg_length_ * its_main_s->second.counter_ + _length) / + (its_main_s->second.counter_ + 1); + its_main_s->second.counter_++; + + if (its_tuple == message_to_discard_) { + // check list for entry with least counter value + uint32_t its_min_count(0xFFFFFFFF); + auto its_tuple_to_discard = std::make_tuple(0xFFFF, 0xFFFF, 0xFFFF); + for (const auto it : message_statistics_) { + if (it.second.counter_ < its_min_count) { + its_min_count = it.second.counter_; + its_tuple_to_discard = it.first; + } + } + if (its_min_count != 0xFFFF + && its_min_count < its_main_s->second.counter_) { + // update message to discard with current message + message_to_discard_ = its_tuple; + } + } + } else { + if (message_statistics_.size() < its_max_messages) { + message_statistics_[its_tuple] = {1, _length}; + message_to_discard_ = its_tuple; + } else { + // no slot empty + const auto it = message_statistics_.find(message_to_discard_); + if (it != message_statistics_.end() + && it->second.counter_ == 1) { + message_statistics_.erase(message_to_discard_); + message_statistics_[its_tuple] = {1, _length}; + message_to_discard_ = its_tuple; + } else { + // ignore message + ignored_statistics_counter_++; + return false; + } + } + } + return true; +} + +void routing_manager_impl::statistics_log_timer_cbk(boost::system::error_code const & _error) { + if (!_error) { + static uint32_t its_interval = configuration_->get_statistics_interval(); + its_interval = its_interval >= 1000 ? its_interval : 1000; + static uint32_t its_min_freq = configuration_->get_statistics_min_freq(); + std::stringstream its_log; + { + std::lock_guard<std::mutex> its_lock(message_statistics_mutex_); + for (const auto s : message_statistics_) { + if (s.second.counter_ / (its_interval / 1000) >= its_min_freq) { + uint16_t its_subscribed(0); + std::shared_ptr<event> its_event = find_event(std::get<0>(s.first), std::get<1>(s.first), std::get<2>(s.first)); + if (its_event) { + if (!its_event->is_provided()) { + its_subscribed = static_cast<std::uint16_t>(its_event->get_subscribers().size()); + } + } + its_log << std::hex << std::setw(4) << std::setfill('0') + << std::get<0>(s.first) << "." + << std::get<1>(s.first) << "." + << std::get<2>(s.first) << ": #=" + << std::dec << s.second.counter_ << " L=" + << s.second.avg_length_ << " S=" + << std::dec << its_subscribed << ", "; + } + } + + if (ignored_statistics_counter_) { + its_log << std::dec << " #ignored: " << ignored_statistics_counter_; + } + + message_statistics_.clear(); + message_to_discard_ = std::make_tuple(0x00, 0x00, 0x00); + ignored_statistics_counter_ = 0; + } + + if (its_log.str().length() > 0) { + VSOMEIP_INFO << "Received events statistics: [" << its_log.str() << "]"; + } + + { + std::lock_guard<std::mutex> its_lock(statistics_log_timer_mutex_); + statistics_log_timer_.expires_from_now(std::chrono::milliseconds(its_interval)); + statistics_log_timer_.async_wait( + std::bind(&routing_manager_impl::statistics_log_timer_cbk, + this, std::placeholders::_1)); + } + } +} + } // namespace vsomeip_v3 diff --git a/implementation/routing/src/routing_manager_proxy.cpp b/implementation/routing/src/routing_manager_proxy.cpp index 3b95a2d..410559b 100644 --- a/implementation/routing/src/routing_manager_proxy.cpp +++ b/implementation/routing/src/routing_manager_proxy.cpp @@ -125,7 +125,7 @@ void routing_manager_proxy::stop() { sender_ = nullptr; } - for (auto client: ep_mgr_->get_connected_clients()) { + for (const auto client : ep_mgr_->get_connected_clients()) { if (client != VSOMEIP_ROUTING_CLIENT) { remove_local(client, true); } @@ -202,12 +202,17 @@ void routing_manager_proxy::stop_offer_service(client_t _client, (void)_client; - routing_manager_base::stop_offer_service(_client, _service, _instance, _major, _minor); - clear_remote_subscriber_count(_service, _instance); + { + // Hold the mutex to ensure no placeholder event is created inbetween. + std::lock_guard<std::mutex> its_lock(stop_mutex_); + + routing_manager_base::stop_offer_service(_client, _service, _instance, _major, _minor); + clear_remote_subscriber_count(_service, _instance); - // Reliable/Unreliable unimportant as routing_proxy does not - // create server endpoints which needs to be freed - clear_service_info(_service, _instance, false); + // Note: The last argument does not matter here as a proxy + // does not manage endpoints to the external network. + clear_service_info(_service, _instance, false); + } { std::lock_guard<std::mutex> its_lock(state_mutex_); @@ -876,6 +881,7 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, client_t its_subscriber; remote_subscription_id_t its_subscription_id(PENDING_SUBSCRIPTION_ID); std::uint32_t its_remote_subscriber_count(0); + bool is_internal_policy_update(false); std::uint32_t its_sender_uid = std::get<0>(_credentials); std::uint32_t its_sender_gid = std::get<1>(_credentials); @@ -1389,6 +1395,9 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, << its_client << ")"; break; } + case VSOMEIP_UPDATE_SECURITY_POLICY_INT: + is_internal_policy_update = true; + /* Fallthrough */ case VSOMEIP_UPDATE_SECURITY_POLICY: { if (_size < VSOMEIP_COMMAND_HEADER_SIZE + sizeof(pending_security_update_id_t) || _size - VSOMEIP_COMMAND_HEADER_SIZE != its_length) { @@ -1397,23 +1406,33 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, } if (!its_security->is_enabled() || message_from_routing) { pending_security_update_id_t its_update_id(0); - uint32_t its_uid(0); - uint32_t its_gid(0); std::memcpy(&its_update_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], sizeof(pending_security_update_id_t)); std::shared_ptr<policy> its_policy(std::make_shared<policy>()); - const byte_t* buffer_ptr = _data + (VSOMEIP_COMMAND_PAYLOAD_POS + + const byte_t *its_policy_data = _data + (VSOMEIP_COMMAND_PAYLOAD_POS + sizeof(pending_security_update_id_t)); - uint32_t its_size = uint32_t(_size - (VSOMEIP_COMMAND_PAYLOAD_POS + uint32_t its_policy_size = uint32_t(_size - (VSOMEIP_COMMAND_PAYLOAD_POS + sizeof(pending_security_update_id_t))); - its_security->parse_policy(buffer_ptr, its_size, its_uid, its_gid, its_policy); - if (its_security->is_policy_update_allowed(its_uid, its_policy)) { - its_security->update_security_policy(its_uid, its_gid, its_policy); - send_update_security_policy_response(its_update_id); + bool is_valid = its_policy->deserialize(its_policy_data, its_policy_size); + if (is_valid) { + uint32_t its_uid; + uint32_t its_gid; + is_valid = its_policy->get_uid_gid(its_uid, its_gid); + if (is_valid) { + if (is_internal_policy_update + || its_security->is_policy_update_allowed(its_uid, its_policy)) { + its_security->update_security_policy(its_uid, its_gid, its_policy); + send_update_security_policy_response(its_update_id); + } + } else { + VSOMEIP_ERROR << "vSomeIP Security: Policy has no valid uid/gid!"; + } + } else { + VSOMEIP_ERROR << "vSomeIP Security: Policy deserialization failed!"; } } else { VSOMEIP_WARNING << "vSomeIP Security: Client 0x" << std::hex << get_client() @@ -2350,21 +2369,29 @@ bool routing_manager_proxy::create_placeholder_event_and_subscribe( service_t _service, instance_t _instance, eventgroup_t _eventgroup, event_t _notifier, client_t _client) { + std::lock_guard<std::mutex> its_lock(stop_mutex_); + bool is_inserted(false); - // we received a event which was not yet requested/offered - // create a placeholder field until someone requests/offers this event with - // full information like eventgroup, field or not etc. - std::set<eventgroup_t> its_eventgroups({ _eventgroup }); - // routing_manager_proxy: Always register with own client id and shadow = false - routing_manager_base::register_event(host_->get_client(), - _service, _instance, _notifier, - its_eventgroups, event_type_e::ET_UNKNOWN, reliability_type_e::RT_UNKNOWN, - std::chrono::milliseconds::zero(), false, true, nullptr, false, false, - true); - std::shared_ptr<event> its_event = find_event(_service, _instance, _notifier); - if (its_event) { - is_inserted = its_event->add_subscriber(_eventgroup, _client, false); + + if (find_service(_service, _instance)) { + // We received an event for an existing service which was not yet + // requested/offered. Create a placeholder field until someone + // requests/offers this event with full information like eventgroup, + // field/event, etc. + std::set<eventgroup_t> its_eventgroups({ _eventgroup }); + // routing_manager_proxy: Always register with own client id and shadow = false + routing_manager_base::register_event(host_->get_client(), + _service, _instance, _notifier, + its_eventgroups, event_type_e::ET_UNKNOWN, reliability_type_e::RT_UNKNOWN, + std::chrono::milliseconds::zero(), false, true, nullptr, false, false, + true); + + std::shared_ptr<event> its_event = find_event(_service, _instance, _notifier); + if (its_event) { + is_inserted = its_event->add_subscriber(_eventgroup, _client, false); + } } + return is_inserted; } @@ -2561,7 +2588,8 @@ void routing_manager_proxy::on_update_security_credentials(const byte_t *_data, uint32_t i = 0; while ( (i + sizeof(uint32_t) + sizeof(uint32_t)) <= _size) { std::shared_ptr<policy> its_policy(std::make_shared<policy>()); - ranges_t its_uid_ranges, its_gid_ranges; + + boost::icl::interval_set<uint32_t> its_gid_set; uint32_t its_uid, its_gid; std::memcpy(&its_uid, &_data[i], sizeof(uint32_t)); @@ -2569,11 +2597,13 @@ void routing_manager_proxy::on_update_security_credentials(const byte_t *_data, std::memcpy(&its_gid, &_data[i], sizeof(uint32_t)); i += uint32_t(sizeof(uint32_t)); - its_uid_ranges.insert(std::make_pair(its_uid, its_uid)); - its_gid_ranges.insert(std::make_pair(its_gid, its_gid)); + its_gid_set.insert(its_gid); + its_policy->credentials_ += std::make_pair( + boost::icl::interval<uid_t>::closed(its_uid, its_uid), its_gid_set); its_policy->allow_who_ = true; - its_policy->ids_.insert(std::make_pair(its_uid_ranges, its_gid_ranges)); + its_policy->allow_what_ = true; + its_security->add_security_credentials(its_uid, its_gid, its_policy, get_client()); } } diff --git a/implementation/routing/src/routing_manager_stub.cpp b/implementation/routing/src/routing_manager_stub.cpp index d7a1dd2..72668ea 100644 --- a/implementation/routing/src/routing_manager_stub.cpp +++ b/implementation/routing/src/routing_manager_stub.cpp @@ -47,7 +47,8 @@ routing_manager_stub::routing_manager_stub( client_registration_running_(false), max_local_message_size_(configuration_->get_max_message_size_local()), configured_watchdog_timeout_(configuration_->get_watchdog_timeout()), - pinged_clients_timer_(io_) { + pinged_clients_timer_(io_), + pending_security_update_id_(0) { } routing_manager_stub::~routing_manager_stub() { @@ -760,7 +761,7 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, std::memcpy(&its_pending_security_update_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], sizeof(pending_security_update_id_t)); - host_->on_security_update_response(its_pending_security_update_id ,its_client); + on_security_update_response(its_pending_security_update_id ,its_client); break; } case VSOMEIP_REMOVE_SECURITY_POLICY_RESPONSE: { @@ -773,7 +774,7 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, std::memcpy(&its_pending_security_update_id, &_data[VSOMEIP_COMMAND_PAYLOAD_POS], sizeof(pending_security_update_id_t)); - host_->on_security_update_response(its_pending_security_update_id ,its_client); + on_security_update_response(its_pending_security_update_id ,its_client); break; } } @@ -787,9 +788,20 @@ void routing_manager_stub::on_register_application(client_t _client) { VSOMEIP_WARNING << "Reregistering application: " << std::hex << _client << ". Last registration might have been taken too long."; } else { - (void)host_->find_or_create_local(_client); - std::lock_guard<std::mutex> its_lock(routing_info_mutex_); - routing_info_[_client].first = 0; + endpoint = host_->find_or_create_local(_client); + { + std::lock_guard<std::mutex> its_lock(routing_info_mutex_); + routing_info_[_client].first = 0; + } + + std::pair<uid_t, gid_t> its_uid_gid; + std::set<std::shared_ptr<policy> > its_policies; + + security::get()->get_client_to_uid_gid_mapping(_client, its_uid_gid); + get_requester_policies(its_uid_gid.first, its_uid_gid.second, its_policies); + + if (!its_policies.empty()) + send_requester_policies({ _client }, its_policies); } } @@ -2153,4 +2165,408 @@ bool routing_manager_stub::send_remove_security_policy_request( client_t _client } } +bool +routing_manager_stub::add_requester_policies(uid_t _uid, gid_t _gid, + const std::set<std::shared_ptr<policy> > &_policies) { + + std::lock_guard<std::mutex> its_lock(requester_policies_mutex_); + auto found_uid = requester_policies_.find(_uid); + if (found_uid != requester_policies_.end()) { + auto found_gid = found_uid->second.find(_gid); + if (found_gid != found_uid->second.end()) { + found_gid->second.insert(_policies.begin(), _policies.end()); + } else { + found_uid->second.insert(std::make_pair(_gid, _policies)); + } + } else { + requester_policies_[_uid][_gid] = _policies; + } + + // Check whether clients with uid/gid are already registered. + // If yes, update their policy + std::unordered_set<client_t> its_clients; + security::get()->get_clients(_uid, _gid, its_clients); + + if (!its_clients.empty()) + return send_requester_policies(its_clients, _policies); + + return (true); +} + +void +routing_manager_stub::remove_requester_policies(uid_t _uid, gid_t _gid) { + + std::lock_guard<std::mutex> its_lock(requester_policies_mutex_); + auto found_uid = requester_policies_.find(_uid); + if (found_uid != requester_policies_.end()) { + found_uid->second.erase(_gid); + if (found_uid->second.empty()) + requester_policies_.erase(_uid); + } +} + +void +routing_manager_stub::get_requester_policies(uid_t _uid, gid_t _gid, + std::set<std::shared_ptr<policy> > &_policies) const { + + std::lock_guard<std::mutex> its_lock(requester_policies_mutex_); + auto found_uid = requester_policies_.find(_uid); + if (found_uid != requester_policies_.end()) { + auto found_gid = found_uid->second.find(_gid); + if (found_gid != found_uid->second.end()) + _policies = found_gid->second; + } +} + +void +routing_manager_stub::add_pending_security_update_handler( + pending_security_update_id_t _id, security_update_handler_t _handler) { + + std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); + security_update_handlers_[_id] = _handler; +} + +void +routing_manager_stub::add_pending_security_update_timer( + pending_security_update_id_t _id) { + + std::shared_ptr<boost::asio::steady_timer> its_timer + = std::make_shared<boost::asio::steady_timer>(io_); + + boost::system::error_code ec; + its_timer->expires_from_now(std::chrono::milliseconds(3000), ec); + if (!ec) { + its_timer->async_wait( + std::bind( + &routing_manager_stub::on_security_update_timeout, + shared_from_this(), + std::placeholders::_1, _id, its_timer)); + } else { + VSOMEIP_ERROR << __func__ + << "[" << std::dec << _id << "]: timer creation: " + << ec.message(); + } + std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); + security_update_timers_[_id] = its_timer; +} + +bool +routing_manager_stub::send_requester_policies(const std::unordered_set<client_t> &_clients, + const std::set<std::shared_ptr<policy> > &_policies) { + + pending_security_update_id_t its_policy_id; + + // serialize the policies and send them... + for (const auto p : _policies) { + std::vector<byte_t> its_policy_data; + if (p->serialize(its_policy_data)) { + std::vector<byte_t> its_message; + its_message.push_back(VSOMEIP_UPDATE_SECURITY_POLICY_INT); + its_message.push_back(0); + its_message.push_back(0); + + uint32_t its_policy_size = static_cast<uint32_t>(its_policy_data.size() + sizeof(uint32_t)); + its_message.push_back(VSOMEIP_LONG_BYTE0(its_policy_size)); + its_message.push_back(VSOMEIP_LONG_BYTE1(its_policy_size)); + its_message.push_back(VSOMEIP_LONG_BYTE2(its_policy_size)); + its_message.push_back(VSOMEIP_LONG_BYTE3(its_policy_size)); + + its_policy_id = pending_security_update_add(_clients); + its_message.push_back(VSOMEIP_LONG_BYTE0(its_policy_id)); + its_message.push_back(VSOMEIP_LONG_BYTE1(its_policy_id)); + its_message.push_back(VSOMEIP_LONG_BYTE2(its_policy_id)); + its_message.push_back(VSOMEIP_LONG_BYTE3(its_policy_id)); + + its_message.insert(its_message.end(), its_policy_data.begin(), its_policy_data.end()); + + for (const auto c : _clients) { + std::shared_ptr<endpoint> its_endpoint = host_->find_local(c); + if (its_endpoint) + its_endpoint->send(&its_message[0], static_cast<uint32_t>(its_message.size())); + } + } + } + + return (true); +} + +void routing_manager_stub::on_security_update_timeout( + const boost::system::error_code& _error, + pending_security_update_id_t _id, + std::shared_ptr<boost::asio::steady_timer> _timer) { + (void)_timer; + if (_error) { + // timer was cancelled + return; + } + security_update_state_e its_state = security_update_state_e::SU_UNKNOWN_USER_ID; + std::unordered_set<client_t> its_missing_clients = pending_security_update_get(_id); + { + // erase timer + std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); + security_update_timers_.erase(_id); + } + { + // print missing responses and check if some clients did not respond because they already disconnected + if (!its_missing_clients.empty()) { + for (auto its_client : its_missing_clients) { + VSOMEIP_INFO << __func__ << ": Client 0x" << std::hex << its_client + << " did not respond to the policy update / removal with ID: 0x" << std::hex << _id; + if (!host_->find_local(its_client)) { + VSOMEIP_INFO << __func__ << ": Client 0x" << std::hex << its_client + << " is not connected anymore, do not expect answer for policy update / removal with ID: 0x" + << std::hex << _id; + pending_security_update_remove(_id, its_client); + } + } + } + + its_missing_clients = pending_security_update_get(_id); + if (its_missing_clients.empty()) { + VSOMEIP_INFO << __func__ << ": Received all responses for " + "security update/removal ID: 0x" << std::hex << _id; + its_state = security_update_state_e::SU_SUCCESS; + } + { + // erase pending security update + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + pending_security_updates_.erase(_id); + } + + // call handler with error on timeout or with SUCCESS if missing clients are not connected + std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); + const auto found_handler = security_update_handlers_.find(_id); + if (found_handler != security_update_handlers_.end()) { + found_handler->second(its_state); + security_update_handlers_.erase(found_handler); + } else { + VSOMEIP_WARNING << __func__ << ": Callback not found for security update / removal with ID: 0x" + << std::hex << _id; + } + } +} + +bool routing_manager_stub::update_security_policy_configuration( + uint32_t _uid, uint32_t _gid, + const std::shared_ptr<policy> &_policy, + const std::shared_ptr<payload> &_payload, + const security_update_handler_t &_handler) { + + bool ret(true); + + // cache security policy payload for later distribution to new registering clients + policy_cache_add(_uid, _payload); + + // update security policy from configuration + security::get()->update_security_policy(_uid, _gid, _policy); + + // Build requester policies for the services offered by the new policy + std::set<std::shared_ptr<policy> > its_requesters; + security::get()->get_requester_policies(_policy, its_requesters); + + // and add them to the requester policy cache + add_requester_policies(_uid, _gid, its_requesters); + + // determine currently connected clients + std::unordered_set<client_t> its_clients_to_inform; + auto its_epm = host_->get_endpoint_manager(); + if (its_epm) + its_clients_to_inform = its_epm->get_connected_clients(); + + // add handler + pending_security_update_id_t its_id; + if (!its_clients_to_inform.empty()) { + its_id = pending_security_update_add(its_clients_to_inform); + + add_pending_security_update_handler(its_id, _handler); + add_pending_security_update_timer(its_id); + + // trigger all currently connected clients to update the security policy + uint32_t sent_counter(0); + uint32_t its_tranche = + uint32_t(its_clients_to_inform.size() >= 10 ? (its_clients_to_inform.size() / 10) : 1); + VSOMEIP_INFO << __func__ << ": Informing [" << std::dec << its_clients_to_inform.size() + << "] currently connected clients about policy update for UID: " + << std::dec << _uid << " with update ID: 0x" << std::hex << its_id; + for (auto its_client : its_clients_to_inform) { + if (!send_update_security_policy_request(its_client, its_id, _uid, _payload)) { + VSOMEIP_INFO << __func__ << ": Couldn't send update security policy " + << "request to client 0x" << std::hex << std::setw(4) + << std::setfill('0') << its_client << " policy UID: " + << std::hex << std::setw(4) << std::setfill('0') << _uid << " GID: " + << std::hex << std::setw(4) << std::setfill('0') << _gid + << " with update ID: 0x" << std::hex << its_id + << " as client already disconnected"; + // remove client from expected answer list + pending_security_update_remove(its_id, its_client); + } + sent_counter++; + // Prevent burst + if (sent_counter % its_tranche == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + } else { + // if routing manager has no client call the handler directly + _handler(security_update_state_e::SU_SUCCESS); + } + + return ret; +} + +bool routing_manager_stub::remove_security_policy_configuration( + uint32_t _uid, uint32_t _gid, const security_update_handler_t &_handler) { + + bool ret(true); + + // remove security policy from configuration (only if there was a updateACL call before) + if (is_policy_cached(_uid)) { + if (!security::get()->remove_security_policy(_uid, _gid)) { + _handler(security_update_state_e::SU_UNKNOWN_USER_ID); + ret = false; + } else { + // remove policy from cache to prevent sending it to registering clients + policy_cache_remove(_uid); + + // add handler + pending_security_update_id_t its_id; + + // determine currently connected clients + std::unordered_set<client_t> its_clients_to_inform; + auto its_epm = host_->get_endpoint_manager(); + if (its_epm) + its_clients_to_inform = its_epm->get_connected_clients(); + + if (!its_clients_to_inform.empty()) { + its_id = pending_security_update_add(its_clients_to_inform); + + add_pending_security_update_handler(its_id, _handler); + add_pending_security_update_timer(its_id); + + // trigger all clients to remove the security policy + uint32_t sent_counter(0); + uint32_t its_tranche = + uint32_t(its_clients_to_inform.size() >= 10 ? (its_clients_to_inform.size() / 10) : 1); + VSOMEIP_INFO << __func__ << ": Informing [" << std::dec << its_clients_to_inform.size() + << "] currently connected clients about policy removal for UID: " + << std::dec << _uid << " with update ID: " << its_id; + for (auto its_client : its_clients_to_inform) { + if (!send_remove_security_policy_request(its_client, its_id, _uid, _gid)) { + VSOMEIP_INFO << __func__ << ": Couldn't send remove security policy " + << "request to client 0x" << std::hex << std::setw(4) + << std::setfill('0') << its_client << " policy UID: " + << std::hex << std::setw(4) << std::setfill('0') << _uid << " GID: " + << std::hex << std::setw(4) << std::setfill('0') << _gid + << " with update ID: 0x" << std::hex << its_id + << " as client already disconnected"; + // remove client from expected answer list + pending_security_update_remove(its_id, its_client); + } + sent_counter++; + // Prevent burst + if (sent_counter % its_tranche == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + } else { + // if routing manager has no client call the handler directly + _handler(security_update_state_e::SU_SUCCESS); + } + } + } + else { + _handler(security_update_state_e::SU_UNKNOWN_USER_ID); + ret = false; + } + return ret; +} + +pending_security_update_id_t routing_manager_stub::pending_security_update_add( + const std::unordered_set<client_t>& _clients) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + if (++pending_security_update_id_ == 0) { + pending_security_update_id_++; + } + pending_security_updates_[pending_security_update_id_] = _clients; + + return pending_security_update_id_; +} + +std::unordered_set<client_t> routing_manager_stub::pending_security_update_get( + pending_security_update_id_t _id) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + std::unordered_set<client_t> its_missing_clients; + auto found_si = pending_security_updates_.find(_id); + if (found_si != pending_security_updates_.end()) { + its_missing_clients = pending_security_updates_[_id]; + } + return its_missing_clients; +} + +bool routing_manager_stub::pending_security_update_remove( + pending_security_update_id_t _id, client_t _client) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + auto found_si = pending_security_updates_.find(_id); + if (found_si != pending_security_updates_.end()) { + if (found_si->second.erase(_client)) { + return true; + } + } + return false; +} + +bool routing_manager_stub::is_pending_security_update_finished( + pending_security_update_id_t _id) { + std::lock_guard<std::mutex> its_lock(pending_security_updates_mutex_); + bool ret(false); + auto found_si = pending_security_updates_.find(_id); + if (found_si != pending_security_updates_.end()) { + if (!found_si->second.size()) { + ret = true; + } + } + if (ret) { + pending_security_updates_.erase(_id); + } + return ret; +} + +void routing_manager_stub::on_security_update_response( + pending_security_update_id_t _id, client_t _client) { + if (pending_security_update_remove(_id, _client)) { + if (is_pending_security_update_finished(_id)) { + // cancel timeout timer + { + std::lock_guard<std::mutex> its_lock(security_update_timers_mutex_); + auto found_timer = security_update_timers_.find(_id); + if (found_timer != security_update_timers_.end()) { + boost::system::error_code ec; + found_timer->second->cancel(ec); + security_update_timers_.erase(found_timer); + } else { + VSOMEIP_WARNING << __func__ << ": Received all responses " + "for security update/removal ID: 0x" + << std::hex << _id << " but timeout already happened"; + } + } + + // call handler + { + std::lock_guard<std::recursive_mutex> its_lock(security_update_handlers_mutex_); + auto found_handler = security_update_handlers_.find(_id); + if (found_handler != security_update_handlers_.end()) { + found_handler->second(security_update_state_e::SU_SUCCESS); + security_update_handlers_.erase(found_handler); + VSOMEIP_INFO << __func__ << ": Received all responses for " + "security update/removal ID: 0x" << std::hex << _id; + } else { + VSOMEIP_WARNING << __func__ << ": Received all responses " + "for security update/removal ID: 0x" + << std::hex << _id << " but didn't find handler"; + } + } + } + } +} + } // namespace vsomeip_v3 diff --git a/implementation/runtime/include/application_impl.hpp b/implementation/runtime/include/application_impl.hpp index f60c390..37df949 100644 --- a/implementation/runtime/include/application_impl.hpp +++ b/implementation/runtime/include/application_impl.hpp @@ -347,7 +347,7 @@ private: typedef std::map<major_version_t, std::map<minor_version_t, std::pair<availability_handler_t, bool>>> availability_major_minor_t; std::map<service_t, std::map<instance_t, availability_major_minor_t>> availability_; - mutable std::mutex availability_mutex_; + mutable std::recursive_mutex availability_mutex_; // Availability mutable available_t available_; @@ -434,6 +434,10 @@ private: uid_t own_uid_; gid_t own_gid_; + +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + bool has_session_handling_; +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG }; } // namespace vsomeip_v3 diff --git a/implementation/runtime/src/application_impl.cpp b/implementation/runtime/src/application_impl.cpp index 44b14fd..45ac83f 100644 --- a/implementation/runtime/src/application_impl.cpp +++ b/implementation/runtime/src/application_impl.cpp @@ -67,7 +67,11 @@ application_impl::application_impl(const std::string &_name) is_routing_manager_host_(false), stopped_called_(false), watchdog_timer_(io_), - client_side_logging_(false) { + client_side_logging_(false) +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + , has_session_handling_(true) +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG +{ own_uid_ = ANY_UID; own_gid_ = ANY_GID; #ifndef _WIN32 @@ -230,6 +234,13 @@ bool application_impl::init() { max_dispatchers_ = its_configuration->get_max_dispatchers(name_) + 1; max_dispatch_time_ = its_configuration->get_max_dispatch_time(name_); +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + has_session_handling_ = its_configuration->has_session_handling(name_); + if (!has_session_handling_) + VSOMEIP_INFO << "application: " << name_ + << " has session handling switched off!"; +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG + std::string its_routing_host = its_configuration->get_routing_host(); if (its_routing_host != "") { is_routing_manager_host_ = (its_routing_host == name_); @@ -615,7 +626,7 @@ void application_impl::unsubscribe(service_t _service, instance_t _instance, bool application_impl::is_available( service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) const { - std::lock_guard<std::mutex> its_lock(availability_mutex_); + std::lock_guard<std::recursive_mutex> its_lock(availability_mutex_); return is_available_unlocked(_service, _instance, _major, _minor); } @@ -689,7 +700,7 @@ bool application_impl::are_available( available_t &_available, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) const { - std::lock_guard<std::mutex> its_lock(availability_mutex_); + std::lock_guard<std::recursive_mutex> its_lock(availability_mutex_); return are_available_unlocked(_available, _service, _instance, _major, _minor); } @@ -868,7 +879,7 @@ void application_impl::unregister_state_handler() { void application_impl::register_availability_handler(service_t _service, instance_t _instance, availability_handler_t _handler, major_version_t _major, minor_version_t _minor) { - std::lock_guard<std::mutex> availability_lock(availability_mutex_); + std::lock_guard<std::recursive_mutex> availability_lock(availability_mutex_); if (state_ == state_type_e::ST_REGISTERED) { do_register_availability_handler(_service, _instance, _handler, _major, _minor); @@ -904,7 +915,7 @@ void application_impl::do_register_availability_handler(service_t _service, void application_impl::unregister_availability_handler(service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) { - std::lock_guard<std::mutex> its_lock(availability_mutex_); + std::lock_guard<std::recursive_mutex> its_lock(availability_mutex_); auto found_service = availability_.find(_service); if (found_service != availability_.end()) { auto found_instance = found_service->second.find(_instance); @@ -1253,11 +1264,18 @@ void application_impl::set_client(const client_t &_client) { } session_t application_impl::get_session() { + +#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG + if (!has_session_handling_) + return (0); +#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG + std::lock_guard<std::mutex> its_lock(session_mutex_); if (0 == ++session_) { // Smallest allowed session identifier session_ = 1; } + return session_; } @@ -1275,7 +1293,7 @@ boost::asio::io_service & application_impl::get_io() { void application_impl::on_state(state_type_e _state) { { - std::lock_guard<std::mutex> availability_lock(availability_mutex_); + std::lock_guard<std::recursive_mutex> availability_lock(availability_mutex_); if (state_ != _state) { state_ = _state; if (state_ == state_type_e::ST_REGISTERED) { @@ -1294,6 +1312,17 @@ void application_impl::on_state(state_type_e _state) { } } } + } else { + // Call on_availability callback on each service + for (const auto &its_service : availability_) { + for (const auto &its_instance : its_service.second) { + for (const auto &its_major : its_instance.second) { + for (const auto &its_minor : its_major.second) { + on_availability(its_service.first, its_instance.first, false, its_major.first, its_minor.first); + } + } + } + } } } } @@ -1322,7 +1351,7 @@ void application_impl::on_availability(service_t _service, instance_t _instance, bool _is_available, major_version_t _major, minor_version_t _minor) { std::vector<availability_handler_t> its_handlers; { - std::lock_guard<std::mutex> availability_lock(availability_mutex_); + std::lock_guard<std::recursive_mutex> availability_lock(availability_mutex_); if (_is_available == is_available_unlocked(_service, _instance, _major, _minor)) { return; } @@ -1860,7 +1889,7 @@ void application_impl::clear_all_handler() { } { - std::lock_guard<std::mutex> availability_lock(availability_mutex_); + std::lock_guard<std::recursive_mutex> availability_lock(availability_mutex_); availability_.clear(); } diff --git a/implementation/security/include/policy.hpp b/implementation/security/include/policy.hpp index f8727f8..82f3eb9 100644 --- a/implementation/security/include/policy.hpp +++ b/implementation/security/include/policy.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// Copyright (C) 2014-2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. @@ -9,8 +9,11 @@ #include <cstring> #include <map> #include <mutex> -#include <set> #include <utility> +#include <vector> + +#include <boost/icl/interval_map.hpp> +#include <boost/icl/interval_set.hpp> #include <vsomeip/constants.hpp> #include <vsomeip/primitive_types.hpp> @@ -18,20 +21,88 @@ namespace vsomeip_v3 { -typedef std::set<std::pair<uint32_t, uint32_t>> ranges_t; -typedef std::set<std::pair<ranges_t, ranges_t>> ids_t; +template<typename T_> +void get_bounds(const boost::icl::discrete_interval<T_> &_interval, + T_ &_lower, T_ &_upper) { + + T_ its_lower, its_upper; + + its_lower = _interval.lower(); + its_upper = _interval.upper(); + + switch (_interval.bounds().bits()) { + case boost::icl::interval_bounds::static_open: + its_lower++; + its_upper--; + break; + case boost::icl::interval_bounds::static_left_open: + its_lower++; + break; + case boost::icl::interval_bounds::static_right_open: + its_upper--; + break; + default: + ; + } + + _lower = its_lower; + _upper = its_upper; +} struct policy { policy() : allow_who_(false), allow_what_(false) {}; - ids_t ids_; + // Returns true if the policy is defined for single uid/gid pair. + // uid & gid are copied to the arguments. Otherwise, returns false. + bool get_uid_gid(uid_t &_uid, gid_t &_gid) const; + + bool deserialize_uid_gid(const byte_t * &_data, uint32_t &_size, + uid_t &_uid, gid_t &_gid) const; + bool deserialize(const byte_t * &_data, uint32_t &_size); + bool serialize(std::vector<byte_t> &_data) const; + + void print() const; + + // Members + boost::icl::interval_map<uid_t, + boost::icl::interval_set<gid_t> > credentials_; bool allow_who_; - std::map<service_t, ids_t> services_; - std::map<service_t, ranges_t> offers_; + boost::icl::interval_map<service_t, + boost::icl::interval_map<instance_t, + boost::icl::interval_set<method_t> > > requests_; + boost::icl::interval_map<service_t, + boost::icl::interval_set<instance_t> > offers_; bool allow_what_; - std::mutex mutex_; + mutable std::mutex mutex_; + +private: + bool deserialize_ids(const byte_t * &_data, uint32_t &_size, + boost::icl::interval_map<uint16_t, + boost::icl::interval_set<uint16_t> > &_ids) const; + bool deserialize_id_item_list(const byte_t * &_data, uint32_t &_size, + boost::icl::interval_set<uint16_t> &_intervals) const; + bool deserialize_id_item(const byte_t * &_data, uint32_t &_size, + uint16_t &_low, uint16_t &_high) const; + + bool deserialize_u32(const byte_t * &_data, uint32_t &_size, + uint32_t &_value) const; + bool deserialize_u16(const byte_t * &_data, uint32_t &_size, + uint16_t &_value) const; + + bool serialize_uid_gid(std::vector<byte_t> &_data) const; + void serialize_interval_set( + const boost::icl::interval_set<uint16_t> &_intervals, + std::vector<byte_t> &_data) const; + void serialize_interval( + const boost::icl::discrete_interval<uint16_t> &_interval, + std::vector<byte_t> &_data) const; + + void serialize_u32(uint32_t _value, std::vector<byte_t> &_data) const; + void serialize_u32_at(uint32_t _value, std::vector<byte_t> &_data, + size_t _pos) const; + void serialize_u16(uint16_t _value, std::vector<byte_t> &_data) const; }; } // namespace vsomeip_v3 diff --git a/implementation/security/include/security.hpp b/implementation/security/include/security.hpp index 0bac7a0..03406c6 100644 --- a/implementation/security/include/security.hpp +++ b/implementation/security/include/security.hpp @@ -7,6 +7,10 @@ #define VSOMEIP_V3_SECURITY_SECURITY_HPP_ #include <memory> +#include <unordered_set> + +#include <vsomeip/payload.hpp> +#include <vsomeip/primitive_types.hpp> namespace vsomeip_v3 { @@ -49,6 +53,11 @@ public: uint32_t _uid, uint32_t _gid) = 0; virtual void store_uid_gid_to_client_mapping(uint32_t _uid, uint32_t _gid, client_t _client) = 0; + + virtual void get_requester_policies(const std::shared_ptr<policy> _policy, + std::set<std::shared_ptr<policy> > &_requesters) const = 0; + virtual void get_clients(uid_t _uid, gid_t _gid, + std::unordered_set<client_t> &_clients) const = 0; }; } // namespace vsomeip_v3 diff --git a/implementation/security/include/security_impl.hpp b/implementation/security/include/security_impl.hpp index 564c029..dfeea6b 100644 --- a/implementation/security/include/security_impl.hpp +++ b/implementation/security/include/security_impl.hpp @@ -50,8 +50,6 @@ public: bool is_policy_removal_allowed(uint32_t _uid) const; - bool parse_uid_gid(const byte_t* &_buffer, uint32_t &_buffer_size, - uint32_t &_uid, uint32_t &_gid) const; bool parse_policy(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_uid, uint32_t &_gid, const std::shared_ptr<policy> &_policy) const; @@ -62,30 +60,24 @@ public: bool store_client_to_uid_gid_mapping(client_t _client, uint32_t _uid, uint32_t _gid); void store_uid_gid_to_client_mapping(uint32_t _uid, uint32_t _gid, client_t _client); + void get_requester_policies(const std::shared_ptr<policy> _policy, + std::set<std::shared_ptr<policy> > &_requesters) const; + void get_clients(uid_t _uid, gid_t _gid, std::unordered_set<client_t> &_clients) const; + private: - // Helper - - bool get_struct_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) const; - bool get_union_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) const; - bool get_array_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) const; - bool is_range(const byte_t* &_buffer, uint32_t &_buffer_size) const; - bool parse_id_item(const byte_t* &_buffer, uint32_t& parsed_ids_bytes, - ranges_t& its_ranges, uint32_t &_buffer_size) const; - bool parse_range(const byte_t* &_buffer, uint32_t &_buffer_size, - uint16_t &_first, uint16_t &_last) const; - bool parse_id(const byte_t* &_buffer, uint32_t &_buffer_size, uint16_t &_id) const; // Configuration void load_policies(const configuration_element &_element); void load_policy(const boost::property_tree::ptree &_tree); - void load_credential(const boost::property_tree::ptree &_tree, ids_t &_ids); + void load_policy_body(std::shared_ptr<policy> &_policy, + const boost::property_tree::ptree::const_iterator &_tree); + void load_credential(const boost::property_tree::ptree &_tree, + boost::icl::interval_map<uid_t, boost::icl::interval_set<gid_t> > &_ids); bool load_routing_credentials(const configuration_element &_element); - void load_ranges(const boost::property_tree::ptree &_tree, ranges_t &_range); - void load_instance_ranges(const boost::property_tree::ptree &_tree, ranges_t &_range); - + template<typename T_> + void load_interval_set(const boost::property_tree::ptree &_tree, + boost::icl::interval_set<T_> &_range, bool _exclude_margins = false); void load_security_update_whitelist(const configuration_element &_element); - void load_service_ranges(const boost::property_tree::ptree &_tree, - std::set<std::pair<service_t, service_t>> &_ranges); private: client_t routing_client_; @@ -106,10 +98,10 @@ private: bool check_whitelist_; mutable std::mutex service_interface_whitelist_mutex_; - std::set<std::pair<service_t, service_t>> service_interface_whitelist_; + boost::icl::interval_set<service_t> service_interface_whitelist_; mutable std::mutex uid_whitelist_mutex_; - ranges_t uid_whitelist_; + boost::icl::interval_set<uint32_t> uid_whitelist_; mutable std::mutex routing_credentials_mutex_; std::pair<uint32_t, uint32_t> routing_credentials_; diff --git a/implementation/security/src/policy.cpp b/implementation/security/src/policy.cpp new file mode 100644 index 0000000..4babd06 --- /dev/null +++ b/implementation/security/src/policy.cpp @@ -0,0 +1,499 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <iomanip> + +#include <vsomeip/internal/logger.hpp> + +#include "../include/policy.hpp" +#include "../../utility/include/byteorder.hpp" + +namespace vsomeip_v3 { + +bool +policy::get_uid_gid(uid_t &_uid, gid_t &_gid) const { + + if (credentials_.size() != 1) + return (false); + + const auto its_uids = credentials_.begin()->first; + const auto its_gids = credentials_.begin()->second; + + if (its_gids.size() != 1) + return (false); + + if (its_uids.lower() != its_uids.upper() + || its_gids.begin()->lower() != its_gids.begin()->upper()) + return (false); + + _uid = its_uids.lower(); + _gid = its_gids.begin()->lower(); + + return (true); +} + +bool +policy::deserialize_uid_gid(const byte_t * &_data, uint32_t &_size, + uid_t &_uid, gid_t &_gid) const { + + bool its_result; + + its_result = deserialize_u32(_data, _size, _uid); + if (its_result == false) + return (false); + + its_result = deserialize_u32(_data, _size, _gid); + if (its_result == false) + return (false); + + return (true); +} + +bool +policy::deserialize(const byte_t * &_data, uint32_t &_size) { + + bool its_result; + uid_t its_uid; + gid_t its_gid; + + std::lock_guard<std::mutex> its_lock(mutex_); + + its_result = deserialize_uid_gid(_data, _size, its_uid, its_gid); + if (its_result == false) + return (false); + + // Fill policy uid/gid + const auto its_uid_interval + = boost::icl::interval<uid_t>::closed(its_uid, its_uid); + boost::icl::interval_set<gid_t> its_gid_set; + its_gid_set.insert(its_gid); + credentials_ += std::make_pair(its_uid_interval, its_gid_set); + + // Deserialized policies are always "Allow" - policies + allow_who_ = true; + allow_what_ = true; + + // Deserialize requests array length + uint32_t its_requests_length; + its_result = deserialize_u32(_data, _size, its_requests_length); + if (its_result == false) + return (false); + + // Deserialize requests + while (0 < its_requests_length) { + + uint32_t its_current_size(_size); + + uint16_t its_service; + its_result = deserialize_u16(_data, _size, its_service); + if (its_result == false) + return (false); + + if (its_service == 0x0000 || its_service == 0xffff) { + VSOMEIP_WARNING << "vSomeIP Security: Policy with service ID: 0x" + << std::hex << its_service << " is not allowed!"; + return (false); + } + + const auto its_service_interval + = boost::icl::interval<service_t>::closed(its_service, its_service); + + boost::icl::interval_map<instance_t, + boost::icl::interval_set<method_t> > its_ids; + its_result = deserialize_ids(_data, _size, its_ids); + if (its_result == false) + return (false); + + requests_ += std::make_pair(its_service_interval, its_ids); + + its_requests_length -= (its_current_size - _size); + } + + // Deserialize offers array length + uint32_t its_offers_length; + its_result = deserialize_u32(_data, _size, its_offers_length); + if (its_result == false) + return (false); + + while (0 < its_offers_length) { + + uint32_t its_current_size(_size); + + uint16_t its_service; + its_result = deserialize_u16(_data, _size, its_service); + if (its_result == false) + return (false); + + if (its_service == 0x0000 || its_service == 0xFFFF) { + VSOMEIP_WARNING << "vSomeIP Security: Policy with service ID: 0x" + << std::hex << its_service << " is not allowed!"; + return false; + } + + const auto its_service_interval + = boost::icl::interval<service_t>::closed(its_service, its_service); + + boost::icl::interval_set<instance_t> its_instance_interval_set; + its_result = deserialize_id_item_list(_data, _size, + its_instance_interval_set); + if (its_result == false) + return (false); + + offers_ += std::make_pair(its_service_interval, its_instance_interval_set); + + its_offers_length -= (its_current_size - _size); + } + + return (true); +} + +bool +policy::deserialize_ids(const byte_t * &_data, uint32_t &_size, + boost::icl::interval_map<uint16_t, + boost::icl::interval_set<uint16_t> > &_ids) const { + + boost::icl::interval_map<uint16_t, + boost::icl::interval_set<uint16_t> > its_ids; + uint32_t its_array_length; + bool its_result; + + its_result = deserialize_u32(_data, _size, its_array_length); + if (its_result == false) + return (false); + + while (0 < its_array_length) { + uint32_t its_current_size(_size); + + boost::icl::interval_set<uint16_t> its_instances, its_methods; + its_result = deserialize_id_item_list(_data, _size, its_instances); + if (its_result == false) + return (false); + + its_result = deserialize_id_item_list(_data, _size, its_methods); + if (its_result == false) + return (false); + + for (const auto i : its_instances) + its_ids += std::make_pair(i, its_methods); + + its_array_length -= (its_current_size - _size); + } + + _ids = std::move(its_ids); + + return (true); +} + +bool +policy::deserialize_id_item_list(const byte_t * &_data, uint32_t &_size, + boost::icl::interval_set<uint16_t> &_intervals) const { + + boost::icl::interval_set<uint16_t> its_intervals; + uint32_t its_length; + bool its_result; + + its_result = deserialize_u32(_data, _size, its_length); + if (its_result == false) + return (its_result); + + while (0 < its_length) { + + uint32_t its_current_size(_size); + + uint16_t its_low, its_high; + its_result = deserialize_id_item(_data, _size, its_low, its_high); + if (its_result == false) + return (false); + + its_intervals.insert(boost::icl::interval<uint16_t>::closed(its_low, its_high)); + + its_length -= (its_current_size - _size); + } + + _intervals = std::move(its_intervals); + + return (true); +} + +bool +policy::deserialize_id_item(const byte_t * &_data, uint32_t &_size, + uint16_t &_low, uint16_t &_high) const { + + uint32_t its_length, its_type; + bool its_result; + + its_result = deserialize_u32(_data, _size, its_length); + if (its_result == false) + return (false); + + its_result = deserialize_u32(_data, _size, its_type); + if (its_result == false) + return (false); + + if (its_type == 1 && its_length == sizeof(uint16_t)) { + its_result = deserialize_u16(_data, _size, _low); + if (its_result == false) + return (false); + + _high = _low; + } else if (its_type == 2 + && its_length == sizeof(uint16_t) + sizeof(uint16_t)) { + its_result = deserialize_u16(_data, _size, _low); + if (its_result == false) + return (false); + + its_result = deserialize_u16(_data, _size, _high); + if (its_result == false) + return (false); + + if (_low > _high) + return (false); + } + + // handle ANY_METHOD configuration + if (_low == ANY_METHOD && _high == ANY_METHOD) { + _low = 0x01; + } + + return (_low != 0x0000); +} + +bool +policy::deserialize_u16(const byte_t * &_data, uint32_t &_size, + uint16_t &_value) const { + + if (_size < sizeof(uint16_t)) + return (false); + + _value = VSOMEIP_BYTES_TO_WORD(_data[0], _data[1]); + + _data += sizeof(uint16_t); + _size -= static_cast<uint16_t>(sizeof(uint16_t)); + + return (true); +} + +bool +policy::deserialize_u32(const byte_t * &_data, uint32_t &_size, + uint32_t &_value) const { + + if (_size < sizeof(uint32_t)) + return (false); + + _value = VSOMEIP_BYTES_TO_LONG(_data[0], _data[1], _data[2], _data[3]); + + _data += sizeof(uint32_t); + _size -= static_cast<uint32_t>(sizeof(uint32_t)); + + return (true); +} + +bool +policy::serialize(std::vector<byte_t> &_data) const { + + bool its_result; + + std::lock_guard<std::mutex> its_lock(mutex_); + + its_result = serialize_uid_gid(_data); + if (!its_result) + return (false); + + size_t its_requests_pos = _data.size(); + uint32_t its_requests_size(0); + serialize_u32(its_requests_size, _data); + + for (const auto its_request : requests_) { + for (auto its_service = its_request.first.lower(); + its_service <= its_request.first.upper(); + its_service++) { + + serialize_u16(its_service, _data); + + size_t its_pos = _data.size(); + uint32_t its_instances_size(0); + serialize_u32(its_instances_size, _data); + + for (const auto i : its_request.second) { + boost::icl::interval_set<instance_t> its_instances; + its_instances.insert(i.first); + serialize_interval_set(its_instances, _data); + serialize_interval_set(i.second, _data); + } + + its_instances_size = static_cast<uint32_t>(_data.size() - its_pos - sizeof(uint32_t)); + serialize_u32_at(its_instances_size, _data, its_pos); + } + } + + its_requests_size = static_cast<uint32_t>(_data.size() - its_requests_pos - sizeof(uint32_t)); + serialize_u32_at(its_requests_size, _data, its_requests_pos); + + uint32_t its_offers_size = 0; + serialize_u32(its_offers_size, _data); + + return (true); +} + +bool +policy::serialize_uid_gid(std::vector<byte_t> &_data) const { + + if (credentials_.size() != 1) { + VSOMEIP_ERROR << "Unserializable policy (ids)."; + return (false); + } + + auto its_credential = *(credentials_.begin()); + if (its_credential.second.size() != 1) { + VSOMEIP_ERROR << "Unserializable policy (intervals)."; + return (false); + } + + auto its_uid_interval = its_credential.first; + if (its_uid_interval.lower() != its_uid_interval.upper()) { + VSOMEIP_ERROR << "Unserializable policy (uid)."; + return (false); + } + + auto its_gid_interval = *(its_credential.second.begin()); + if (its_gid_interval.lower() != its_gid_interval.upper()) { + VSOMEIP_ERROR << "Unserializable policy (gid)."; + return (false); + } + + serialize_u32(its_uid_interval.lower(), _data); + serialize_u32(its_gid_interval.lower(), _data); + + return (true); +} + +void +policy::serialize_interval_set( + const boost::icl::interval_set<uint16_t> &_intervals, + std::vector<byte_t> &_data) const { + + size_t its_pos(_data.size()); + uint32_t its_interval_set_size(0); + serialize_u32(its_interval_set_size, _data); + + for (const auto i : _intervals) + serialize_interval(i, _data); + + its_interval_set_size = static_cast<uint32_t>(_data.size() + - its_pos - sizeof(uint32_t)); + serialize_u32_at(its_interval_set_size, _data, its_pos); +} + +void +policy::serialize_interval( + const boost::icl::discrete_interval<uint16_t> &_interval, + std::vector<byte_t> &_data) const { + + uint32_t its_union_length, its_union_type; + + if (_interval.lower() == _interval.upper()) { // single value + its_union_length = static_cast<uint32_t>(sizeof(uint16_t)); + its_union_type = 1; + + serialize_u32(its_union_length, _data); + serialize_u32(its_union_type, _data); + + serialize_u16(_interval.lower(), _data); + } else { // value interval + its_union_type = 2; + its_union_length = static_cast<uint32_t>( + sizeof(uint16_t) + sizeof(uint16_t)); + + serialize_u32(its_union_length, _data); + serialize_u32(its_union_type, _data); + + serialize_u16(_interval.lower(), _data); + serialize_u16(_interval.upper(), _data); + } +} + +void +policy::serialize_u16(uint16_t _value, + std::vector<byte_t> &_data) const { + + _data.push_back(VSOMEIP_WORD_BYTE1(_value)); + _data.push_back(VSOMEIP_WORD_BYTE0(_value)); +} + +void +policy::serialize_u32(uint32_t _value, + std::vector<byte_t> &_data) const { + + _data.push_back(VSOMEIP_LONG_BYTE3(_value)); + _data.push_back(VSOMEIP_LONG_BYTE2(_value)); + _data.push_back(VSOMEIP_LONG_BYTE1(_value)); + _data.push_back(VSOMEIP_LONG_BYTE0(_value)); +} + +void +policy::serialize_u32_at(uint32_t _value, + std::vector<byte_t> &_data, size_t _pos) const { + + _data[_pos] = VSOMEIP_LONG_BYTE3(_value); + _data[_pos+1] = VSOMEIP_LONG_BYTE2(_value); + _data[_pos+2] = VSOMEIP_LONG_BYTE1(_value); + _data[_pos+3] = VSOMEIP_LONG_BYTE0(_value); +} + +void +policy::print() const { + + for (auto its_credential : credentials_) { + auto its_uid_interval = its_credential.first; + if (its_uid_interval.lower() == std::numeric_limits<uint32_t>::max()) { + VSOMEIP_INFO << "policy::print Security configuration: UID: any"; + } else { + VSOMEIP_INFO << "policy::print Security configuration: UID: " + << std::dec << its_uid_interval.lower(); + } + for (auto its_gid_interval : its_credential.second) { + if (its_gid_interval.lower() == std::numeric_limits<uint32_t>::max()) { + VSOMEIP_INFO << " policy::print Security configuration: GID: any"; + } else { + VSOMEIP_INFO << " policy::print Security configuration: GID: " + << std::dec << its_gid_interval.lower(); + } + } + } + + VSOMEIP_INFO << "policy::print Security configuration: REQUESTS POLICY SIZE: " + << std::dec << requests_.size(); + for (auto its_request : requests_) { + VSOMEIP_INFO << "policy::print ALLOWED REQUESTS Services:" + << std::hex << its_request.first; + for (auto its_instance : its_request.second) { + VSOMEIP_INFO << "policy::print Instances: "; + VSOMEIP_INFO << "policy::print first: 0x" + << std::hex << its_instance.first.lower() + << " last: 0x" << its_instance.first.upper(); + VSOMEIP_INFO << "policy::print Methods: "; + for (auto its_method : its_instance.second) { + VSOMEIP_INFO << "policy::print first: 0x" + << std::hex << its_method.lower() + << " last: 0x" << its_method.upper(); + } + } + } + + VSOMEIP_INFO << "policy::print Security configuration: OFFER POLICY SIZE: " + << std::dec << offers_.size(); + for (auto its_offer : offers_) { + VSOMEIP_INFO << "policy::print ALLOWED OFFERS Services:" + << std::hex << its_offer.first; + for (auto its_instance : its_offer.second) { + VSOMEIP_INFO << "policy::print Instances: "; + VSOMEIP_INFO << "policy::print first: 0x" + << std::hex << its_instance.lower() + << " last: 0x" << its_instance.upper(); + } + } +} + +} // namespace vsomeip_v3 diff --git a/implementation/security/src/policy_manager_impl.cpp b/implementation/security/src/policy_manager_impl.cpp index 7789fcc..a816714 100644 --- a/implementation/security/src/policy_manager_impl.cpp +++ b/implementation/security/src/policy_manager_impl.cpp @@ -20,67 +20,17 @@ policy_manager_impl::create_policy() const { void policy_manager_impl::print_policy(const std::shared_ptr<policy> &_policy) const { - for (auto its_credential : _policy->ids_) { - for (auto its_range : std::get<0>(its_credential)) { - if (std::get<0>(its_range) == 0xFFFFFFFF) { - VSOMEIP_INFO << "print_policy Security configuration: UID: any"; - } else { - VSOMEIP_INFO << "print_policy Security configuration: UID: 0x" - << std::hex << std::get<0>(its_range); - } - } - for (auto its_range : std::get<1>(its_credential)) { - if (std::get<0>(its_range) == 0xFFFFFFFF) { - VSOMEIP_INFO << "print_policy Security configuration: GID: any"; - } else { - VSOMEIP_INFO << "print_policy Security configuration: GID: 0x" - << std::hex << std::get<0>(its_range); - } - } - } - - VSOMEIP_INFO << "print_policy Security configuration: RQUESTS POLICY SIZE: " - << std::dec << _policy->services_.size(); - for (auto its_offer : _policy->services_) { - VSOMEIP_INFO << "print_policy ALLOWED REQUESTS Service: 0x" - << std::hex << std::get<0>(its_offer); - for (auto its_ids : std::get<1>(its_offer)) { - VSOMEIP_INFO << "print_policy Instances: "; - for (auto its_instance_range : std::get<0>(its_ids)) { - VSOMEIP_INFO << "print_policy first: 0x" - << std::hex << std::get<0>(its_instance_range) - << " last: 0x" << std::get<1>(its_instance_range); - } - VSOMEIP_INFO << "print_policy Methods: "; - for (auto its_method_range : std::get<1>(its_ids)) { - VSOMEIP_INFO << "print_policy first: 0x" - << std::hex << std::get<0>(its_method_range) - << " last: 0x" << std::get<1>(its_method_range); - } - } - } - - VSOMEIP_INFO << "print_policy Security configuration: OFFER POLICY SIZE: " - << std::dec << _policy->offers_.size(); - for (auto its_offer : _policy->offers_) { - VSOMEIP_INFO << "print_policy ALLOWED OFFERS Service: 0x" - << std::hex << std::get<0>(its_offer); - for (auto its_ids : std::get<1>(its_offer)) { - VSOMEIP_INFO << "print_policy Instances: "; - VSOMEIP_INFO << "print_policy first: 0x" - << std::hex << std::get<0>(its_ids) - << " last: 0x" << std::get<1>(its_ids); - } - } + if (_policy) + _policy->print(); } bool -policy_manager_impl::parse_uid_gid(const byte_t* &_buffer, uint32_t &_buffer_size, - uint32_t &_uid, uint32_t &_gid) const { +policy_manager_impl::parse_uid_gid(const byte_t* &_buffer, + uint32_t &_buffer_size, uint32_t &_uid, uint32_t &_gid) const { - auto its_security = security_impl::get(); - return (its_security - && its_security->parse_uid_gid(_buffer, _buffer_size, _uid, _gid)); + const auto its_policy = std::make_shared<policy>(); + return (its_policy + && its_policy->deserialize_uid_gid(_buffer, _buffer_size, _uid, _gid)); } bool diff --git a/implementation/security/src/security_impl.cpp b/implementation/security/src/security_impl.cpp index ef1404d..12bb9db 100644 --- a/implementation/security/src/security_impl.cpp +++ b/implementation/security/src/security_impl.cpp @@ -3,16 +3,18 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <sstream> + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN + #define NOMINMAX #include <windows.h> #include <stdlib.h> - #define bswap_16(x) _byteswap_ushort(x) - #define bswap_32(x) _byteswap_ulong(x) -#else - #include <byteswap.h> #endif +#include <algorithm> + +#include <vsomeip/internal/policy_manager.hpp> #include "../include/security_impl.hpp" #include "../../configuration/include/configuration_element.hpp" #ifdef ANDROID @@ -23,16 +25,19 @@ namespace vsomeip_v3 { -static const std::uint8_t uid_width_ = sizeof(std::uint32_t); -static const std::uint8_t gid_width_ = sizeof(std::uint32_t); -static const std::uint8_t id_width_ = sizeof(std::uint16_t); -static const std::uint8_t range_width_ = sizeof(std::uint32_t); +template<typename T_> +void read_data(const std::string &_in, T_ &_out) { + std::stringstream its_converter; + + if (_in.size() > 2 + && _in[0] == '0' + && (_in[1] == 'x' || _in[1] == 'X')) + its_converter << std::hex << _in; + else + its_converter << std::dec << _in; -static const std::uint8_t skip_union_length_ = sizeof(std::uint32_t); -static const std::uint8_t skip_union_type_ = sizeof(std::uint32_t); -static const std::uint8_t skip_union_length_type_ = sizeof(std::uint32_t) + sizeof(std::uint32_t); -static const std::uint8_t skip_struct_length_ = sizeof(std::uint32_t); -static const std::uint8_t skip_array_length_ = sizeof(std::uint32_t); + its_converter >> _out; +} security_impl::security_impl() : policy_enabled_(false), @@ -67,8 +72,9 @@ security_impl::is_audit() const { } bool -security_impl::check_credentials(client_t _client, uid_t _uid, - gid_t _gid) { +security_impl::check_credentials(client_t _client, + uid_t _uid, gid_t _gid) { + if (!policy_enabled_) { return true; } @@ -82,27 +88,18 @@ security_impl::check_credentials(client_t _client, uid_t _uid, for (const auto &p : its_policies) { std::lock_guard<std::mutex> its_policy_lock(p->mutex_); - for (auto its_credential : p->ids_) { - bool has_uid(false), has_gid(false); - for (auto its_range : std::get<0>(its_credential)) { - if (std::get<0>(its_range) <= _uid && _uid <= std::get<1>(its_range)) { - has_uid = true; - break; - } - } - for (auto its_range : std::get<1>(its_credential)) { - if (std::get<0>(its_range) <= _gid && _gid <= std::get<1>(its_range)) { - has_gid = true; - break; - } - } - if (has_uid && has_gid) { - has_id = true; - break; - } + bool has_uid, has_gid(false); + + const auto found_uid = p->credentials_.find(_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(_gid); + has_gid = (found_gid != found_uid->second.end()); } + has_id = (has_uid && has_gid); + if ((has_id && p->allow_who_) || (!has_id && !p->allow_who_)) { if (!store_client_to_uid_gid_mapping(_client,_uid, _gid)) { std::string security_mode_text = "!"; @@ -132,8 +129,10 @@ security_impl::check_credentials(client_t _client, uid_t _uid, } bool -security_impl::is_client_allowed(uint32_t _uid, uint32_t _gid, client_t _client, service_t _service, - instance_t _instance, method_t _method, bool _is_request_service) const { +security_impl::is_client_allowed(uint32_t _uid, uint32_t _gid, client_t _client, + service_t _service, instance_t _instance, method_t _method, + bool _is_request_service) const { + if (!policy_enabled_) { return true; } @@ -145,8 +144,7 @@ security_impl::is_client_allowed(uint32_t _uid, uint32_t _gid, client_t _client, its_policies = any_client_policies_; } - if (_uid != ANY_UID - && _gid != ANY_GID) { + if (_uid != ANY_UID && _gid != ANY_GID) { its_uid = _uid; its_gid = _gid; } else { @@ -165,51 +163,27 @@ security_impl::is_client_allowed(uint32_t _uid, uint32_t _gid, client_t _client, for (const auto &p : its_policies) { std::lock_guard<std::mutex> its_policy_lock(p->mutex_); - bool has_uid(false), has_gid(false), has_service(false), has_instance_id(false), has_method_id(false); - for (auto its_credential : p->ids_) { - has_uid = has_gid = false; - for (auto its_range : std::get<0>(its_credential)) { - if (std::get<0>(its_range) <= its_uid && its_uid <= std::get<1>(its_range)) { - has_uid = true; - break; - } - } - for (auto its_range : std::get<1>(its_credential)) { - if (std::get<0>(its_range) <= its_gid && its_gid <= std::get<1>(its_range)) { - has_gid = true; - break; - } - } - if (has_uid && has_gid) - break; + bool has_uid, has_gid(false); + bool is_matching(false); + + const auto found_uid = p->credentials_.find(_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(_gid); + has_gid = (found_gid != found_uid->second.end()); } - auto its_service = p->services_.find(_service); - if (its_service != p->services_.end()) { - for (auto its_ids : its_service->second) { - has_service = has_instance_id = has_method_id = false; - for (auto its_instance_range : std::get<0>(its_ids)) { - if (std::get<0>(its_instance_range) <= _instance && _instance <= std::get<1>(its_instance_range)) { - has_instance_id = true; - break; - } - } + const auto found_service = p->requests_.find(_service); + if (found_service != p->requests_.end()) { + const auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { if (!_is_request_service) { - for (auto its_method_range : std::get<1>(its_ids)) { - if (std::get<0>(its_method_range) <= _method && _method <= std::get<1>(its_method_range)) { - has_method_id = true; - break; - } - } + const auto found_method = found_instance->second.find(_method); + is_matching = (found_method != found_instance->second.end()); } else { // handle VSOMEIP_REQUEST_SERVICE - has_method_id = true; - } - - if (has_instance_id && has_method_id) { - has_service = true; - break; + is_matching = true; } } } @@ -217,17 +191,17 @@ security_impl::is_client_allowed(uint32_t _uid, uint32_t _gid, client_t _client, if ((has_uid && has_gid && p->allow_who_) || ((!has_uid || !has_gid) && !p->allow_who_)) { if (p->allow_what_) { // allow policy - if (has_service) { - return true; + if (is_matching) { + return (true); } } else { // deny policy // allow client if the service / instance / !ANY_METHOD was not found - if ((!has_service && (_method != ANY_METHOD)) + if ((!is_matching && (_method != ANY_METHOD)) // allow client if the service / instance / ANY_METHOD was not found // and it is a "deny nothing" policy - || (!has_service && (_method == ANY_METHOD) && p->services_.empty())) { - return true; + || (!is_matching && (_method == ANY_METHOD) && p->requests_.empty())) { + return (true); } } } @@ -244,7 +218,7 @@ security_impl::is_client_allowed(uint32_t _uid, uint32_t _gid, client_t _client, << _service << "/" << _instance << "/" << _method << security_mode_text; - return !check_credentials_; + return (!check_credentials_); } bool @@ -281,41 +255,25 @@ security_impl::is_offer_allowed(uint32_t _uid, uint32_t _gid, client_t _client, for (const auto &p : its_policies) { std::lock_guard<std::mutex> its_policy_lock(p->mutex_); - bool has_uid(false), has_gid(false), has_offer(false); - for (auto its_credential : p->ids_) { - has_uid = has_gid = false; - for (auto its_range : std::get<0>(its_credential)) { - if (std::get<0>(its_range) <= its_uid && its_uid <= std::get<1>(its_range)) { - has_uid = true; - break; - } - } - for (auto its_range : std::get<1>(its_credential)) { - if (std::get<0>(its_range) <= its_gid && its_gid <= std::get<1>(its_range)) { - has_gid = true; - break; - } - } + bool has_uid, has_gid(false), has_offer(false); - if (has_uid && has_gid) - break; + const auto found_uid = p->credentials_.find(_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(_gid); + has_gid = (found_gid != found_uid->second.end()); } - auto find_service = p->offers_.find(_service); - if (find_service != p->offers_.end()) { - for (auto its_instance_range : find_service->second) { - if (std::get<0>(its_instance_range) <= _instance - && _instance <= std::get<1>(its_instance_range)) { - has_offer = true; - break; - } - } + const auto found_service = p->offers_.find(_service); + if (found_service != p->offers_.end()) { + const auto found_instance = found_service->second.find(_instance); + has_offer = (found_instance != found_service->second.end()); } if ((has_uid && has_gid && p->allow_who_) || ((!has_uid || !has_gid) && !p->allow_who_)) { if (p->allow_what_ == has_offer) { - return true; + return (true); } } } @@ -325,13 +283,15 @@ security_impl::is_offer_allowed(uint32_t _uid, uint32_t _gid, client_t _client, security_mode_text = " but will be allowed due to audit mode is active!"; } - VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client - << " with UID/GID=" << std::dec << its_uid << "/" << its_gid - << " isn't allowed to offer service/instance " << std::hex - << _service << "/" << _instance + VSOMEIP_INFO << "vSomeIP Security: Client 0x" + << std::hex << _client + << " with UID/GID=" + << std::dec << its_uid << "/" << its_gid + << " isn't allowed to offer service/instance " + << std::hex << _service << "/" << _instance << security_mode_text; - return !check_credentials_; + return (!check_credentials_); } bool @@ -456,45 +416,81 @@ security_impl::remove_security_policy(uint32_t _uid, uint32_t _gid) { if (!any_client_policies_.empty()) { std::vector<std::shared_ptr<policy>>::iterator p_it = any_client_policies_.begin(); while (p_it != any_client_policies_.end()) { - std::lock_guard<std::mutex> its_policy_lock((*p_it)->mutex_); - bool has_uid(false), has_gid(false); - for (auto its_credential : p_it->get()->ids_) { - has_uid = has_gid = false; - for (auto its_range : std::get<0>(its_credential)) { - if (std::get<0>(its_range) <= _uid && _uid <= std::get<1>(its_range)) { - has_uid = true; - break; - } - } - for (auto its_range : std::get<1>(its_credential)) { - if (std::get<0>(its_range) <= _gid && _gid <= std::get<1>(its_range)) { - has_gid = true; - break; - } + bool is_matching(false); + { + std::lock_guard<std::mutex> its_policy_lock((*p_it)->mutex_); + bool has_uid(false), has_gid(false); + const auto found_uid = (*p_it)->credentials_.find(_uid); + has_uid = (found_uid != (*p_it)->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(_gid); + has_gid = (found_gid != found_uid->second.end()); } + // only remove "credentials allow" policies to prevent removal of // blacklist configured in file - if (has_uid && has_gid && p_it->get()->allow_who_) { - was_removed = true; - break; + if (has_uid && has_gid && (*p_it)->allow_who_) { + is_matching = true; } } - if (was_removed) { + if (is_matching) { + was_removed = true; p_it = any_client_policies_.erase(p_it); - break; } else { ++p_it; } } } - return was_removed; + return (was_removed); } void -security_impl::update_security_policy(uint32_t _uid, uint32_t _gid, const std::shared_ptr<policy> &_policy) { - remove_security_policy(_uid, _gid); +security_impl::update_security_policy(uint32_t _uid, uint32_t _gid, + const std::shared_ptr<policy> &_policy) { + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); - any_client_policies_.push_back(_policy); + std::shared_ptr<policy> its_matching_policy; + for (auto p : any_client_policies_) { + if (p->credentials_.size() == 1) { + const auto its_uids = *(p->credentials_.begin()); + if (its_uids.first.lower() == _uid + && its_uids.first.upper() == _uid) { + if (its_uids.second.size() == 1) { + const auto its_gids = *(its_uids.second.begin()); + if (its_gids.lower() == _gid + && its_gids.upper() == _gid) { + if (p->allow_who_ == _policy->allow_who_) { + its_matching_policy = p; + break; + } + } + } + } + } + } + + if (its_matching_policy) { + for (const auto r : _policy->requests_) { + service_t its_lower, its_upper; + get_bounds(r.first, its_lower, its_upper); + for (auto s = its_lower; s <= its_upper; s++) { + boost::icl::discrete_interval<service_t> its_service(s, s, + boost::icl::interval_bounds::closed()); + its_matching_policy->requests_ += std::make_pair(its_service, r.second); + } + } + for (const auto o : _policy->offers_) { + service_t its_lower, its_upper; + get_bounds(o.first, its_lower, its_upper); + for (auto s = its_lower; s <= its_upper; s++) { + boost::icl::discrete_interval<service_t> its_service(s, s, + boost::icl::interval_bounds::closed()); + its_matching_policy->offers_ += std::make_pair(its_service, o.second); + } + } + } else { + any_client_policies_.push_back(_policy); + } } void @@ -504,32 +500,24 @@ security_impl::add_security_credentials(uint32_t _uid, uint32_t _gid, bool was_found(false); std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); for (const auto &p : any_client_policies_) { - std::lock_guard<std::mutex> its_policy_lock(p->mutex_); bool has_uid(false), has_gid(false); - for (auto its_credential : p->ids_) { - has_uid = has_gid = false; - for (auto its_range : std::get<0>(its_credential)) { - if (std::get<0>(its_range) <= _uid && _uid <= std::get<1>(its_range)) { - has_uid = true; - break; - } - } - for (auto its_range : std::get<1>(its_credential)) { - if (std::get<0>(its_range) <= _gid && _gid <= std::get<1>(its_range)) { - has_gid = true; - break; - } - } - if (has_uid && has_gid && p->allow_who_) { - was_found = true; - break; - } + + std::lock_guard<std::mutex> its_policy_lock(p->mutex_); + const auto found_uid = p->credentials_.find(_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(_gid); + has_gid = (found_gid != found_uid->second.end()); } - if (was_found) { + + if (has_uid && has_gid && p->allow_who_) { + was_found = true; break; } } - // Do not add the new (credentials-only-policy) if a allow credentials policy with same credentials was found + + // Do not add the new (credentials-only-policy) if a allow + // credentials policy with same credentials was found if (!was_found) { any_client_policies_.push_back(_policy); VSOMEIP_INFO << __func__ << " Added security credentials at client: 0x" @@ -546,66 +534,46 @@ security_impl::is_remote_client_allowed() const { } bool -security_impl::parse_uid_gid(const byte_t* &_buffer, uint32_t &_buffer_size, - uint32_t &_uid, uint32_t &_gid) const { - - uint32_t its_uid = ANY_UID; - uint32_t its_gid = ANY_GID; - - if (_buffer_size >= sizeof(uint32_t) * 2) { - std::memcpy(&its_uid, _buffer, sizeof(uint32_t)); - _uid = bswap_32(its_uid); - - std::memcpy(&its_gid, _buffer + sizeof(uint32_t), sizeof(uint32_t)); - _gid = bswap_32(its_gid); - - _buffer_size -= (uid_width_ + gid_width_); - _buffer += (uid_width_ + gid_width_); - return true; - } - return false; -} - -bool security_impl::is_policy_update_allowed(uint32_t _uid, std::shared_ptr<policy> &_policy) const { - bool uid_allowed(false); + + bool is_uid_allowed(false); { std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); - for (auto its_uid_range : uid_whitelist_) { - if (std::get<0>(its_uid_range) <= _uid && _uid <= std::get<1>(its_uid_range)) { - uid_allowed = true; - break; - } - } + const auto found_uid = uid_whitelist_.find(_uid); + is_uid_allowed = (found_uid != uid_whitelist_.end()); } - if (uid_allowed) { + if (is_uid_allowed) { std::lock_guard<std::mutex> its_lock(service_interface_whitelist_mutex_); std::lock_guard<std::mutex> its_policy_lock(_policy->mutex_); - for (auto its_request : _policy->services_) { - auto its_requested_service = std::get<0>(its_request); + for (auto its_request : _policy->requests_) { bool has_service(false); - for (auto its_service_range : service_interface_whitelist_) { - if (std::get<0>(its_service_range) <= its_requested_service - && its_requested_service <= std::get<1>(its_service_range)) { - has_service = true; + + service_t its_service(0); + for (its_service = its_request.first.lower(); + its_service <= its_request.first.upper(); + its_service++) { + + const auto found_service = service_interface_whitelist_.find(its_service); + has_service = (found_service != service_interface_whitelist_.end()); + if (!has_service) break; - } } + if (!has_service) { if (!check_whitelist_) { VSOMEIP_INFO << "vSomeIP Security: Policy update requesting service ID: " - << std::hex << its_requested_service + << std::hex << its_service << " is not allowed, but will be allowed due to whitelist audit mode is active!"; } else { VSOMEIP_WARNING << "vSomeIP Security: Policy update requesting service ID: " - << std::hex << its_requested_service + << std::hex << its_service << " is not allowed! -> ignore update"; } - return !check_whitelist_; + return (!check_whitelist_); } } - return true; + return (true); } else { if (!check_whitelist_) { VSOMEIP_INFO << "vSomeIP Security: Policy update for UID: " << std::dec << _uid @@ -614,7 +582,7 @@ security_impl::is_policy_update_allowed(uint32_t _uid, std::shared_ptr<policy> & VSOMEIP_WARNING << "vSomeIP Security: Policy update for UID: " << std::dec << _uid << " is not allowed! -> ignore update"; } - return !check_whitelist_; + return (!check_whitelist_); } } @@ -622,388 +590,59 @@ bool security_impl::is_policy_removal_allowed(uint32_t _uid) const { std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); for (auto its_uid_range : uid_whitelist_) { - if (std::get<0>(its_uid_range) <= _uid && _uid <= std::get<1>(its_uid_range)) { - return true; + if (its_uid_range.lower() <= _uid && _uid <= its_uid_range.upper()) { + return (true); } } if (!check_whitelist_) { - VSOMEIP_INFO << "vSomeIP Security: Policy removal for UID: " << std::dec << _uid + VSOMEIP_INFO << "vSomeIP Security: Policy removal for UID: " + << std::dec << _uid << " is not allowed, but will be allowed due to whitelist audit mode is active!"; } else { - VSOMEIP_WARNING << "vSomeIP Security: Policy removal for UID: " << std::dec << _uid + VSOMEIP_WARNING << "vSomeIP Security: Policy removal for UID: " + << std::dec << _uid << " is not allowed! -> ignore removal"; } - return !check_whitelist_; + return (!check_whitelist_); } bool -security_impl::check_routing_credentials(client_t _client, uint32_t _uid, uint32_t _gid) const { +security_impl::check_routing_credentials(client_t _client, + uint32_t _uid, uint32_t _gid) const { + std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); - if ( std::get<0>(routing_credentials_) == _uid - && std::get<1>(routing_credentials_) == _gid) { - return true; + if (routing_credentials_.first == _uid + && routing_credentials_.second == _gid) { + + return (true); } std::string security_mode_text = "!"; if (!check_routing_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" - << std::hex << _client << " and UID/GID=" << std::dec << _uid - << "/" << _gid << " : Check routing credentials failed as " + << std::hex << _client << " and UID/GID=" + << std::dec << _uid << "/" << _gid + << " : Check routing credentials failed as " << "configured routing manager credentials " << "do not match with routing manager credentials" << security_mode_text; - return !check_routing_credentials_; + return (!check_routing_credentials_); } bool security_impl::parse_policy(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_uid, uint32_t &_gid, const std::shared_ptr<policy> &_policy) const { - uint32_t its_uid = ANY_UID; - uint32_t its_gid = ANY_GID; - bool has_error(false); - - // get user ID String - if (parse_uid_gid(_buffer, _buffer_size, its_uid, its_gid)) { - std::lock_guard<std::mutex> its_policy_lock(_policy->mutex_); - - _uid = its_uid; - _gid = its_gid; - - // policy elements - std::pair<uint32_t, uint32_t> its_uid_range, its_gid_range; - std::set<std::pair<uint32_t, uint32_t>> its_uids, its_gids; - - // fill uid and gid range - std::get<0>(its_uid_range) = its_uid; - std::get<1>(its_uid_range) = its_uid; - std::get<0>(its_gid_range) = its_gid; - std::get<1>(its_gid_range) = its_gid; - its_uids.insert(its_uid_range); - its_gids.insert(its_gid_range); - - _policy->ids_.insert(std::make_pair(its_uids, its_gids)); - _policy->allow_who_ = true; - _policy->allow_what_ = true; - - // get struct AclUpdate - uint32_t acl_length = 0; - if (get_struct_length(_buffer, _buffer_size, acl_length)) { - // get requests array length - uint32_t requests_array_length = 0; - if (get_array_length(_buffer, _buffer_size, requests_array_length)) { - // loop through requests array consisting of n x "struct Request" - uint32_t parsed_req_bytes = 0; - while (parsed_req_bytes + skip_struct_length_ <= requests_array_length) { - // get request struct length - uint32_t req_length = 0; - if (get_struct_length(_buffer, _buffer_size, req_length)) { - if (req_length != 0) - parsed_req_bytes += skip_struct_length_; - - uint16_t its_service_id = 0; - ids_t its_instance_method_ranges; - // get serviceID - if (!parse_id(_buffer, _buffer_size, its_service_id)) { - has_error = true; - } else { - if (its_service_id == 0x00 - || its_service_id == 0xFFFF) { - VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy with service ID: 0x" - << its_service_id << " is not allowed!"; - return false; - } - // add length of serviceID - parsed_req_bytes += id_width_; - } - - // get instances array length - uint32_t instances_array_length = 0; - if (get_array_length(_buffer, _buffer_size, instances_array_length)) { - // loop trough instances array consisting of n x "struct Instance" - uint32_t parsed_inst_bytes = 0; - while (parsed_inst_bytes + skip_struct_length_ <= instances_array_length) { - // get instance struct length - uint32_t inst_length = 0; - if (get_struct_length(_buffer, _buffer_size, inst_length)) { - if (inst_length != 0) - parsed_inst_bytes += skip_struct_length_; - - ranges_t its_instance_ranges; - ranges_t its_method_ranges; - // get "IdItem[] ids" array length - uint32_t ids_array_length = 0; - if (get_array_length(_buffer, _buffer_size, ids_array_length)) { - uint32_t parsed_ids_bytes = 0; - while (parsed_ids_bytes + skip_struct_length_ <= ids_array_length) { - if (!parse_id_item(_buffer, parsed_ids_bytes, its_instance_ranges, _buffer_size)) { - return false; - } - } - parsed_inst_bytes += (skip_array_length_ + ids_array_length); - } - // get "IdItem[] methods" array length - uint32_t methods_array_length = 0; - if (get_array_length(_buffer, _buffer_size, methods_array_length)) { - uint32_t parsed_method_bytes = 0; - while (parsed_method_bytes + skip_struct_length_ <= methods_array_length) { - if (!parse_id_item(_buffer, parsed_method_bytes, its_method_ranges, _buffer_size)) { - return false; - } - } - if (!its_instance_ranges.empty() && !its_method_ranges.empty()) { - its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); - } - parsed_inst_bytes += (skip_array_length_ + methods_array_length); - } - } - } - parsed_req_bytes += (skip_array_length_ + instances_array_length); - } - if (!its_instance_method_ranges.empty()) { - auto find_service = _policy->services_.find(its_service_id); - if (find_service != _policy->services_.end()) { - find_service->second.insert(its_instance_method_ranges.begin(), - its_instance_method_ranges.end()); - } else { - _policy->services_.insert( - std::make_pair(its_service_id, its_instance_method_ranges)); - } - } - } - } - } - // get offers array length - uint32_t offers_array_length = 0; - if (get_array_length(_buffer, _buffer_size, offers_array_length)){ - // loop through offers array - uint32_t parsed_offers_bytes = 0; - while (parsed_offers_bytes + skip_struct_length_ <= offers_array_length) { - // get service ID - uint16_t its_service_id = 0; - ranges_t its_instance_ranges; - // get serviceID - if (!parse_id(_buffer, _buffer_size, its_service_id)) { - has_error = true; - } else { - if (its_service_id == 0x00 - || its_service_id == 0xFFFF) { - VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy with service ID: 0x" - << its_service_id << " is not allowed!"; - return false; - } - // add length of serviceID - parsed_offers_bytes += id_width_; - } - - // get "IdItem[] ids" array length - uint32_t ids_array_length = 0; - if (get_array_length(_buffer, _buffer_size, ids_array_length)) { - uint32_t parsed_ids_bytes = 0; - while (parsed_ids_bytes + skip_struct_length_ <= ids_array_length) { - if (!parse_id_item(_buffer, parsed_ids_bytes, its_instance_ranges, _buffer_size)) { - return false; - } - } - parsed_offers_bytes += (skip_array_length_ + ids_array_length); - } - if (!its_instance_ranges.empty()) { - auto find_service = _policy->offers_.find(its_service_id); - if (find_service != _policy->offers_.end()) { - find_service->second.insert(its_instance_ranges.begin(), - its_instance_ranges.end()); - } else { - _policy->offers_.insert( - std::make_pair(its_service_id, its_instance_ranges)); - } - } - } - } - } else { - VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy with empty request / offer section is not allowed!"; - has_error = true; - } - } else { - VSOMEIP_WARNING << std::hex << "vSomeIP Security: Policy without UID / GID is not allowed!"; - has_error = true; - } - - if (!has_error) - return true; - else - return false; -} - -bool -security_impl::get_struct_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) const { - uint32_t its_length = 0; - bool length_field_deployed(false); - // [TR_SOMEIP_00080] d If the length of the length field is not specified, a length of 0 - // has to be assumed and no length field is in the message. - if (length_field_deployed) { - if (_buffer_size >= sizeof(uint32_t)) { - std::memcpy(&its_length, _buffer, sizeof(uint32_t)); - _length = bswap_32(its_length); - _buffer_size -= skip_struct_length_; - _buffer += skip_struct_length_; - return true; - } - } else { - _length = 0; - return true; - } - - return false; -} - -bool -security_impl::get_union_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) const { - uint32_t its_length = 0; - - // [TR_SOMEIP_00125] d If the Interface Specification does not specify the length of the - // length field for a union, 32 bit length of the length field shall be used. - if (_buffer_size >= sizeof(uint32_t)) { - std::memcpy(&its_length, _buffer, sizeof(uint32_t)); - _length = bswap_32(its_length); - _buffer_size -= skip_union_length_; - _buffer += skip_union_length_; - return true; - } - return false; -} - -bool -security_impl::get_array_length(const byte_t* &_buffer, uint32_t &_buffer_size, uint32_t &_length) const { - - uint32_t its_length = 0; - - // [TR_SOMEIP_00106] d The layout of arrays with dynamic length basically is based on - // the layout of fixed length arrays. To determine the size of the array the serialization - // adds a length field (default length 32 bit) in front of the data, which counts the bytes - // of the array. The length does not include the size of the length field. Thus, when - // transporting an array with zero elements the length is set to zero. - if (_buffer_size >= sizeof(uint32_t)) { - std::memcpy(&its_length, _buffer, sizeof(uint32_t)); - _length = bswap_32(its_length); - _buffer_size -= skip_array_length_; - _buffer += skip_array_length_; - return true; - } - return false; -} - -bool -security_impl::is_range(const byte_t* &_buffer, uint32_t &_buffer_size) const { - - uint32_t its_type = 0; - - // [TR_SOMEIP_00128] If the Interface Specification does not specify the length of the - // type field of a union, 32 bit length of the type field shall be used. - if (_buffer_size >= sizeof(uint32_t)) { - std::memcpy(&its_type, _buffer, sizeof(uint32_t)); - its_type = bswap_32(its_type); - _buffer_size -= skip_union_type_; - _buffer += skip_union_type_; - if (its_type == 0x02) { - return true; - } else { - return false; - } - } - return false; -} - -bool -security_impl::parse_id_item(const byte_t* &_buffer, uint32_t& parsed_ids_bytes, - ranges_t& its_ranges, uint32_t &_buffer_size) const { - - // get "union IdItem" length - uint32_t iditem_length = 0; - if (get_union_length(_buffer, _buffer_size, iditem_length)) { - // determine type of union - uint16_t its_first = 0; - uint16_t its_last = 0; - if (is_range(_buffer, _buffer_size)) { - // get range of instance IDs "struct IdRange" length - uint32_t range_length = 0; - if (get_struct_length(_buffer, _buffer_size, range_length)) { - // read first and last instance range - if (parse_range(_buffer, _buffer_size, its_first, its_last)) { - its_ranges.insert(std::make_pair(its_first, its_last)); - } else { - return false; - } - } - } else { - // a single instance ID - if (parse_id(_buffer, _buffer_size, its_first)) { - if (its_first != ANY_METHOD) { - if (its_first != 0x00) { - its_last = its_first; - its_ranges.insert(std::make_pair(its_first, its_last)); - } else { - return false; - } - } else { - its_first = 0x01; - its_last = 0xFFFE; - its_ranges.insert(std::make_pair(its_first, its_last)); - } - } - } - parsed_ids_bytes += (skip_union_length_type_ + iditem_length); - } - return true; -} - -bool -security_impl::parse_range(const byte_t* &_buffer, uint32_t &_buffer_size, - uint16_t &_first, uint16_t &_last) const { - - uint16_t its_first = 0; - uint16_t its_last = 0; - - if (_buffer_size >= sizeof(uint16_t) * 2) { - if (parse_id(_buffer, _buffer_size, its_first)) { - _first = its_first; - } - if (parse_id(_buffer, _buffer_size, its_last)) { - _last = its_last; - } - if (_first != _last - && (_first == ANY_METHOD || _last == ANY_METHOD)) { - return false; - } - if (_first != 0x0 && _last != 0x00 - && _first <= _last) { - if (_first == ANY_METHOD && - _last == ANY_METHOD) { - _first = 0x01; - _last = 0xFFFE; - } - return true; - } else { - return false; - } - } - return false; -} - -bool -security_impl::parse_id(const byte_t* &_buffer, uint32_t &_buffer_size, uint16_t &_id) const { - uint16_t its_id = 0; - if (_buffer_size >= sizeof(uint16_t)) { - std::memcpy(&its_id, _buffer, sizeof(uint16_t)); - _id = bswap_16(its_id); - _buffer_size -= id_width_; - _buffer += id_width_; - return true; - } - return false; + bool is_valid = _policy->deserialize(_buffer, _buffer_size); + if (is_valid) + is_valid = _policy->get_uid_gid(_uid, _gid); + return is_valid; } /////////////////////////////////////////////////////////////////////////////// @@ -1048,12 +687,15 @@ security_impl::load_policies(const configuration_element &_element) { void security_impl::load_policy(const boost::property_tree::ptree &_tree) { + std::shared_ptr<policy> policy(std::make_shared<policy>()); bool allow_deny_set(false); for (auto i = _tree.begin(); i != _tree.end(); ++i) { if (i->first == "credentials") { - std::pair<uint32_t, uint32_t> its_uid_range, its_gid_range; - ranges_t its_uid_ranges, its_gid_ranges; + boost::icl::interval_set<uid_t> its_uid_interval_set; + boost::icl::interval_set<gid_t> its_gid_interval_set; + boost::icl::discrete_interval<uid_t> its_uid_interval; + boost::icl::discrete_interval<gid_t> its_gid_interval; bool has_uid(false), has_gid(false); bool has_uid_range(false), has_gid_range(false); @@ -1063,58 +705,62 @@ security_impl::load_policy(const boost::property_tree::ptree &_tree) { std::string its_value(n->second.data()); if (its_key == "uid") { if(n->second.data().empty()) { - load_ranges(n->second, its_uid_ranges); + load_interval_set(n->second, its_uid_interval_set); has_uid_range = true; } else { if (its_value != "any") { uint32_t its_uid; - std::stringstream its_converter; - its_converter << std::dec << its_value; - its_converter >> its_uid; - std::get<0>(its_uid_range) = its_uid; - std::get<1>(its_uid_range) = its_uid; + read_data(its_value, its_uid); + its_uid_interval = boost::icl::construct< + boost::icl::discrete_interval<uid_t> >( + its_uid, its_uid, + boost::icl::interval_bounds::closed()); } else { - std::get<0>(its_uid_range) = 0; - std::get<1>(its_uid_range) = 0xFFFFFFFF; + its_uid_interval = boost::icl::construct< + boost::icl::discrete_interval<uid_t> >( + std::numeric_limits<uid_t>::min(), + std::numeric_limits<uid_t>::max(), + boost::icl::interval_bounds::closed()); } has_uid = true; } } else if (its_key == "gid") { if(n->second.data().empty()) { - load_ranges(n->second, its_gid_ranges); + load_interval_set(n->second, its_gid_interval_set); has_gid_range = true; } else { if (its_value != "any") { uint32_t its_gid; - std::stringstream its_converter; - its_converter << std::dec << its_value; - its_converter >> its_gid; - std::get<0>(its_gid_range) = its_gid; - std::get<1>(its_gid_range) = its_gid; + read_data(its_value, its_gid); + its_gid_interval = boost::icl::construct< + boost::icl::discrete_interval<gid_t> >( + its_gid, its_gid, + boost::icl::interval_bounds::closed()); } else { - std::get<0>(its_gid_range) = 0; - std::get<1>(its_gid_range) = 0xFFFFFFFF; + its_gid_interval = boost::icl::construct< + boost::icl::discrete_interval<gid_t> >( + std::numeric_limits<gid_t>::min(), + std::numeric_limits<gid_t>::max(), + boost::icl::interval_bounds::closed()); } has_gid = true; } } else if (its_key == "allow" || its_key == "deny") { policy->allow_who_ = (its_key == "allow"); - load_credential(n->second, policy->ids_); + load_credential(n->second, policy->credentials_); } } if (has_uid && has_gid) { - std::set<std::pair<uint32_t, uint32_t>> its_uids, its_gids; - - its_uids.insert(its_uid_range); - its_gids.insert(its_gid_range); + its_gid_interval_set.insert(its_gid_interval); + policy->credentials_ += std::make_pair(its_uid_interval, its_gid_interval_set); policy->allow_who_ = true; - policy->ids_.insert(std::make_pair(its_uids, its_gids)); } if (has_uid_range && has_gid_range) { + for (const auto u : its_uid_interval_set) + policy->credentials_ += std::make_pair(u, its_gid_interval_set); policy->allow_who_ = true; - policy->ids_.insert(std::make_pair(its_uid_ranges, its_gid_ranges)); } } else if (i->first == "allow") { if (allow_deny_set) { @@ -1124,112 +770,7 @@ security_impl::load_policy(const boost::property_tree::ptree &_tree) { } allow_deny_set = true; policy->allow_what_ = true; - for (auto l = i->second.begin(); l != i->second.end(); ++l) { - if (l->first == "requests") { - for (auto n = l->second.begin(); n != l->second.end(); ++n) { - service_t service = 0x0; - instance_t instance = 0x0; - ids_t its_instance_method_ranges; - for (auto k = n->second.begin(); k != n->second.end(); ++k) { - std::stringstream its_converter; - if (k->first == "service") { - std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> service; - } else if (k->first == "instance") { // legacy definition for instances - ranges_t its_instance_ranges; - ranges_t its_method_ranges; - std::string value = k->second.data(); - if (value != "any") { - its_converter << std::hex << value; - its_converter >> instance; - if (instance != 0x0) { - its_instance_ranges.insert(std::make_pair(instance, instance)); - its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); - } - } else { - its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); - its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); - } - its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); - } else if (k->first == "instances") { // new instances definition - for (auto p = k->second.begin(); p != k->second.end(); ++p) { - ranges_t its_instance_ranges; - ranges_t its_method_ranges; - for (auto m = p->second.begin(); m != p->second.end(); ++m) { - if (m->first == "ids") { - load_instance_ranges(m->second, its_instance_ranges); - } else if (m->first == "methods") { - load_instance_ranges(m->second, its_method_ranges); - } - if (!its_instance_ranges.empty() && !its_method_ranges.empty()) { - its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); - } - } - } - if (its_instance_method_ranges.empty()) { - ranges_t its_legacy_instance_ranges; - ranges_t its_legacy_method_ranges; - its_legacy_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); - // try to only load instance ranges with any method to be allowed - load_instance_ranges(k->second, its_legacy_instance_ranges); - if (!its_legacy_instance_ranges.empty() && !its_legacy_method_ranges.empty()) { - its_instance_method_ranges.insert(std::make_pair(its_legacy_instance_ranges, - its_legacy_method_ranges)); - } - } - } - } - if (service != 0x0 && !its_instance_method_ranges.empty()) { - auto find_policy = policy->services_.find(service); - if (find_policy != policy->services_.end()) { - find_policy->second.insert(its_instance_method_ranges.begin(), - its_instance_method_ranges.end()); - } else { - policy->services_.insert( - std::make_pair(service, its_instance_method_ranges)); - } - } - } - } else if (l->first == "offers") { - for (auto n = l->second.begin(); n != l->second.end(); ++n) { - service_t service = 0x0; - instance_t instance = 0x0; - ranges_t its_instance_ranges; - for (auto k = n->second.begin(); k != n->second.end(); ++k) { - std::stringstream its_converter; - if (k->first == "service") { - std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> service; - } else if (k->first == "instance") { // legacy definition for instances - std::string value = k->second.data(); - if (value != "any") { - its_converter << std::hex << value; - its_converter >> instance; - if (instance != 0x0) { - its_instance_ranges.insert(std::make_pair(instance, instance)); - } - } else { - its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); - } - } else if (k->first == "instances") { // new instances definition - load_instance_ranges(k->second, its_instance_ranges); - } - } - if (service != 0x0 && !its_instance_ranges.empty()) { - auto find_service = policy->offers_.find(service); - if (find_service != policy->offers_.end()) { - find_service->second.insert(its_instance_ranges.begin(), - its_instance_ranges.end()); - } else { - policy->offers_.insert( - std::make_pair(service, its_instance_ranges)); - } - } - } - } - } + load_policy_body(policy, i); } else if (i->first == "deny") { if (allow_deny_set) { VSOMEIP_WARNING << "vSomeIP Security: Security configuration: \"deny\" tag overrides " @@ -1238,138 +779,160 @@ security_impl::load_policy(const boost::property_tree::ptree &_tree) { } allow_deny_set = true; policy->allow_what_ = false; - for (auto l = i->second.begin(); l != i->second.end(); ++l) { - if (l->first == "requests") { - for (auto n = l->second.begin(); n != l->second.end(); ++n) { - service_t service = 0x0; - instance_t instance = 0x0; - ids_t its_instance_method_ranges; - for (auto k = n->second.begin(); k != n->second.end(); ++k) { - std::stringstream its_converter; - if (k->first == "service") { - std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> service; - } else if (k->first == "instance") { // legacy definition for instances - ranges_t its_instance_ranges; - ranges_t its_method_ranges; - std::string value = k->second.data(); - if (value != "any") { - its_converter << std::hex << value; - its_converter >> instance; - if (instance != 0x0) { - its_instance_ranges.insert(std::make_pair(instance, instance)); - its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); - } - } else { - its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); - its_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); - } - its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); - } else if (k->first == "instances") { // new instances definition - for (auto p = k->second.begin(); p != k->second.end(); ++p) { - ranges_t its_instance_ranges; - ranges_t its_method_ranges; - for (auto m = p->second.begin(); m != p->second.end(); ++m) { - if (m->first == "ids") { - load_instance_ranges(m->second, its_instance_ranges); - } else if (m->first == "methods") { - load_instance_ranges(m->second, its_method_ranges); - } - if (!its_instance_ranges.empty() && !its_method_ranges.empty()) { - its_instance_method_ranges.insert(std::make_pair(its_instance_ranges, its_method_ranges)); - } - } - } - if (its_instance_method_ranges.empty()) { - ranges_t its_legacy_instance_ranges; - ranges_t its_legacy_method_ranges; - its_legacy_method_ranges.insert(std::make_pair(0x01, 0xFFFF)); - // try to only load instance ranges with any method to be allowed - load_instance_ranges(k->second, its_legacy_instance_ranges); - if (!its_legacy_instance_ranges.empty() && !its_legacy_method_ranges.empty()) { - its_instance_method_ranges.insert(std::make_pair(its_legacy_instance_ranges, - its_legacy_method_ranges)); - } + load_policy_body(policy, i); + } + } + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); + any_client_policies_.push_back(policy); +} + +void +security_impl::load_policy_body(std::shared_ptr<policy> &_policy, + const boost::property_tree::ptree::const_iterator &_tree) { + + for (auto l = _tree->second.begin(); l != _tree->second.end(); ++l) { + if (l->first == "requests") { + for (auto n = l->second.begin(); n != l->second.end(); ++n) { + service_t its_service = 0x0; + instance_t its_instance = 0x0; + boost::icl::interval_map<instance_t, + boost::icl::interval_set<method_t> > its_instance_method_intervals; + for (auto k = n->second.begin(); k != n->second.end(); ++k) { + if (k->first == "service") { + read_data(k->second.data(), its_service); + } else if (k->first == "instance") { // legacy definition for instances + boost::icl::interval_set<instance_t> its_instance_interval_set; + boost::icl::interval_set<method_t> its_method_interval_set; + boost::icl::discrete_interval<instance_t> all_instances(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + boost::icl::discrete_interval<method_t> all_methods(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + + std::string its_value(k->second.data()); + if (its_value != "any") { + read_data(its_value, its_instance); + if (its_instance != 0x0) { + its_instance_interval_set.insert(its_instance); + its_method_interval_set.insert(all_methods); + } + } else { + its_instance_interval_set.insert(all_instances); + its_method_interval_set.insert(all_methods); + } + for (const auto i : its_instance_interval_set) { + its_instance_method_intervals + += std::make_pair(i, its_method_interval_set); + } + } else if (k->first == "instances") { // new instances definition + for (auto p = k->second.begin(); p != k->second.end(); ++p) { + boost::icl::interval_set<instance_t> its_instance_interval_set; + boost::icl::interval_set<method_t> its_method_interval_set; + boost::icl::discrete_interval<method_t> all_methods(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + for (auto m = p->second.begin(); m != p->second.end(); ++m) { + if (m->first == "ids") { + load_interval_set(m->second, its_instance_interval_set); + } else if (m->first == "methods") { + load_interval_set(m->second, its_method_interval_set); } } + if (its_method_interval_set.empty()) + its_method_interval_set.insert(all_methods); + for (const auto i : its_instance_interval_set) { + its_instance_method_intervals + += std::make_pair(i, its_method_interval_set); + } } - if (service != 0x0 && !its_instance_method_ranges.empty()) { - auto find_policy = policy->services_.find(service); - if (find_policy != policy->services_.end()) { - find_policy->second.insert(its_instance_method_ranges.begin(), - its_instance_method_ranges.end()); - } else { - policy->services_.insert( - std::make_pair(service, its_instance_method_ranges)); + + if (its_instance_method_intervals.empty()) { + boost::icl::interval_set<instance_t> its_legacy_instance_interval_set; + boost::icl::interval_set<method_t> its_legacy_method_interval_set; + boost::icl::discrete_interval<method_t> all_methods(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + its_legacy_method_interval_set.insert(all_methods); + + // try to only load instance ranges with any method to be allowed + load_interval_set(k->second, its_legacy_instance_interval_set); + for (const auto i : its_legacy_instance_interval_set) { + its_instance_method_intervals + += std::make_pair(i, its_legacy_method_interval_set); } } } } - if (l->first == "offers") { - for (auto n = l->second.begin(); n != l->second.end(); ++n) { - service_t service = 0x0; - instance_t instance = 0x0; - ranges_t its_instance_ranges; - for (auto k = n->second.begin(); k != n->second.end(); ++k) { - std::stringstream its_converter; - if (k->first == "service") { - std::string value = k->second.data(); - its_converter << std::hex << value; - its_converter >> service; - } else if (k->first == "instance") { // legacy definition for instances - std::string value = k->second.data(); - if (value != "any") { - its_converter << std::hex << value; - its_converter >> instance; - if (instance != 0x0) { - its_instance_ranges.insert(std::make_pair(instance, instance)); - } - } else { - its_instance_ranges.insert(std::make_pair(0x01, 0xFFFF)); - } - } else if (k->first == "instances") { // new instances definition - load_instance_ranges(k->second, its_instance_ranges); - } - } - if (service != 0x0 && !its_instance_ranges.empty()) { - auto find_service = policy->offers_.find(service); - if (find_service != policy->offers_.end()) { - find_service->second.insert(its_instance_ranges.begin(), - its_instance_ranges.end()); - } else { - policy->offers_.insert( - std::make_pair(service, its_instance_ranges)); + if (its_service != 0x0 && !its_instance_method_intervals.empty()) { + _policy->requests_ += std::make_pair( + boost::icl::discrete_interval<service_t>( + its_service, its_service, + boost::icl::interval_bounds::closed()), + its_instance_method_intervals); + } + } + } else if (l->first == "offers") { + for (auto n = l->second.begin(); n != l->second.end(); ++n) { + service_t its_service(0x0); + instance_t its_instance(0x0); + boost::icl::interval_set<instance_t> its_instance_interval_set; + for (auto k = n->second.begin(); k != n->second.end(); ++k) { + if (k->first == "service") { + read_data(k->second.data(), its_service); + } else if (k->first == "instance") { // legacy definition for instances + std::string its_value(k->second.data()); + if (its_value != "any") { + read_data(its_value, its_instance); + if (its_instance != 0x0) { + its_instance_interval_set.insert(its_instance); } + } else { + its_instance_interval_set.insert( + boost::icl::discrete_interval<instance_t>( + 0x0001, 0xFFFF)); } + } else if (k->first == "instances") { // new instances definition + load_interval_set(k->second, its_instance_interval_set); } } + if (its_service != 0x0 && !its_instance_interval_set.empty()) { + _policy->offers_ + += std::make_pair( + boost::icl::discrete_interval<service_t>( + its_service, its_service, + boost::icl::interval_bounds::closed()), + its_instance_interval_set); + } } } } - std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); - any_client_policies_.push_back(policy); } + void security_impl::load_credential( - const boost::property_tree::ptree &_tree, ids_t &_ids) { + const boost::property_tree::ptree &_tree, + boost::icl::interval_map<uid_t, + boost::icl::interval_set<gid_t> > &_credentials) { + for (auto i = _tree.begin(); i != _tree.end(); ++i) { - ranges_t its_uid_ranges, its_gid_ranges; + boost::icl::interval_set<uid_t> its_uid_interval_set; + boost::icl::interval_set<gid_t> its_gid_interval_set; + for (auto j = i->second.begin(); j != i->second.end(); ++j) { std::string its_key(j->first); if (its_key == "uid") { - load_ranges(j->second, its_uid_ranges); + load_interval_set(j->second, its_uid_interval_set); } else if (its_key == "gid") { - load_ranges(j->second, its_gid_ranges); + load_interval_set(j->second, its_gid_interval_set); } else { VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " << "Malformed credential (contains illegal key \"" - << its_key << "\""; + << its_key << "\")"; } } - _ids.insert(std::make_pair(its_uid_ranges, its_gid_ranges)); + for (const auto its_uid_interval : its_uid_interval_set) { + _credentials + += std::make_pair(its_uid_interval, its_gid_interval_set); + } } } @@ -1386,25 +949,14 @@ security_impl::load_routing_credentials(const configuration_element &_element) { ++i) { std::string its_key(i->first); std::string its_value(i->second.data()); - std::stringstream its_converter; if (its_key == "uid") { uint32_t its_uid(0); - if (its_value.find("0x") == 0) { - its_converter << std::hex << its_value; - } else { - its_converter << std::dec << its_value; - } - its_converter >> its_uid; + read_data(its_value, its_uid); std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); std::get<0>(routing_credentials_) = its_uid; } else if (its_key == "gid") { uint32_t its_gid(0); - if (its_value.find("0x") == 0) { - its_converter << std::hex << its_value; - } else { - its_converter << std::dec << its_value; - } - its_converter >> its_gid; + read_data(its_value, its_gid); std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); std::get<1>(routing_credentials_) = its_gid; } @@ -1436,12 +988,12 @@ security_impl::load_security_update_whitelist(const configuration_element &_elem if (its_whitelist->first == "uids") { { std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); - load_ranges(its_whitelist->second, uid_whitelist_); + load_interval_set(its_whitelist->second, uid_whitelist_); } } else if (its_whitelist->first == "services") { { std::lock_guard<std::mutex> its_lock(service_interface_whitelist_mutex_); - load_service_ranges(its_whitelist->second, service_interface_whitelist_); + load_interval_set(its_whitelist->second, service_interface_whitelist_); } } else if (its_whitelist->first == "check-whitelist") { if (its_whitelist->second.data() == "true") { @@ -1455,180 +1007,154 @@ security_impl::load_security_update_whitelist(const configuration_element &_elem } } -void -security_impl::load_ranges( - const boost::property_tree::ptree &_tree, ranges_t &_range) { - ranges_t its_ranges; - for (auto i = _tree.begin(); i != _tree.end(); ++i) { - auto its_data = i->second; - if (!its_data.data().empty()) { - uint32_t its_id; - std::stringstream its_converter; - its_converter << std::dec << its_data.data(); - its_converter >> its_id; - its_ranges.insert(std::make_pair(its_id, its_id)); - } else { - uint32_t its_first, its_last; - bool has_first(false), has_last(false); - for (auto j = its_data.begin(); j != its_data.end(); ++j) { - std::string its_key(j->first); - std::string its_value(j->second.data()); - if (its_key == "first") { - if (its_value == "max") { - its_first = 0xFFFFFFFF; - } else { - std::stringstream its_converter; - its_converter << std::dec << j->second.data(); - its_converter >> its_first; - } - has_first = true; - } else if (its_key == "last") { - if (its_value == "max") { - its_last = 0xFFFFFFFF; - } else { - std::stringstream its_converter; - its_converter << std::dec << j->second.data(); - its_converter >> its_last; - } - has_last = true; - } else { - VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " - << " Malformed range. Contains illegal key (" - << its_key << ")"; - } - } +template<typename T_> +void security_impl::load_interval_set( + const boost::property_tree::ptree &_tree, + boost::icl::interval_set<T_> &_intervals, bool _exclude_margins) { - if (has_first && has_last) { - its_ranges.insert(std::make_pair(its_first, its_last)); - } - } - } - - _range = its_ranges; -} + boost::icl::interval_set<T_> its_intervals; + T_ its_min = std::numeric_limits<T_>::min(); + T_ its_max = std::numeric_limits<T_>::max(); -void -security_impl::load_instance_ranges( - const boost::property_tree::ptree &_tree, ranges_t &_range) { - ranges_t its_ranges; - const std::string& key(_tree.data()); - if (key == "any") { - its_ranges.insert(std::make_pair(0x01, 0xFFFF)); - _range = its_ranges; - return; + if (_exclude_margins) { + its_min++; + its_max--; } - for (auto i = _tree.begin(); i != _tree.end(); ++i) { - auto its_data = i->second; - if (!its_data.data().empty()) { - uint32_t its_id = 0x0; - std::stringstream its_converter; - its_converter << std::hex << its_data.data(); - its_converter >> its_id; - if (its_id != 0x0) { - its_ranges.insert(std::make_pair(its_id, its_id)); - } - } else { - uint32_t its_first, its_last; - bool has_first(false), has_last(false); - for (auto j = its_data.begin(); j != its_data.end(); ++j) { - std::string its_key(j->first); - std::string its_value(j->second.data()); - if (its_key == "first") { - if (its_value == "max") { - its_first = 0xFFFF; - } else { - std::stringstream its_converter; - its_converter << std::hex << j->second.data(); - its_converter >> its_first; - } - has_first = true; - } else if (its_key == "last") { - if (its_value == "max") { - its_last = 0xFFFF; + + const std::string its_key(_tree.data()); + if (its_key == "any") { + its_intervals.insert(boost::icl::discrete_interval<T_>::closed( + its_min, its_max)); + } else { + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + auto its_data = i->second; + if (!its_data.data().empty()) { + T_ its_id; + read_data(its_data.data(), its_id); + if (its_id >= its_min && its_id <= its_max) + its_intervals.insert(its_id); + } else { + T_ its_first, its_last; + bool has_first(false), has_last(false); + for (auto j = its_data.begin(); j != its_data.end(); ++j) { + std::string its_key(j->first); + std::string its_value(j->second.data()); + if (its_key == "first") { + if (its_value == "min") { + its_first = its_min; + } else { + read_data(its_value, its_first); + } + has_first = true; + } else if (its_key == "last") { + if (its_value == "max") { + its_last = its_max; + } else { + read_data(its_value, its_last); + } + has_last = true; } else { - std::stringstream its_converter; - its_converter << std::hex << j->second.data(); - its_converter >> its_last; + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " + << " Malformed range. Contains illegal key (" + << its_key << ")"; } - has_last = true; - } else { - VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " - << " Malformed range. Contains illegal key (" - << its_key << ")"; } - } - - if (has_first && has_last) { - if( its_last > its_first) { - its_ranges.insert(std::make_pair(its_first, its_last)); + if (has_first && has_last && its_first <= its_last) { + its_intervals.insert( + boost::icl::discrete_interval<T_>::closed(its_first, its_last)); } } } } - _range = its_ranges; + _intervals = its_intervals; } void -security_impl::load_service_ranges( - const boost::property_tree::ptree &_tree, std::set<std::pair<service_t, service_t>> &_ranges) { - std::set<std::pair<service_t, service_t>> its_ranges; - const std::string& key(_tree.data()); - if (key == "any") { - its_ranges.insert(std::make_pair(0x01, 0xFFFF)); - _ranges = its_ranges; - return; +security_impl::get_requester_policies(const std::shared_ptr<policy> _policy, + std::set<std::shared_ptr<policy> > &_requesters) const { + + std::vector<std::shared_ptr<policy> > its_policies; + { + std::lock_guard<std::mutex> its_lock(any_client_policies_mutex_); + its_policies = any_client_policies_; } - for (auto i = _tree.begin(); i != _tree.end(); ++i) { - auto its_data = i->second; - if (!its_data.data().empty()) { - service_t its_id = 0x0; - std::stringstream its_converter; - its_converter << std::hex << its_data.data(); - its_converter >> its_id; - if (its_id != 0x0) { - its_ranges.insert(std::make_pair(its_id, its_id)); - } - } else { - service_t its_first, its_last; - bool has_first(false), has_last(false); - for (auto j = its_data.begin(); j != its_data.end(); ++j) { - std::string its_key(j->first); - std::string its_value(j->second.data()); - if (its_key == "first") { - if (its_value == "max") { - its_first = 0xFFFF; - } else { - std::stringstream its_converter; - its_converter << std::hex << j->second.data(); - its_converter >> its_first; - } - has_first = true; - } else if (its_key == "last") { - if (its_value == "max") { - its_last = 0xFFFF; - } else { - std::stringstream its_converter; - its_converter << std::hex << j->second.data(); - its_converter >> its_last; + + std::lock_guard<std::mutex> its_lock(_policy->mutex_); + for (const auto o : _policy->offers_) { + for (const auto p : its_policies) { + if (p == _policy) + continue; + + std::lock_guard<std::mutex> its_lock(p->mutex_); + + auto its_policy = std::make_shared<policy>(); + its_policy->credentials_ = p->credentials_; + + for (const auto r : p->requests_) { + // o represents an offer by a service interval and its instances + // (a set of intervals) + // r represents a request by a service interval and its instances + // and methods (instance intervals mapped to interval sets of methods) + // + // Thus, r matches o if their service identifiers as well as their + // instances overlap. If r and o match, a new policy must be + // created that contains the overlapping services/instances mapping + // of r and o together with the methods from r + service_t its_o_lower, its_o_upper, its_r_lower, its_r_upper; + get_bounds(o.first, its_o_lower, its_o_upper); + get_bounds(r.first, its_r_lower, its_r_upper); + + if (its_o_lower <= its_r_upper && its_r_lower <= its_o_upper) { + auto its_service_min = std::max(its_o_lower, its_r_lower); + auto its_service_max = std::min(its_r_upper, its_o_upper); + + for (const auto i : o.second) { + for (const auto j : r.second) { + for (const auto k : j.second) { + instance_t its_i_lower, its_i_upper, its_k_lower, its_k_upper; + get_bounds(i, its_i_lower, its_i_upper); + get_bounds(k, its_k_lower, its_k_upper); + + if (its_i_lower <= its_k_upper && its_k_lower <= its_i_upper) { + auto its_instance_min = std::max(its_i_lower, its_k_lower); + auto its_instance_max = std::min(its_i_upper, its_k_upper); + + boost::icl::interval_map<instance_t, + boost::icl::interval_set<method_t> > its_instances_methods; + its_instances_methods += std::make_pair( + boost::icl::interval<instance_t>::closed( + its_instance_min, its_instance_max), + j.second); + + its_policy->requests_ += std::make_pair( + boost::icl::interval<instance_t>::closed( + its_service_min, its_service_max), + its_instances_methods); + } + } + } } - has_last = true; - } else { - VSOMEIP_WARNING << "vSomeIP Security: Security interface whitelist configuration: " - << " Malformed range. Contains illegal key (" - << its_key << ")"; } } - if (has_first && has_last) { - if( its_last >= its_first) { - its_ranges.insert(std::make_pair(its_first, its_last)); - } + if (!its_policy->requests_.empty()) { + _requesters.insert(its_policy); + its_policy->print(); } } } +} + +void +security_impl::get_clients(uid_t _uid, gid_t _gid, + std::unordered_set<client_t> &_clients) const { - _ranges = its_ranges; + std::lock_guard<std::mutex> its_lock(ids_mutex_); + for (const auto i : ids_) { + if (i.second.first == _uid && i.second.second == _gid) + _clients.insert(i.first); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/implementation/service_discovery/include/service_discovery_host.hpp b/implementation/service_discovery/include/service_discovery_host.hpp index 88e093b..f864571 100644 --- a/implementation/service_discovery/include/service_discovery_host.hpp +++ b/implementation/service_discovery/include/service_discovery_host.hpp @@ -66,6 +66,7 @@ public: virtual void on_subscribe_ack_with_multicast( service_t _service, instance_t _instance, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_address, uint16_t _port) = 0; virtual std::shared_ptr<endpoint> find_or_create_remote_client( diff --git a/implementation/service_discovery/include/service_discovery_impl.hpp b/implementation/service_discovery/include/service_discovery_impl.hpp index 53b18c0..81bb91f 100644 --- a/implementation/service_discovery/include/service_discovery_impl.hpp +++ b/implementation/service_discovery/include/service_discovery_impl.hpp @@ -79,6 +79,7 @@ public: void unsubscribe(service_t _service, instance_t _instance, eventgroup_t _eventgroup, client_t _client); void unsubscribe_all(service_t _service, instance_t _instance); + void reset_subscriptions(service_t _service, instance_t _instance); bool send(bool _is_announcing); @@ -175,6 +176,7 @@ private: std::shared_ptr<eventgroupentry_impl> &_entry, const std::vector<std::shared_ptr<option_impl> > &_options, std::shared_ptr<remote_subscription_ack> &_acknowledgement, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_destination, bool _is_stop_subscribe_subscribe, bool _force_initial_events, const sd_acceptance_state_t& _sd_ac_state); @@ -194,6 +196,7 @@ private: instance_t _instance, eventgroup_t _eventgroup, major_version_t _major, ttl_t _ttl, uint8_t _counter, const std::set<client_t> &_clients, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_address, uint16_t _port); void handle_eventgroup_subscription_nack(service_t _service, instance_t _instance, eventgroup_t _eventgroup, uint8_t _counter, diff --git a/implementation/service_discovery/src/service_discovery_impl.cpp b/implementation/service_discovery/src/service_discovery_impl.cpp index fba2227..b252e62 100644 --- a/implementation/service_discovery/src/service_discovery_impl.cpp +++ b/implementation/service_discovery/src/service_discovery_impl.cpp @@ -521,6 +521,28 @@ service_discovery_impl::unsubscribe_all( serialize_and_send(its_messages, its_address); } +void +service_discovery_impl::reset_subscriptions( + service_t _service, instance_t _instance) { + + std::lock_guard<std::mutex> its_lock(subscribed_mutex_); + auto found_service = subscribed_.find(_service); + if (found_service != subscribed_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + for (auto &its_eventgroup : found_instance->second) { + auto its_subscription = its_eventgroup.second; + for (auto its_client : its_subscription->get_clients()) { + its_subscription->set_state(its_client, + subscription_state_e::ST_UNKNOWN); + } + its_subscription->set_endpoint(nullptr, true); + its_subscription->set_endpoint(nullptr, false); + } + } + } +} + std::pair<session_t, bool> service_discovery_impl::get_session( const boost::asio::ip::address &_address) { @@ -1100,7 +1122,7 @@ service_discovery_impl::on_message( is_stop_subscribe_subscribe = check_stop_subscribe_subscribe(iter, its_end, its_options); process_eventgroupentry(its_eventgroup_entry, its_options, - its_acknowledgement, _destination, + its_acknowledgement, _sender, _destination, is_stop_subscribe_subscribe, force_initial_events, accept_state); } @@ -1249,7 +1271,7 @@ service_discovery_impl::process_serviceentry( remove_remote_offer_type(its_service, its_instance, its_reliable_address, its_reliable_port, its_unreliable_address, its_unreliable_port); - unsubscribe_all(its_service, its_instance); + reset_subscriptions(its_service, its_instance); if (!is_diagnosis_ && !is_suspended_) { host_->del_routing_info(its_service, its_instance, (its_reliable_port != ILLEGAL_PORT), @@ -1308,6 +1330,7 @@ service_discovery_impl::process_offerservice_serviceentry( VSOMEIP_WARNING << __func__ << ": Unknown remote offer type [" << std::hex << std::setw(4) << std::setfill('0') << _service << "." << std::hex << std::setw(4) << std::setfill('0') << _instance << "]"; + return; // Unknown remote offer type --> no way to access it! } if (_sd_ac_state.sd_acceptance_required_) { @@ -1673,9 +1696,6 @@ service_discovery_impl::insert_offer_service( its_ttl = ttl_; its_entry->set_ttl(its_ttl); - // This would be a clean solution but does _not_ work with the ANDi tool - // unsubscribe_all(_service, _instance); - add_entry_data(_messages, its_data); } else { VSOMEIP_ERROR << __func__ << ": Failed to create service entry."; @@ -1687,6 +1707,7 @@ service_discovery_impl::process_eventgroupentry( std::shared_ptr<eventgroupentry_impl> &_entry, const std::vector<std::shared_ptr<option_impl> > &_options, std::shared_ptr<remote_subscription_ack> &_acknowledgement, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_destination, bool _is_stop_subscribe_subscribe, bool _force_initial_events, const sd_acceptance_state_t& _sd_ac_state) { @@ -2122,7 +2143,7 @@ service_discovery_impl::process_eventgroupentry( if (its_ttl > 0) { handle_eventgroup_subscription_ack(its_service, its_instance, its_eventgroup, its_major, its_ttl, 0, - its_clients, + its_clients, _sender, its_first_address, its_first_port); } else { handle_eventgroup_subscription_nack(its_service, its_instance, its_eventgroup, @@ -2383,6 +2404,7 @@ service_discovery_impl::handle_eventgroup_subscription_ack( service_t _service, instance_t _instance, eventgroup_t _eventgroup, major_version_t _major, ttl_t _ttl, uint8_t _counter, const std::set<client_t> &_clients, + const boost::asio::ip::address &_sender, const boost::asio::ip::address &_address, uint16_t _port) { (void)_major; (void)_ttl; @@ -2407,7 +2429,7 @@ service_discovery_impl::handle_eventgroup_subscription_ack( } if (_address.is_multicast()) { host_->on_subscribe_ack_with_multicast( - _service, _instance, _address, _port); + _service, _instance, _sender, _address, _port); } } } diff --git a/implementation/utility/include/utility.hpp b/implementation/utility/include/utility.hpp index 9975bf9..db54eb9 100644 --- a/implementation/utility/include/utility.hpp +++ b/implementation/utility/include/utility.hpp @@ -145,7 +145,11 @@ private: #else static int lock_fd__; #endif +#ifndef VSOMEIP_ENABLE_CONFIGURATION_OVERLAYS static bool is_checked__; +#else + static std::set<std::string> is_checked__; +#endif }; } // namespace vsomeip_v3 diff --git a/implementation/utility/src/utility.cpp b/implementation/utility/src/utility.cpp index 7f05e95..139faef 100644 --- a/implementation/utility/src/utility.cpp +++ b/implementation/utility/src/utility.cpp @@ -39,7 +39,11 @@ HANDLE utility::lock_handle__(INVALID_HANDLE_VALUE); #else int utility::lock_fd__(-1); #endif +#ifndef VSOMEIP_ENABLE_CONFIGURATION_OVERLAYS bool utility::is_checked__(false); +#else +std::set<std::string> utility::is_checked__; +#endif uint64_t utility::get_message_size(const byte_t *_data, size_t _size) { uint64_t its_size(0); @@ -63,10 +67,17 @@ bool utility::is_routing_manager(const std::shared_ptr<configuration> &_config) // Only the first caller can become routing manager. // Therefore, subsequent calls can be immediately answered... std::lock_guard<std::mutex> its_lock(mutex__); +#ifndef VSOMEIP_ENABLE_CONFIGURATION_OVERLAYS if (is_checked__) return false; is_checked__ = true; +#else + if (is_checked__.find(_config->get_network()) != is_checked__.end()) + return false; + + is_checked__.insert(_config->get_network()); +#endif #ifdef _WIN32 wchar_t its_tmp_folder[MAX_PATH]; if (GetTempPathW(MAX_PATH, its_tmp_folder)) { @@ -112,8 +123,13 @@ bool utility::is_routing_manager(const std::shared_ptr<configuration> &_config) void utility::remove_lockfile(const std::shared_ptr<configuration> &_config) { std::lock_guard<std::mutex> its_lock(mutex__); +#ifndef VSOMEIP_ENABLE_CONFIGURATION_OVERLAYS if (!is_checked__) // No need to do anything as automatic return; +#else + if (is_checked__.find(_config->get_network()) == is_checked__.end()) // No need to do anything as automatic + return; +#endif #ifdef _WIN32 if (lock_handle__ != INVALID_HANDLE_VALUE) { @@ -157,7 +173,11 @@ void utility::remove_lockfile(const std::shared_ptr<configuration> &_config) { << ": " << std::strerror(errno); } #endif +#ifndef VSOMEIP_ENABLE_CONFIGURATION_OVERLAYS is_checked__ = false; +#else + is_checked__.erase(_config->get_network()); +#endif } bool utility::exists(const std::string &_path) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8250c1c..23a62aa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2565,6 +2565,74 @@ if(NOT ${TESTS_BAT}) endif() ############################################################################## +# e2e profile 04 test +############################################################################## + +if(NOT ${TESTS_BAT} AND ${TEST_E2E_PROFILE_04}) + set(TEST_E2E_PROFILE_04_NAME e2e_profile_04_test) + set(TEST_E2E_PROFILE_04_SERVICE e2e_profile_04_test_service) + set(TEST_E2E_PROFILE_04_CLIENT e2e_profile_04_test_client) + + add_executable(${TEST_E2E_PROFILE_04_SERVICE} e2e_tests/${TEST_E2E_PROFILE_04_SERVICE}.cpp) + target_link_libraries(${TEST_E2E_PROFILE_04_SERVICE} + vsomeip3 + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + add_executable(${TEST_E2E_PROFILE_04_CLIENT} + e2e_tests/${TEST_E2E_PROFILE_04_CLIENT}.cpp + ) + target_link_libraries(${TEST_E2E_PROFILE_04_CLIENT} + vsomeip3 + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + # Copy service config file for external allow tests into $BUILDDIR/test + set(TEST_E2E_PROFILE_04_SERVICE_CONFIG_FILE_EXTERNAL ${TEST_E2E_PROFILE_04_NAME}_service_external.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/conf/${TEST_E2E_PROFILE_04_SERVICE_CONFIG_FILE_EXTERNAL}.in + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_PROFILE_04_SERVICE_CONFIG_FILE_EXTERNAL} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_PROFILE_04_SERVICE_CONFIG_FILE_EXTERNAL} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_PROFILE_04_SERVICE_CONFIG_FILE_EXTERNAL} + ${TEST_E2E_PROFILE_04_SERVICE} + ) + + # Copy client config file for external allow tests into $BUILDDIR/test + set(TEST_E2E_PROFILE_04_CLIENT_CONFIG_FILE_EXTERNAL ${TEST_E2E_PROFILE_04_NAME}_client_external.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/conf/${TEST_E2E_PROFILE_04_CLIENT_CONFIG_FILE_EXTERNAL}.in + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_PROFILE_04_CLIENT_CONFIG_FILE_EXTERNAL} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_PROFILE_04_CLIENT_CONFIG_FILE_EXTERNAL} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_PROFILE_04_CLIENT_CONFIG_FILE_EXTERNAL} + ${TEST_E2E_PROFILE_04_SERVICE} + ) + + # Copy bashscript to start external tests (master) into $BUILDDIR/test + set(TEST_E2E_PROFILE_04_EXTERNAL_MASTER_START_SCRIPT ${TEST_E2E_PROFILE_04_NAME}_external_master_start.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_PROFILE_04_EXTERNAL_MASTER_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_PROFILE_04_EXTERNAL_MASTER_START_SCRIPT} + ${TEST_E2E_PROFILE_04_SERVICE} + ) + + # Copy bashscript to start external tests (slave) into $BUILDDIR/test + set(TEST_E2E_PROFILE_04_EXTERNAL_SLAVE_START_SCRIPT ${TEST_E2E_PROFILE_04_NAME}_external_slave_start.sh) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/e2e_tests/${TEST_E2E_PROFILE_04_EXTERNAL_SLAVE_START_SCRIPT} + ${PROJECT_BINARY_DIR}/test/${TEST_E2E_PROFILE_04_EXTERNAL_SLAVE_START_SCRIPT} + ${TEST_E2E_PROFILE_04_SERVICE} + ) +endif() + +############################################################################## # event tests ############################################################################## @@ -3053,6 +3121,10 @@ if(NOT ${TESTS_BAT}) endif() add_dependencies(${TEST_E2E_SERVICE} gtest) add_dependencies(${TEST_E2E_CLIENT} gtest) + if (${TEST_E2E_PROFILE_04}) + add_dependencies(${TEST_E2E_PROFILE_04_SERVICE} gtest) + add_dependencies(${TEST_E2E_PROFILE_04_CLIENT} gtest) + endif() add_dependencies(${TEST_EVENT_SERVICE} gtest) add_dependencies(${TEST_EVENT_CLIENT} gtest) add_dependencies(${TEST_NPDU_SERVICE_ONE} gtest) @@ -3129,6 +3201,10 @@ if(NOT ${TESTS_BAT}) add_dependencies(build_tests ${TEST_MALICIOUS_DATA_CLIENT}) add_dependencies(build_tests ${TEST_E2E_SERVICE}) add_dependencies(build_tests ${TEST_E2E_CLIENT}) + if (${TEST_E2E_PROFILE_04}) + add_dependencies(build_tests ${TEST_E2E_PROFILE_04_SERVICE}) + add_dependencies(build_tests ${TEST_E2E_PROFILE_04_CLIENT}) + endif() add_dependencies(build_tests ${TEST_EVENT_SERVICE}) add_dependencies(build_tests ${TEST_EVENT_CLIENT}) add_dependencies(build_tests ${TEST_NPDU_SERVICE_ONE}) @@ -3594,11 +3670,17 @@ if(NOT ${TESTS_BAT}) COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_NPDU_STARTER} TCP sync) set_tests_properties(${TEST_NPDU_NAME}_tcp PROPERTIES TIMEOUT 840) - # e2e test + # e2e tests add_test(NAME ${TEST_E2E_NAME}_external COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_E2E_EXTERNAL_MASTER_START_SCRIPT} e2e_test_client_external.json) set_tests_properties(${TEST_E2E_NAME}_external PROPERTIES TIMEOUT 180) + if (${TEST_E2E_PROFILE_04}) + add_test(NAME ${TEST_E2E_PROFILE_04_NAME}_external + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_E2E_PROFILE_04_EXTERNAL_MASTER_START_SCRIPT} e2e_profile_04_test_client_external.json) + set_tests_properties(${TEST_E2E_PROFILE_04_NAME}_external PROPERTIES TIMEOUT 180) + endif () + # event tests add_test(NAME ${TEST_EVENT_NAME}_payload_fixed_udp COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_EVENT_MASTER_START_SCRIPT} PAYLOAD_FIXED UDP) diff --git a/test/configuration_tests/configuration-test-deprecated.json b/test/configuration_tests/configuration-test-deprecated.json index f9b23d8..5a8b0ee 100644 --- a/test/configuration_tests/configuration-test-deprecated.json +++ b/test/configuration_tests/configuration-test-deprecated.json @@ -321,7 +321,7 @@ }, { "service" : "0x1236", - "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "5678"] + "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "0x5678"] } ] } @@ -390,7 +390,7 @@ }, { "service" : "0x1236", - "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "5678"] + "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "0x5678"] } ] } diff --git a/test/configuration_tests/configuration-test.cpp b/test/configuration_tests/configuration-test.cpp index 4056664..ad4faa7 100644 --- a/test/configuration_tests/configuration-test.cpp +++ b/test/configuration_tests/configuration-test.cpp @@ -638,46 +638,49 @@ void check_file(const std::string &_config_file, uint32_t its_gid = 1000; // policy elements - std::pair<uint32_t, uint32_t> its_uid_range, its_gid_range; - std::set<std::pair<uint32_t, uint32_t>> its_uids, its_gids; - - // fill uid and gid range - std::get<0>(its_uid_range) = its_uid; - std::get<1>(its_uid_range) = its_uid; - std::get<0>(its_gid_range) = its_gid; - std::get<1>(its_gid_range) = its_gid; - its_uids.insert(its_uid_range); - its_gids.insert(its_gid_range); - - _policy->ids_.insert(std::make_pair(its_uids, its_gids)); + boost::icl::discrete_interval<uid_t> its_uids(its_uid, its_uid); + boost::icl::interval_set<gid_t> its_gids; + its_gids.insert(boost::icl::interval<gid_t>::closed(its_gid, its_gid)); + + _policy->credentials_ += std::make_pair(its_uids, its_gids); _policy->allow_who_ = true; _policy->allow_what_ = true; - uint16_t its_service_id = 0x1234; - vsomeip::ids_t its_instance_method_ranges; - vsomeip::ranges_t its_instance_ranges; - its_instance_ranges.insert(std::make_pair(0x01, 0x2)); + vsomeip::service_t its_service(0x1234); - vsomeip::ranges_t its_method_ranges; - its_method_ranges.insert(std::make_pair(0x01, 0x2)); + boost::icl::discrete_interval<vsomeip::instance_t> its_instances(0x1, 0x2); + boost::icl::interval_set<vsomeip::method_t> its_methods; + its_methods.insert(boost::icl::interval<vsomeip::method_t>::closed(0x01, 0x2)); + boost::icl::interval_map<vsomeip::instance_t, + boost::icl::interval_set<vsomeip::method_t> > its_instances_methods; + its_instances_methods += std::make_pair(its_instances, its_methods); - _policy->services_.insert( - std::make_pair(its_service_id, its_instance_method_ranges)); + _policy->requests_ += std::make_pair( + boost::icl::discrete_interval<vsomeip::service_t>( + its_service, its_service, + boost::icl::interval_bounds::closed()), + its_instances_methods); EXPECT_TRUE(its_security->is_policy_update_allowed(1000, _policy)); // test valid policy that holds a single service id which is whitelisted - its_service_id = 0x7800; - _policy->services_.insert( - std::make_pair(its_service_id, its_instance_method_ranges)); + vsomeip::service_t its_second_service(0x7800); + _policy->requests_ += std::make_pair( + boost::icl::discrete_interval<vsomeip::service_t>( + its_second_service, its_second_service, + boost::icl::interval_bounds::closed()), + its_instances_methods); EXPECT_TRUE(its_security->is_policy_update_allowed(1000, _policy)); // test invalid UID which is not whitelisted EXPECT_FALSE(its_security->is_policy_update_allowed(2002, _policy)); // test invalid policy that additionally holds a service id which is not whitelisted - its_service_id = 0x8888; - _policy->services_.insert( - std::make_pair(its_service_id, its_instance_method_ranges)); + vsomeip::service_t its_third_service(0x8888); + _policy->requests_ += std::make_pair( + boost::icl::discrete_interval<vsomeip::service_t>( + its_third_service, its_third_service, + boost::icl::interval_bounds::closed()), + its_instances_methods); EXPECT_FALSE(its_security->is_policy_update_allowed(1000, _policy)); // TCP connection setting: diff --git a/test/configuration_tests/configuration-test.json b/test/configuration_tests/configuration-test.json index 70c34e3..e18026e 100644 --- a/test/configuration_tests/configuration-test.json +++ b/test/configuration_tests/configuration-test.json @@ -310,7 +310,7 @@ }, { "service" : "0x1236", - "instances" : [{ "first" : "0x5676", "last" : "0x5677"}, "5678"] + "instances" : [{ "first" : "0x5676", "last" : "0x5677"}, "0x5678"] } ] } @@ -379,7 +379,7 @@ }, { "service" : "0x1236", - "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "5678"] + "instances" : [{ "first" : "0x5675", "last" : "0x5677"}, "0x5678"] } ] } diff --git a/test/e2e_tests/conf/e2e_profile_04_test_client_external.json.in b/test/e2e_tests/conf/e2e_profile_04_test_client_external.json.in new file mode 100644 index 0000000..6952459 --- /dev/null +++ b/test/e2e_tests/conf/e2e_profile_04_test_client_external.json.in @@ -0,0 +1,56 @@ +{ + "unicast" : "@TEST_IP_MASTER@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "client-sample", + "id" : "0x1255" + } + ], + "e2e" : + { + "e2e_enabled" : "true", + "protected" : + [ + { + "service_id" : "0xd025", + "event_id" : "0x0001", + "profile" : "P04", + "variant" : "checker", + "crc_offset" : "64", + "data_id" : "0x2d" + }, + { + "service_id" : "0xd025", + "event_id" : "0x8001", + "profile" : "P04", + "variant" : "checker", + "crc_offset" : "64", + "data_id" : "0x2d" + } + ] + }, + "routing" : "client-sample", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "1000", + "request_response_delay" : "1500" + } +} diff --git a/test/e2e_tests/conf/e2e_profile_04_test_service_external.json.in b/test/e2e_tests/conf/e2e_profile_04_test_service_external.json.in new file mode 100644 index 0000000..efeffef --- /dev/null +++ b/test/e2e_tests/conf/e2e_profile_04_test_service_external.json.in @@ -0,0 +1,57 @@ +{ + "unicast" : "@TEST_IP_SLAVE@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "info", + "console" : "true", + "file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" }, + "dlt" : "false" + }, + "applications" : + [ + { + "name" : "service-sample", + "id" : "0x1277", + "has_session_handling" : "false" + } + ], + "services" : + [ + { + "service" : "0xd025", + "instance" : "0x0001", + "unreliable" : "30501" + } + ], + "e2e" : + { + "e2e_enabled" : "true", + "protected" : + [ + { + "service_id" : "0xd025", + "event_id" : "0x0001", + "profile" : "P04", + "variant" : "protector", + "crc_offset" : "64", + "data_id" : "0x2d" + } + ] + }, + "routing" : "service-sample", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp", + "initial_delay_min" : "10", + "initial_delay_max" : "100", + "repetitions_base_delay" : "200", + "repetitions_max" : "3", + "ttl" : "3", + "cyclic_offer_delay" : "2000", + "request_response_delay" : "1500" + } +} diff --git a/test/e2e_tests/e2e_profile_04_test_client.cpp b/test/e2e_tests/e2e_profile_04_test_client.cpp new file mode 100644 index 0000000..7056e16 --- /dev/null +++ b/test/e2e_tests/e2e_profile_04_test_client.cpp @@ -0,0 +1,313 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "e2e_profile_04_test_common.hpp" +#include "e2e_profile_04_test_client.hpp" + +#include <vsomeip/internal/logger.hpp> + +std::vector<std::vector<vsomeip::byte_t>> responses_; +std::vector<std::vector<vsomeip::byte_t>> events_; + +std::map<vsomeip::method_t, uint32_t> counters_; + + +e2e_profile_04_test_client::e2e_profile_04_test_client() + : app_(vsomeip::runtime::get()->create_application()), + is_available_(false), + sender_(std::bind(&e2e_profile_04_test_client::run, this)), + received_(0) { + +} + +bool +e2e_profile_04_test_client::init() { + + if (!app_->init()) { + ADD_FAILURE() << __func__ << ": Cannot initialize application"; + return (false); + } + + app_->register_state_handler( + std::bind(&e2e_profile_04_test_client::on_state, this, + std::placeholders::_1)); + + app_->register_message_handler( + PROFILE_04_SERVICE, PROFILE_04_INSTANCE, vsomeip::ANY_METHOD, + std::bind(&e2e_profile_04_test_client::on_message, this, + std::placeholders::_1)); + + app_->register_availability_handler( + PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + std::bind(&e2e_profile_04_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + + return (true); +} + +void +e2e_profile_04_test_client::start() { + + VSOMEIP_INFO << __func__ << ": Starting..."; + app_->start(); +} + +void +e2e_profile_04_test_client::stop() { + + VSOMEIP_INFO << __func__ << ": Stopping..."; + shutdown_service(); + app_->clear_all_handler(); + app_->stop(); +} + +void +e2e_profile_04_test_client::on_state(vsomeip::state_type_e _state) { + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + app_->request_service(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, false); + + // request event 0x8001, that is protected by E2E Profile 04 + app_->request_event(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + PROFILE_04_EVENT, { PROFILE_04_EVENTGROUP }, + vsomeip::event_type_e::ET_FIELD, + vsomeip::reliability_type_e::RT_UNRELIABLE); + } +} + +void +e2e_profile_04_test_client::on_availability( + vsomeip::service_t _service, vsomeip::instance_t _instance, + bool _is_available) { + + VSOMEIP_INFO << __func__ << ": Client " + << std::hex << std::setw(4) << std::setfill('0') + << app_->get_client() + << " : Service [" << _service << "." << _instance + << "] is " << (_is_available ? "available." : "NOT available."); + + // check that correct service / instance ID gets available + if (_is_available) { + EXPECT_EQ(PROFILE_04_SERVICE, _service); + EXPECT_EQ(PROFILE_04_INSTANCE, _instance); + } + + if (PROFILE_04_SERVICE == _service && PROFILE_04_INSTANCE == _instance) { + std::unique_lock<std::mutex> its_lock(mutex_); + if (is_available_ && !_is_available) { + is_available_ = false; + } else if(_is_available && !is_available_) { + is_available_ = true; + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + app_->subscribe(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + PROFILE_04_EVENTGROUP, PROFILE_04_MAJOR); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + condition_.notify_one(); + } + } +} + +void +e2e_profile_04_test_client::on_message(const std::shared_ptr<vsomeip::message> &_message) { + + VSOMEIP_INFO << __func__ << ": Received a message from Service [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "." << _message->get_instance() + << "] to Client/Session [" + << _message->get_client() << "/" << _message->get_session() + << "]"; + + EXPECT_EQ(PROFILE_04_SERVICE, _message->get_service()); + EXPECT_EQ(PROFILE_04_INSTANCE, _message->get_instance()); + + // check fixed payload / CRC in response for service: d025 method: 0001 + if (vsomeip::message_type_e::MT_RESPONSE == _message->get_message_type() + && PROFILE_04_METHOD == _message->get_method()) { + // check for calculated CRC status OK for the predefined fixed payload sent by service + VSOMEIP_INFO << "Method ID 0x0001 -> IS_VALID_CRC = " + << std::boolalpha << _message->is_valid_crc(); + EXPECT_EQ(true, _message->is_valid_crc()); + + // check if payload is as expected as well (including CRC / counter / data ID) + std::shared_ptr<vsomeip::payload> its_payload = _message->get_payload(); + const auto its_data = its_payload->get_data(); + for (size_t i = 0; i < its_payload->get_length(); i++) + EXPECT_EQ(its_data[i], responses_[counters_[PROFILE_04_METHOD] + % PROFILE_O4_NUM_MESSAGES][i]); + + counters_[PROFILE_04_METHOD]++; + + } else if (vsomeip::message_type_e::MT_NOTIFICATION == _message->get_message_type() + && PROFILE_04_EVENT == _message->get_method()) { + + // check CRC / payload calculated by sender for event 0x8001 against expected payload + // check for calculated CRC status OK for the calculated CRC / payload sent by service + VSOMEIP_INFO << __func__ << ": Event 0x8001 -> IS_VALID_CRC = " + << std::boolalpha << _message->is_valid_crc(); + EXPECT_EQ(true, _message->is_valid_crc()); + + // check if payload is as expected as well (including CRC / counter / data ID nibble) + std::shared_ptr<vsomeip::payload> its_payload = _message->get_payload(); + const auto its_data = its_payload->get_data(); + for (size_t i = 0; i< its_payload->get_length(); i++) + EXPECT_EQ(its_data[i], events_[counters_[PROFILE_04_EVENT] + % PROFILE_O4_NUM_MESSAGES][i]); + + counters_[PROFILE_04_EVENT]++; + } + + received_++; + if (received_ == PROFILE_O4_NUM_MESSAGES * 2) { + VSOMEIP_WARNING << __func__ << ": Client" + << std::setw(4) << std::setfill('0') << std::hex + << app_->get_client() + << " received all messages ~> going down!"; + } +} + +void +e2e_profile_04_test_client::run() { + + for (int i = 0; i < PROFILE_O4_NUM_MESSAGES; ++i) { + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (!is_available_) { + condition_.wait(its_lock); + } + } + + auto request = vsomeip::runtime::get()->create_request(false); + request->set_service(PROFILE_04_SERVICE); + request->set_instance(PROFILE_04_INSTANCE); + request->set_interface_version(PROFILE_04_MAJOR); + + // send a request which is not e2e protected and expect an + // protected answer holding a fixed payload (E2E Profile 04) + // this call triggers also an event 0x8001 which holds a + // calculated payload + request->set_method(PROFILE_04_METHOD); + + app_->send(request); + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + + stop(); +} + +void +e2e_profile_04_test_client::join_sender_thread() { + + if (sender_.joinable()) { + sender_.join(); + } +} + +void +e2e_profile_04_test_client::shutdown_service() { + + auto request = vsomeip::runtime::get()->create_request(false); + request->set_service(PROFILE_04_SERVICE); + request->set_instance(PROFILE_04_INSTANCE); + request->set_method(PROFILE_04_SHUTDOWN); + request->set_interface_version(PROFILE_04_MAJOR); + + app_->send(request); + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + + // expect 10 responses + 10 events + EXPECT_EQ(received_, PROFILE_O4_NUM_MESSAGES * 2); +} + +TEST(someip_e2e_profile_04_test, test_crc_calculation) { + + e2e_profile_04_test_client test_client; + + if (test_client.init()) { + test_client.start(); + test_client.join_sender_thread(); + } +} + +int main(int argc, char** argv) { + + responses_ = { + { + 0x00, 0x50, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2d, + 0xaa, 0x1d, 0x3f, 0xdf, 0x08, 0xb7, 0xf4, 0x4c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3d, 0x83, 0x3e, 0xba, 0x68, 0xed, 0x3f, 0xb3, + 0x7a, 0xf2, 0xbd, 0x96, 0xc1, 0x42, 0x3d, 0x25, + 0x1a, 0x62, 0xbd, 0xae, 0x77, 0xf3, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1d, 0xbd, + 0x4e, 0x01, 0x01, 0x3c, 0x2b, 0x87, 0xed, 0x00 + }, + { + 0x00, 0x50, 0x00, 0x01, 0x01, 0x00, 0x00, 0x2d, + 0xe7, 0xb7, 0x13, 0x87, 0x0c, 0x69, 0x02, 0x1c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3c, 0x2f, 0x3e, 0xba, 0x46, 0x81, 0x3f, 0xb3, + 0x73, 0x8d, 0xbd, 0x93, 0xcb, 0xae, 0x3c, 0xf7, + 0xd2, 0x58, 0xbd, 0xa2, 0x6e, 0xcd, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x89, + 0x24, 0x01, 0x01, 0x3c, 0x2b, 0x24, 0x45, 0x00 + }, + { + 0x00, 0x50, 0x00, 0x02, 0x01, 0x00, 0x00, 0x2d, + 0xb6, 0x19, 0x94, 0x2c, 0x10, 0x1b, 0x28, 0xae, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3e, 0xf3, 0x3e, 0xba, 0x97, 0x45, 0x3f, 0xb3, + 0x86, 0x81, 0xbd, 0x8a, 0xda, 0xc2, 0x3c, 0xf6, + 0x00, 0x7a, 0xbd, 0xb4, 0xf9, 0xb9, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x1b, + 0x72, 0x01, 0x01, 0x3c, 0x2a, 0x9e, 0x1f, 0x00 + } + }; + + events_ = { + { + 0x00, 0x50, 0x8f, 0x81, 0x01, 0x00, 0x00, 0x2d, + 0xed, 0x6e, 0x78, 0x8d, 0x08, 0xb7, 0xf4, 0x4c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3d, 0x83, 0x3e, 0xba, 0x68, 0xed, 0x3f, 0xb3, + 0x7a, 0xf2, 0xbd, 0x96, 0xc1, 0x42, 0x3d, 0x25, + 0x1a, 0x62, 0xbd, 0xae, 0x77, 0xf3, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1d, 0xbd, + 0x4e, 0x01, 0x01, 0x3c, 0x2b, 0x87, 0xed, 0x00 + }, + { + 0x00, 0x50, 0x8f, 0x82, 0x01, 0x00, 0x00, 0x2d, + 0x9d, 0xbb, 0x49, 0x3f, 0x0c, 0x69, 0x02, 0x1c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3c, 0x2f, 0x3e, 0xba, 0x46, 0x81, 0x3f, 0xb3, + 0x73, 0x8d, 0xbd, 0x93, 0xcb, 0xae, 0x3c, 0xf7, + 0xd2, 0x58, 0xbd, 0xa2, 0x6e, 0xcd, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x89, + 0x24, 0x01, 0x01, 0x3c, 0x2b, 0x24, 0x45, 0x00 + }, + { + 0x00, 0x50, 0x8f, 0x83, 0x01, 0x00, 0x00, 0x2d, + 0x13, 0x04, 0xf8, 0x81, 0x10, 0x1b, 0x28, 0xae, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3e, 0xf3, 0x3e, 0xba, 0x97, 0x45, 0x3f, 0xb3, + 0x86, 0x81, 0xbd, 0x8a, 0xda, 0xc2, 0x3c, 0xf6, + 0x00, 0x7a, 0xbd, 0xb4, 0xf9, 0xb9, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x1b, + 0x72, 0x01, 0x01, 0x3c, 0x2a, 0x9e, 0x1f, 0x00 + } + }; + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/e2e_tests/e2e_profile_04_test_client.hpp b/test/e2e_tests/e2e_profile_04_test_client.hpp new file mode 100644 index 0000000..ad00291 --- /dev/null +++ b/test/e2e_tests/e2e_profile_04_test_client.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef E2E_PROFILE_04_TEST_CLIENT_HPP_ +#define E2E_PROFILE_04_TEST_CLIENT_HPP_ + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> + +#include <thread> +#include <mutex> +#include <condition_variable> +#include <atomic> + +class e2e_profile_04_test_client { +public: + e2e_profile_04_test_client(); + + bool init(); + void start(); + void stop(); + + void on_state(vsomeip::state_type_e _state); + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available); + void on_message(const std::shared_ptr<vsomeip::message> &_response); + + void run(); + void join_sender_thread(); + +private: + void shutdown_service(); + + std::shared_ptr<vsomeip::application> app_; + + std::mutex mutex_; + std::condition_variable condition_; + bool is_available_; + + std::thread sender_; + + std::atomic<uint32_t> received_; +}; + +#endif // E2E_PROFILE_04_TEST_CLIENT_HPP_ diff --git a/test/e2e_tests/e2e_profile_04_test_common.hpp b/test/e2e_tests/e2e_profile_04_test_common.hpp new file mode 100644 index 0000000..04cba23 --- /dev/null +++ b/test/e2e_tests/e2e_profile_04_test_common.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef E2E_PROFILE_04_TEST_COMMON_HPP_ +#define E2E_PROFILE_04_TEST_COMMON_HPP_ + +#include <vsomeip/vsomeip.hpp> + +const vsomeip::service_t PROFILE_04_SERVICE = 0xd025; +const vsomeip::instance_t PROFILE_04_INSTANCE = 0x0001; +const vsomeip::major_version_t PROFILE_04_MAJOR = 0x01; +const vsomeip::minor_version_t PROFILE_04_MINOR = 0x00000000; + +const vsomeip::method_t PROFILE_04_METHOD = 0x0001; +const vsomeip::method_t PROFILE_04_SHUTDOWN = 0x0002; + +const vsomeip::eventgroup_t PROFILE_04_EVENTGROUP = 0x0001; +const vsomeip::event_t PROFILE_04_EVENT = 0x8001; + +#define PROFILE_O4_NUM_MESSAGES 3 + +#endif // E2E_PROFILE_04_TEST_COMMON_HPP_ diff --git a/test/e2e_tests/e2e_profile_04_test_external_master_start.sh b/test/e2e_tests/e2e_profile_04_test_external_master_start.sh new file mode 100755 index 0000000..ce16d71 --- /dev/null +++ b/test/e2e_tests/e2e_profile_04_test_external_master_start.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 1 ] +then + echo "Please pass a json file to this script" + echo "For example: $0 e2e_profile_04_test_client_external.json" + exit 1 +fi + +MASTER_JSON_FILE=$1 +SERVICE_JSON_FILE=${MASTER_JSON_FILE/client/service} +ALLOW_DENY=$2 + +FAIL=0 + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=client-sample +./e2e_profile_04_test_client --remote & +PID_CLIENT=$! + + +if [ ! -z "$USE_LXC_TEST" ]; then + echo "starting external e2e profile 04 test on slave LXC" + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP "bash -ci \"set -m; cd \\\$SANDBOX_TARGET_DIR/vsomeip_lib/test; ./e2e_profile_04_test_external_slave_start.sh $SERVICE_JSON_FILE\"" & +elif [ ! -z "$USE_DOCKER" ]; then + docker run --name citms --cap-add NET_ADMIN $DOCKER_IMAGE sh -c "route add -net 224.0.0.0/4 dev eth0 && cd $DOCKER_TESTS && ./e2e_profile_04_test_external_slave_start.sh $SERVICE_JSON_FILE" & +else +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** e2e_profile_04_test_external_slave_start.sh $SERVICE_JSON_FILE +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** e2e_profile_04_test_service_external.json and +** e2e_profile_04_test_client_external.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message +fi + +# Wait until client and service are finished +for client_pid in "${PID_CLIENT}" +do + if [ -n "$client_pid" ]; then + # Fail gets incremented if either client or service exit + # with a non-zero exit code + wait "$client_pid" || ((FAIL+=1)) + fi +done + +if [ ! -z "$USE_DOCKER" ]; then + docker stop citms + docker rm citms +fi + +kill $PID_CLIENT + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/e2e_tests/e2e_profile_04_test_external_slave_start.sh b/test/e2e_tests/e2e_profile_04_test_external_slave_start.sh new file mode 100755 index 0000000..afd692b --- /dev/null +++ b/test/e2e_tests/e2e_profile_04_test_external_slave_start.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright (C) 2015-2018 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 1 ] +then + echo "Please pass a json file to this script" + echo "For example: $0 e2e_profile_04_test_service_external.json" + exit 1 +fi + +FAIL=0 + +export VSOMEIP_CONFIGURATION=$1 +export VSOMEIP_APPLICATION_NAME=service-sample +./e2e_profile_04_test_service --remote & +PID_SERVICE=$! + +# Wait until client and service are finished +for client_pid in "${PID_SERVICE}" +do + if [ -n "$client_pid" ]; then + # Fail gets incremented if either client or service exit + # with a non-zero exit code + wait "$client_pid" || ((FAIL+=1)) + fi +done + +if [ ! -z "$USE_DOCKER" ]; then + docker stop citms + docker rm citms +fi + +kill $PID_SERVICE + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/e2e_tests/e2e_profile_04_test_service.cpp b/test/e2e_tests/e2e_profile_04_test_service.cpp new file mode 100644 index 0000000..ca54084 --- /dev/null +++ b/test/e2e_tests/e2e_profile_04_test_service.cpp @@ -0,0 +1,324 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "e2e_profile_04_test_common.hpp" +#include "e2e_profile_04_test_service.hpp" + +static bool is_remote_test = false; +static bool remote_client_allowed = true; + +std::vector<std::vector<vsomeip::byte_t> > responses_; +std::vector<std::vector<vsomeip::byte_t> > events_; + +std::map<vsomeip::method_t, uint32_t> counters_; + +e2e_profile_04_test_service::e2e_profile_04_test_service() + : app_(vsomeip::runtime::get()->create_application()), + is_registered_(false), + blocked_(false), + offer_thread_(std::bind(&e2e_profile_04_test_service::run, this)), + received_(0) { +} + +bool +e2e_profile_04_test_service::init() { + + std::lock_guard<std::mutex> its_lock(mutex_); + + if (!app_->init()) { + ADD_FAILURE() << __func__ << ": Cannot initialize application."; + return false; + } + + app_->register_message_handler(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + PROFILE_04_METHOD, + std::bind(&e2e_profile_04_test_service::on_message, this, + std::placeholders::_1)); + + app_->register_message_handler(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + PROFILE_04_SHUTDOWN, + std::bind(&e2e_profile_04_test_service::on_message_shutdown, this, + std::placeholders::_1)); + + app_->register_state_handler( + std::bind(&e2e_profile_04_test_service::on_state, this, + std::placeholders::_1)); + + // E2E Profile 04: Event 8001 + app_->offer_event(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + PROFILE_04_EVENT, { PROFILE_04_EVENTGROUP }, + vsomeip::event_type_e::ET_FIELD, std::chrono::milliseconds::zero(), + false, true, nullptr, vsomeip::reliability_type_e::RT_UNRELIABLE); + + // Initialize the attribute + auto its_payload = vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data[] = { + 0x00, 0x50, 0x8f, 0x80, 0x01, 0x00, 0x00, 0x2d, + 0xf3, 0x2a, 0x8c, 0x89, 0x05, 0x04, 0xcc, 0x46, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x40, 0xb1, 0x3e, 0xba, 0xc4, 0x76, 0x3f, 0xb3, + 0x7b, 0x03, 0xbd, 0x95, 0x74, 0x53, 0x3d, 0x32, + 0x4b, 0x9d, 0xbd, 0xbc, 0xd6, 0x3b, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1f, 0xf5, + 0xf6, 0x01, 0x01, 0x3c, 0x2b, 0xb1, 0xa2, 0x00 + }; + its_payload->set_data(its_data, sizeof(its_data)); + + app_->notify(vsomeip_test::TEST_SERVICE_SERVICE_ID, vsomeip_test::TEST_SERVICE_INSTANCE_ID, + static_cast<vsomeip::event_t>(0x8001), its_payload); + + return (true); +} + +void +e2e_profile_04_test_service::start() { + + VSOMEIP_INFO << __func__ << ": Starting..."; + app_->start(); +} + +void +e2e_profile_04_test_service::stop() { + + VSOMEIP_INFO << __func__ << ": Stopping..."; + app_->clear_all_handler(); + app_->stop(); +} + +void +e2e_profile_04_test_service::join_offer_thread() { + + if (offer_thread_.joinable()) { + offer_thread_.join(); + } +} + +void +e2e_profile_04_test_service::offer() { + + app_->offer_service(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + PROFILE_04_MAJOR, PROFILE_04_MINOR); +} + +void +e2e_profile_04_test_service::stop_offer() { + + app_->stop_offer_service(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, + PROFILE_04_MAJOR, PROFILE_04_MINOR); +} + +void +e2e_profile_04_test_service::on_state(vsomeip::state_type_e _state) { + + VSOMEIP_INFO << __func__ << ": Application " + << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + if (!is_registered_) { + is_registered_ = true; + + std::lock_guard<std::mutex> its_lock(mutex_); + blocked_ = true; + + // "start" the run method thread + condition_.notify_one(); + } + } else { + is_registered_ = false; + } +} + +void +e2e_profile_04_test_service::on_message( + const std::shared_ptr<vsomeip::message> &_request) { + + ASSERT_EQ(PROFILE_04_SERVICE, _request->get_service()); + ASSERT_EQ(PROFILE_04_INSTANCE, _request->get_instance()); + + VSOMEIP_INFO << "Received a message with Client/Session [" + << std::setw(4) << std::setfill('0') << std::hex + << _request->get_client() << "/" << _request->get_session() + << "] method: " << _request->get_method() ; + + std::shared_ptr<vsomeip::message> its_response = + vsomeip::runtime::get()->create_response(_request); + std::shared_ptr< vsomeip::payload > its_response_payload = + vsomeip::runtime::get()->create_payload(); + std::shared_ptr<vsomeip::payload> its_event_payload = + vsomeip::runtime::get()->create_payload(); + + // send fixed payload for profile 01 CRC8 + if (PROFILE_04_METHOD == _request->get_method()) { + its_response_payload->set_data(responses_[counters_[PROFILE_04_METHOD] % PROFILE_O4_NUM_MESSAGES]); + its_response->set_payload(its_response_payload); + app_->send(its_response); + + counters_[PROFILE_04_METHOD]++; + + // set value to field which gets filled by e2e protection with CRC on sending + its_event_payload->set_data(events_[counters_[PROFILE_04_EVENT] % PROFILE_O4_NUM_MESSAGES]); + app_->notify(PROFILE_04_SERVICE, PROFILE_04_INSTANCE, PROFILE_04_EVENT, its_event_payload); + + counters_[PROFILE_04_EVENT]++; + } + + received_++; + if (received_ == PROFILE_O4_NUM_MESSAGES) { + VSOMEIP_INFO << __func__ << ": Received all messages!"; + } +} + +void +e2e_profile_04_test_service::on_message_shutdown( + const std::shared_ptr<vsomeip::message> &_request) { + + (void)_request; + VSOMEIP_INFO << __func__ << ": Shutdown method was called, going down now."; + stop(); +} + +void +e2e_profile_04_test_service::run() { + + std::unique_lock<std::mutex> its_lock(mutex_); + while (!blocked_) + condition_.wait(its_lock); + + offer(); +} + +TEST(someip_e2e_profile_04_test, basic_subscribe_request_response) { + e2e_profile_04_test_service test_service; + if (test_service.init()) { + test_service.start(); + test_service.join_offer_thread(); + } +} + +#ifndef _WIN32 +int main(int argc, char** argv) { + + + counters_[PROFILE_04_METHOD] = 0; + counters_[PROFILE_04_EVENT] = 0; + + std::string test_remote("--remote"); + std::string test_local("--local"); + std::string test_allow_remote_client("--allow"); + std::string test_deny_remote_client("--deny"); + std::string help("--help"); + + int i = 1; + while (i < argc) + { + if(test_remote == argv[i]) + { + is_remote_test = true; + } + else if(test_local == argv[i]) + { + is_remote_test = false; + } + else if(test_allow_remote_client == argv[i]) + { + remote_client_allowed = true; + } + else if(test_deny_remote_client == argv[i]) + { + remote_client_allowed = false; + } + else if(help == argv[i]) + { + VSOMEIP_INFO << "Parameters:\n" + << "--remote: Run test between two hosts\n" + << "--local: Run test locally\n" + << "--allow: test is started with a policy that allows remote messages sent by this test client to the service\n" + << "--deny: test is started with a policy that denies remote messages sent by this test client to the service\n" + << "--help: print this help"; + } + i++; + } + + // Payloads (without counter, data id and crc) + responses_ = { + { + 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0xb7, 0xf4, 0x4c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3d, 0x83, 0x3e, 0xba, 0x68, 0xed, 0x3f, 0xb3, + 0x7a, 0xf2, 0xbd, 0x96, 0xc1, 0x42, 0x3d, 0x25, + 0x1a, 0x62, 0xbd, 0xae, 0x77, 0xf3, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1d, 0xbd, + 0x4e, 0x01, 0x01, 0x3c, 0x2b, 0x87, 0xed, 0x00 + }, + { + 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x69, 0x02, 0x1c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3c, 0x2f, 0x3e, 0xba, 0x46, 0x81, 0x3f, 0xb3, + 0x73, 0x8d, 0xbd, 0x93, 0xcb, 0xae, 0x3c, 0xf7, + 0xd2, 0x58, 0xbd, 0xa2, 0x6e, 0xcd, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x89, + 0x24, 0x01, 0x01, 0x3c, 0x2b, 0x24, 0x45, 0x00 + }, + { + 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x1b, 0x28, 0xae, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3e, 0xf3, 0x3e, 0xba, 0x97, 0x45, 0x3f, 0xb3, + 0x86, 0x81, 0xbd, 0x8a, 0xda, 0xc2, 0x3c, 0xf6, + 0x00, 0x7a, 0xbd, 0xb4, 0xf9, 0xb9, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x1b, + 0x72, 0x01, 0x01, 0x3c, 0x2a, 0x9e, 0x1f, 0x00 + } + }; + + // Payloads (full data with counter, data id and crc to be sent raw) + events_ = { + { + 0x00, 0x50, 0x8f, 0x81, 0x01, 0x00, 0x00, 0x2d, + 0xed, 0x6e, 0x78, 0x8d, 0x08, 0xb7, 0xf4, 0x4c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3d, 0x83, 0x3e, 0xba, 0x68, 0xed, 0x3f, 0xb3, + 0x7a, 0xf2, 0xbd, 0x96, 0xc1, 0x42, 0x3d, 0x25, + 0x1a, 0x62, 0xbd, 0xae, 0x77, 0xf3, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1d, 0xbd, + 0x4e, 0x01, 0x01, 0x3c, 0x2b, 0x87, 0xed, 0x00 + }, + { + 0x00, 0x50, 0x8f, 0x82, 0x01, 0x00, 0x00, 0x2d, + 0x9d, 0xbb, 0x49, 0x3f, 0x0c, 0x69, 0x02, 0x1c, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3c, 0x2f, 0x3e, 0xba, 0x46, 0x81, 0x3f, 0xb3, + 0x73, 0x8d, 0xbd, 0x93, 0xcb, 0xae, 0x3c, 0xf7, + 0xd2, 0x58, 0xbd, 0xa2, 0x6e, 0xcd, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x89, + 0x24, 0x01, 0x01, 0x3c, 0x2b, 0x24, 0x45, 0x00 + }, + { + 0x00, 0x50, 0x8f, 0x83, 0x01, 0x00, 0x00, 0x2d, + 0x13, 0x04, 0xf8, 0x81, 0x10, 0x1b, 0x28, 0xae, + 0x00, 0x00, 0x09, 0x3d, 0x00, 0x01, 0x06, 0xfe, + 0x01, 0x3e, 0x4c, 0xcc, 0xcd, 0x80, 0x3f, 0xb2, + 0x3e, 0xf3, 0x3e, 0xba, 0x97, 0x45, 0x3f, 0xb3, + 0x86, 0x81, 0xbd, 0x8a, 0xda, 0xc2, 0x3c, 0xf6, + 0x00, 0x7a, 0xbd, 0xb4, 0xf9, 0xb9, 0x3f, 0x80, + 0x00, 0x00, 0xfc, 0x01, 0x01, 0x3c, 0x1c, 0x1b, + 0x72, 0x01, 0x01, 0x3c, 0x2a, 0x9e, 0x1f, 0x00 + } + }; + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/e2e_tests/e2e_profile_04_test_service.hpp b/test/e2e_tests/e2e_profile_04_test_service.hpp new file mode 100644 index 0000000..626db05 --- /dev/null +++ b/test/e2e_tests/e2e_profile_04_test_service.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef E2E_PROFILE_04_TEST_SERVICE_HPP_ +#define E2E_PROFILE_04_TEST_SERVICE_HPP_ + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> + +#include "../someip_test_globals.hpp" + +#include <atomic> +#include <condition_variable> +#include <mutex> +#include <thread> + +class e2e_profile_04_test_service { +public: + e2e_profile_04_test_service(); + + bool init(); + void start(); + void stop(); + void offer(); + void stop_offer(); + void join_offer_thread(); + void on_state(vsomeip::state_type_e _state); + void on_message(const std::shared_ptr<vsomeip::message> &_request); + void on_message_shutdown(const std::shared_ptr<vsomeip::message> &_request); + void run(); + +private: + std::shared_ptr<vsomeip::application> app_; + bool is_registered_; + + bool blocked_; + std::mutex mutex_; + std::condition_variable condition_; + + std::thread offer_thread_; + + std::atomic<uint32_t> received_; +}; + +#endif // E2E_PROFILE_04_TEST_SERVICE_HPP_ |