diff --git a/core/audio/AudioDecoderManager.cpp b/core/audio/AudioDecoderManager.cpp index 6e89357f5a..0fa4fb90c6 100644 --- a/core/audio/AudioDecoderManager.cpp +++ b/core/audio/AudioDecoderManager.cpp @@ -40,7 +40,7 @@ THE SOFTWARE. # include "audio/AudioDecoderEXT.h" #endif -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" NS_AX_BEGIN diff --git a/core/base/CCUserDefault.h b/core/base/CCUserDefault.h index 9387df8005..245f0c6c3d 100644 --- a/core/base/CCUserDefault.h +++ b/core/base/CCUserDefault.h @@ -32,7 +32,7 @@ THE SOFTWARE. #include #include "mio/mio.hpp" -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" /** * @addtogroup base diff --git a/core/base/ZipUtils.cpp b/core/base/ZipUtils.cpp index 0087d9de98..3b2ad52534 100644 --- a/core/base/ZipUtils.cpp +++ b/core/base/ZipUtils.cpp @@ -48,7 +48,7 @@ #include #include -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" // minizip 1.2.0 is same with other platforms #define unzGoToFirstFile64(A, B, C, D) unzGoToFirstFile2(A, B, C, D, NULL, 0, NULL, 0) diff --git a/core/platform/CCFileUtils.h b/core/platform/CCFileUtils.h index 38be343dbd..1eaac5bcc8 100644 --- a/core/platform/CCFileUtils.h +++ b/core/platform/CCFileUtils.h @@ -2,6 +2,7 @@ Copyright (c) 2010-2013 cocos2d-x.org Copyright (c) 2013-2016 Chukong Technologies Inc. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2022-2023 Bytedance Inc. https://axmolengine.github.io/ diff --git a/core/platform/win32/CCApplication-win32.cpp b/core/platform/win32/CCApplication-win32.cpp index 390154f8c2..221d1f52b2 100644 --- a/core/platform/win32/CCApplication-win32.cpp +++ b/core/platform/win32/CCApplication-win32.cpp @@ -32,7 +32,7 @@ THE SOFTWARE. #include #include -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" #include "ntcvt/ntcvt.hpp" /** @brief This function change the PVRFrame show/hide setting in register. diff --git a/extensions/scripting/lua-bindings/manual/AxluaLoader.cpp b/extensions/scripting/lua-bindings/manual/AxluaLoader.cpp index 677330bcf9..c23a40b9dc 100644 --- a/extensions/scripting/lua-bindings/manual/AxluaLoader.cpp +++ b/extensions/scripting/lua-bindings/manual/AxluaLoader.cpp @@ -31,7 +31,7 @@ THE SOFTWARE. #include "scripting/lua-bindings/manual/CCLuaStack.h" #include "scripting/lua-bindings/manual/CCLuaEngine.h" #include "platform/CCFileUtils.h" -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" USING_NS_AX; diff --git a/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h b/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h index 1f858b12d1..5c773e45fc 100644 --- a/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h +++ b/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h @@ -46,7 +46,7 @@ #include "renderer/backend/VertexLayout.h" #include "ui/GUIDefine.h" -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" #include USING_NS_AX; diff --git a/thirdparty/yasio/bindings/yasio_cclua.cpp b/thirdparty/yasio/bindings/yasio_cclua.cpp index 841458eff0..cf2baa25bd 100644 --- a/thirdparty/yasio/bindings/yasio_cclua.cpp +++ b/thirdparty/yasio/bindings/yasio_cclua.cpp @@ -29,7 +29,7 @@ SOFTWARE. #include "yasio/bindings/lyasio.hpp" #include "yasio/detail/object_pool.hpp" #include "yasio/detail/ref_ptr.hpp" -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" // A workaround to fix compile issue caused by `CCPlatformMacros.h` doesn't handle `__has_attribute` it properly # if !__has_attribute(format) diff --git a/thirdparty/yasio/compiler/feature_test.hpp b/thirdparty/yasio/compiler/feature_test.hpp index 35d46f081f..62f6ca397c 100644 --- a/thirdparty/yasio/compiler/feature_test.hpp +++ b/thirdparty/yasio/compiler/feature_test.hpp @@ -36,6 +36,10 @@ SOFTWARE. # include #endif +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf sprintf_s +#endif + // Tests whether compiler has(fully) c++11 support and keywords workaround for msvc #if !defined(_MSC_VER) || _MSC_VER >= 1900 # define YASIO__HAS_CXX11 1 @@ -50,8 +54,7 @@ SOFTWARE. #endif // Tests whether compiler has c++14 support -#if (defined(__cplusplus) && __cplusplus >= 201402L) || \ - (defined(_MSC_VER) && _MSC_VER >= 1900 && (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L))) +#if (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_MSC_VER) && _MSC_VER >= 1900 && (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L))) # ifndef YASIO_HAS_CXX14 # define YASIO__HAS_CXX14 1 # endif // C++14 features macro @@ -62,9 +65,7 @@ SOFTWARE. // Tests whether compiler has c++17 support #if (defined(__cplusplus) && __cplusplus >= 201703L) || \ - (defined(_MSC_VER) && _MSC_VER > 1900 && \ - ((defined(_HAS_CXX17) && _HAS_CXX17 == 1) || \ - (defined(_MSVC_LANG) && (_MSVC_LANG > 201402L)))) + (defined(_MSC_VER) && _MSC_VER > 1900 && ((defined(_HAS_CXX17) && _HAS_CXX17 == 1) || (defined(_MSVC_LANG) && (_MSVC_LANG > 201402L)))) # ifndef YASIO_HAS_CXX17 # define YASIO__HAS_CXX17 1 # endif // C++17 features macro @@ -75,9 +76,7 @@ SOFTWARE. // Tests whether compiler has c++20 support #if (defined(__cplusplus) && __cplusplus > 201703L) || \ - (defined(_MSC_VER) && _MSC_VER > 1900 && \ - ((defined(_HAS_CXX20) && _HAS_CXX20 == 1) || \ - (defined(_MSVC_LANG) && (_MSVC_LANG > 201703L)))) + (defined(_MSC_VER) && _MSC_VER > 1900 && ((defined(_HAS_CXX20) && _HAS_CXX20 == 1) || (defined(_MSVC_LANG) && (_MSVC_LANG > 201703L)))) # ifndef YASIO__HAS_CXX20 # define YASIO__HAS_CXX20 1 # endif // C++20 features macro @@ -103,8 +102,7 @@ SOFTWARE. // Tests whether current OS is BSD-like system for process common BSD socket behaviors #if !defined(_WIN32) && !defined(__linux__) # include -# if defined(BSD) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) +# if defined(BSD) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) # define YASIO__OS_BSD_LIKE 1 # else # define YASIO__OS_BSD_LIKE 0 @@ -128,8 +126,7 @@ SOFTWARE. #endif // 64bits Sense Macros -#if defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) || \ - defined(__x86_64) || defined(__arm64__) || defined(__aarch64__) +#if defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) || defined(__x86_64) || defined(__arm64__) || defined(__aarch64__) # define YASIO__64BITS 1 #else # define YASIO__64BITS 0 @@ -149,8 +146,7 @@ SOFTWARE. #endif #include -#define yasio__throw_error(ec, what) \ - YASIO__THROW0(std::system_error(std::error_code{(int)ec, std::system_category()}, what)) +#define yasio__throw_error(ec, what) YASIO__THROW0(std::system_error(std::error_code{(int)ec, std::system_category()}, what)) #define yasio__throw_error0(ec) yasio__throw_error(ec, "") // Compatibility with non-clang compilers... diff --git a/thirdparty/yasio/detail/config.hpp b/thirdparty/yasio/detail/config.hpp index b39eb30d7d..a6f12c9b52 100644 --- a/thirdparty/yasio/detail/config.hpp +++ b/thirdparty/yasio/detail/config.hpp @@ -206,13 +206,6 @@ SOFTWARE. // The default max listen count of tcp server. #define YASIO_SOMAXCONN 19 -// The max wait duration in microseconds when io_service nothing to do. -#define YASIO_MAX_WAIT_DURATION (5LL * 60LL * 1000LL * 1000LL) - -// The min wait duration in microseconds when io_service have outstanding work to do. -// !!!Only affects Single Core CPU -#define YASIO_MIN_WAIT_DURATION 10LL - // The default ttl of multicast #define YASIO_DEFAULT_MULTICAST_TTL (int)128 @@ -238,4 +231,7 @@ SOFTWARE. #define YASIO_SSL_PIN "yasio_ssl_client" #define YASIO_SSL_PIN_LEN (sizeof(YASIO_SSL_PIN) - 1) +#define YASIO_SSL_PON "yasio_ssl_server" +#define YASIO_SSL_PON_LEN (sizeof(YASIO_SSL_PON) - 1) + #endif diff --git a/thirdparty/yasio/detail/mbedtls.inl b/thirdparty/yasio/detail/mbedtls.inl new file mode 100644 index 0000000000..d50a0b248d --- /dev/null +++ b/thirdparty/yasio/detail/mbedtls.inl @@ -0,0 +1,207 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// client application. +////////////////////////////////////////////////////////////////////////////////////////// +/* +The MIT License (MIT) + +Copyright (c) 2012-2023 HALX99 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef YASIO__MBEDTLS_INL +#define YASIO__MBEDTLS_INL + +#if YASIO_SSL_BACKEND == 2 + +YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts) +{ + auto ctx = new ssl_ctx_st(); + ::mbedtls_ctr_drbg_init(&ctx->ctr_drbg); + ::mbedtls_entropy_init(&ctx->entropy); + ::mbedtls_ssl_config_init(&ctx->conf); + ::mbedtls_x509_crt_init(&ctx->cert); + ::mbedtls_pk_init(&ctx->pkey); + + do + { + int ret = 0; + // ssl role + if ((ret = ::mbedtls_ssl_config_defaults(&ctx->conf, opts.client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) + YASIO_LOG("mbedtls_ssl_config_defaults fail with ret=%d", ret); + + // rgn engine + cxx17::string_view pers = opts.client ? cxx17::string_view{YASIO_SSL_PIN, YASIO_SSL_PIN_LEN} : cxx17::string_view{YASIO_SSL_PON, YASIO_SSL_PON_LEN}; + ret = ::mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, ::mbedtls_entropy_func, &ctx->entropy, (const unsigned char*)pers.data(), pers.length()); + if (ret != 0) + { + YASIO_LOG("mbedtls_ctr_drbg_seed fail with ret=%d", ret); + break; + } + ::mbedtls_ssl_conf_rng(&ctx->conf, ::mbedtls_ctr_drbg_random, &ctx->ctr_drbg); + + // --- load cert + int authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; + if (yasio__valid_str(opts.crtfile_)) // the cafile_ must be full path + { + if ((ret = ::mbedtls_x509_crt_parse_file(&ctx->cert, opts.crtfile_)) == 0) + authmode = MBEDTLS_SSL_VERIFY_REQUIRED; + else + { + YASIO_LOG("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret); + break; + } + } + + if (opts.client) + { + ::mbedtls_ssl_conf_authmode(&ctx->conf, authmode); + ::mbedtls_ssl_conf_ca_chain(&ctx->conf, &ctx->cert, nullptr); + } + else + { + // --- load server private key + if (yasio__valid_str(opts.keyfile_)) + { +# if MBEDTLS_VERSION_MAJOR >= 3 + if (::mbedtls_pk_parse_keyfile(&ctx->pkey, opts.keyfile_, nullptr, mbedtls_ctr_drbg_random, &ctx->ctr_drbg) != 0) +# else + if (::mbedtls_pk_parse_keyfile(&ctx->pkey, opts.keyfile_, nullptr) != 0) +# endif + { + YASIO_LOG("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret); + break; + } + } + ::mbedtls_ssl_conf_ca_chain(&ctx->conf, ctx->cert.next, nullptr); + if ((ret = mbedtls_ssl_conf_own_cert(&ctx->conf, &ctx->cert, &ctx->pkey)) != 0) + { + YASIO_LOG("mbedtls_ssl_conf_own_cert with ret=-0x%x", (unsigned int)-ret); + break; + } + } + + } while (false); + + return ctx; +} + +YASIO__DECL void yssl_ctx_free(ssl_ctx_st*& ctx) +{ + if (!ctx) + return; + ::mbedtls_pk_free(&ctx->pkey); + ::mbedtls_x509_crt_free(&ctx->cert); + ::mbedtls_ssl_config_free(&ctx->conf); + ::mbedtls_entropy_free(&ctx->entropy); + ::mbedtls_ctr_drbg_free(&ctx->ctr_drbg); + + delete ctx; + ctx = nullptr; +} + +YASIO__DECL ssl_st* yssl_new(ssl_ctx_st* ctx, int fd, const char* hostname, bool client) +{ + auto ssl = new ssl_st(); + ::mbedtls_ssl_init(ssl); + ::mbedtls_ssl_setup(ssl, &ctx->conf); + + // ssl_set_fd + ssl->bio.fd = fd; + ::mbedtls_ssl_set_bio(ssl, &ssl->bio, ::mbedtls_net_send, ::mbedtls_net_recv, nullptr /* rev_timeout() */); + ::mbedtls_ssl_set_hostname(ssl, hostname); + return ssl; +} +YASIO__DECL void yssl_shutdown(ssl_st*& ssl) +{ + ::mbedtls_ssl_close_notify(ssl); + ::mbedtls_ssl_free(ssl); + delete ssl; + ssl = nullptr; +} +YASIO__DECL int yssl_do_handshake(ssl_st* ssl, int& err) +{ + int ret = ::mbedtls_ssl_handshake_step(ssl); + switch (ret) + { + case 0: + if (ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER) + return 0; + err = EWOULDBLOCK; + ret = -1; + break; + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + err = EWOULDBLOCK; + break; // Nothing need to do + default: + err = yasio::errc::ssl_handshake_failed; + } + return ret; +} +const char* yssl_strerror(ssl_st* ssl, int sslerr, char* buf, size_t buflen) +{ + int n = snprintf(buf, buflen, "error:%d:", sslerr); + ::mbedtls_strerror(sslerr, buf + n, buflen - n); + return buf; +} +YASIO__DECL int yssl_write(ssl_st* ssl, const void* data, size_t len, int& err) +{ + int n = ::mbedtls_ssl_write(ssl, static_cast(data), len); + if (n > 0) + return n; + switch (n) + { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + err = EWOULDBLOCK; + break; + default: + err = yasio::errc::ssl_write_failed; + } + return -1; +} +YASIO__DECL int yssl_read(ssl_st* ssl, void* data, size_t len, int& err) +{ + int n = ::mbedtls_ssl_read(ssl, static_cast(data), len); + if (n > 0) + return n; + switch (n) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: // n=0, the upper caller will regards as eof + n = 0; + case 0: + err = yasio::xxsocket::get_last_errno(); + ::mbedtls_ssl_close_notify(ssl); + break; + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + err = EWOULDBLOCK; + break; + default: + err = yasio::errc::ssl_read_failed; + } + return n; +} + +#endif + +#endif diff --git a/thirdparty/yasio/detail/object_pool.hpp b/thirdparty/yasio/detail/object_pool.hpp index 7705148e6f..e3d1ada37b 100644 --- a/thirdparty/yasio/detail/object_pool.hpp +++ b/thirdparty/yasio/detail/object_pool.hpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////////////////// -// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any // client application. ////////////////////////////////////////////////////////////////////////////////////////// /* @@ -49,33 +49,30 @@ namespace gc #define YASIO_POOL_FL_BEGIN(chunk) reinterpret_cast(chunk->data) #define YASIO_POOL_PREALLOCATE 1 -template static size_t aligned_storage_size() +template +static size_t aligned_storage_size() { return sizeof(typename std::aligned_storage::value>::type); } namespace detail { -class object_pool -{ - typedef struct free_link_node - { +class object_pool { + typedef struct free_link_node { free_link_node* next; - } * free_link; + }* free_link; - typedef struct chunk_link_node - { + typedef struct chunk_link_node { chunk_link_node* next; char data[0]; - } * chunk_link; + }* chunk_link; - object_pool(const object_pool&) = delete; + object_pool(const object_pool&) = delete; void operator=(const object_pool&) = delete; public: OBJECT_POOL_DECL object_pool(size_t element_size, size_t element_count) - : free_link_(nullptr), chunk_(nullptr), element_size_(element_size), - element_count_(element_count) + : free_link_(nullptr), chunk_(nullptr), element_size_(element_size), element_count_(element_count) { #if YASIO_POOL_PREALLOCATE release(allocate_from_process_heap()); // preallocate 1 chunk @@ -93,7 +90,7 @@ public: while ((p = *q) != nullptr) { *q = p->next; - free(p); + delete[] (uint8_t*)(p); } free_link_ = nullptr; @@ -147,8 +144,7 @@ private: } OBJECT_POOL_DECL void* allocate_from_process_heap(void) { - chunk_link new_chunk = - (chunk_link)malloc(sizeof(chunk_link_node) + element_size_ * element_count_); + chunk_link new_chunk = (chunk_link) new uint8_t[sizeof(chunk_link_node) + element_size_ * element_count_]; #ifdef _DEBUG ::memset(new_chunk, 0x00, sizeof(chunk_link_node)); #endif @@ -171,8 +167,7 @@ private: for (char* ptr = chunk->data; ptr < rbegin; ptr += element_size_) { - reinterpret_cast(ptr)->next = - reinterpret_cast(ptr + element_size_); + reinterpret_cast(ptr)->next = reinterpret_cast(ptr + element_size_); } return reinterpret_cast(rbegin); } @@ -185,14 +180,13 @@ private: }; } // namespace detail -template class object_pool : public detail::object_pool -{ +template +class object_pool : public detail::object_pool { public: - object_pool(size_t _ElemCount = 512) - : detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount) - {} + object_pool(size_t _ElemCount = 512) : detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount) {} - template _Ty* construct(_Types&&... args) + template + _Ty* construct(_Types&&... args) { return new (allocate()) _Ty(std::forward<_Types>(args)...); } @@ -218,17 +212,16 @@ public: _Mutex mutex_; }; -template class object_pool<_Ty, void> : public detail::object_pool -{ - object_pool(const object_pool&) = delete; +template +class object_pool<_Ty, void> : public detail::object_pool { + object_pool(const object_pool&) = delete; void operator=(const object_pool&) = delete; public: - object_pool(size_t _ElemCount = 512) - : detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount) - {} + object_pool(size_t _ElemCount = 512) : detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount) {} - template _Ty* construct(_Types&&... args) + template + _Ty* construct(_Types&&... args) { return new (allocate()) _Ty(std::forward<_Types>(args)...); } @@ -244,39 +237,57 @@ public: void deallocate(void* _Ptr) { release(_Ptr); } }; -#define DEFINE_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \ -public: \ - static void* operator new(size_t /*size*/) { return get_pool().allocate(); } \ - \ - static void* operator new(size_t /*size*/, std::nothrow_t) { return get_pool().allocate(); } \ - \ - static void operator delete(void* p) { get_pool().deallocate(p); } \ - \ - static yasio::gc::object_pool& get_pool() \ - { \ - static yasio::gc::object_pool s_pool(ELEMENT_COUNT); \ - return s_pool; \ +#define DEFINE_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \ +public: \ + static void* operator new(size_t /*size*/) \ + { \ + return get_pool().allocate(); \ + } \ + \ + static void* operator new(size_t /*size*/, std::nothrow_t) \ + { \ + return get_pool().allocate(); \ + } \ + \ + static void operator delete(void* p) \ + { \ + get_pool().deallocate(p); \ + } \ + \ + static yasio::gc::object_pool& get_pool() \ + { \ + static yasio::gc::object_pool s_pool(ELEMENT_COUNT); \ + return s_pool; \ } // The thread safe edition -#define DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \ -public: \ - static void* operator new(size_t /*size*/) { return get_pool().allocate(); } \ - \ - static void* operator new(size_t /*size*/, std::nothrow_t) { return get_pool().allocate(); } \ - \ - static void operator delete(void* p) { get_pool().deallocate(p); } \ - \ - static yasio::gc::object_pool& get_pool() \ - { \ - static yasio::gc::object_pool s_pool(ELEMENT_COUNT); \ - return s_pool; \ +#define DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \ +public: \ + static void* operator new(size_t /*size*/) \ + { \ + return get_pool().allocate(); \ + } \ + \ + static void* operator new(size_t /*size*/, std::nothrow_t) \ + { \ + return get_pool().allocate(); \ + } \ + \ + static void operator delete(void* p) \ + { \ + get_pool().deallocate(p); \ + } \ + \ + static yasio::gc::object_pool& get_pool() \ + { \ + static yasio::gc::object_pool s_pool(ELEMENT_COUNT); \ + return s_pool; \ } //////////////////////// allocator ///////////////// // TEMPLATE CLASS object_pool_allocator, can't used by std::vector, DO NOT use at non-msvc compiler. -template class object_pool_allocator -{ // generic allocator for objects of class _Ty +template +class object_pool_allocator { // generic allocator for objects of class _Ty public: typedef _Ty value_type; @@ -292,8 +303,8 @@ public: typedef long difference_type; #endif - template struct rebind - { // convert this type to _ALLOCATOR<_Other> + template + struct rebind { // convert this type to _ALLOCATOR<_Other> typedef object_pool_allocator<_Other> other; }; @@ -315,7 +326,8 @@ public: { // construct by copying (do nothing) } - template object_pool_allocator(const object_pool_allocator<_Other>&) throw() + template + object_pool_allocator(const object_pool_allocator<_Other>&) throw() { // construct from a related allocator (do nothing) } @@ -357,17 +369,20 @@ public: new ((void*)_Ptr) _Ty(std::forward<_Ty>(_Val)); } - template void construct(pointer _Ptr, _Other&& _Val) + template + void construct(pointer _Ptr, _Other&& _Val) { // construct object at _Ptr with value _Val new ((void*)_Ptr) _Ty(std::forward<_Other>(_Val)); } - template void construct(_Objty* _Ptr, _Types&&... _Args) + template + void construct(_Objty* _Ptr, _Types&&... _Args) { // construct _Objty(_Types...) at _Ptr ::new ((void*)_Ptr) _Objty(std::forward<_Types>(_Args)...); } - template void destroy(_Uty* _Ptr) + template + void destroy(_Uty* _Ptr) { // destroy object at _Ptr, do nothing _Ptr->~_Uty(); } @@ -386,15 +401,13 @@ public: }; template -inline bool operator==(const object_pool_allocator<_Ty>&, - const object_pool_allocator<_Other>&) throw() +inline bool operator==(const object_pool_allocator<_Ty>&, const object_pool_allocator<_Other>&) throw() { // test for allocator equality return (true); } template -inline bool operator!=(const object_pool_allocator<_Ty>& _Left, - const object_pool_allocator<_Other>& _Right) throw() +inline bool operator!=(const object_pool_allocator<_Ty>& _Left, const object_pool_allocator<_Other>& _Right) throw() { // test for allocator inequality return (!(_Left == _Right)); } diff --git a/thirdparty/yasio/detail/obstream.hpp b/thirdparty/yasio/detail/obstream.hpp index 48cd944563..0188e84d2f 100644 --- a/thirdparty/yasio/detail/obstream.hpp +++ b/thirdparty/yasio/detail/obstream.hpp @@ -33,7 +33,7 @@ SOFTWARE. #include #include #include -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/string_view.hpp" #include "yasio/detail/endian_portable.hpp" #include "yasio/detail/utils.hpp" #include "yasio/detail/byte_buffer.hpp" diff --git a/thirdparty/yasio/detail/openssl.inl b/thirdparty/yasio/detail/openssl.inl new file mode 100644 index 0000000000..89a04f1da1 --- /dev/null +++ b/thirdparty/yasio/detail/openssl.inl @@ -0,0 +1,187 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// A multi-platform support c++11 library with focus on asynchronous socket I/O for any +// client application. +////////////////////////////////////////////////////////////////////////////////////////// +/* +The MIT License (MIT) + +Copyright (c) 2012-2023 HALX99 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef YASIO__OPENSSL_INL +#define YASIO__OPENSSL_INL + +#if YASIO_SSL_BACKEND == 1 // OpenSSL + +// The ssl error mask (1 << 31), a little hack, but works +# define YSSL_ERR_MASK 0x80000000 + +YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts) +{ + auto ctx = ::SSL_CTX_new(opts.client ? ::SSLv23_client_method() : SSLv23_server_method()); + + auto mode = SSL_CTX_get_mode(ctx); +# if defined(SSL_MODE_RELEASE_BUFFERS) + mode |= SSL_MODE_RELEASE_BUFFERS; +# endif + + ::SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | mode); + + if (opts.client) + { + if (yasio__valid_str(opts.crtfile_)) + { + if (::SSL_CTX_load_verify_locations(ctx, opts.crtfile_, nullptr) == 1) + { + ::SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, ::SSL_CTX_get_verify_callback(ctx)); +# if OPENSSL_VERSION_NUMBER >= 0x10101000L + ::SSL_CTX_set_post_handshake_auth(ctx, 1); +# endif +# if defined(X509_V_FLAG_PARTIAL_CHAIN) + /* Have intermediate certificates in the trust store be treated as + trust-anchors, in the same way as self-signed root CA certificates + are. This allows users to verify servers using the intermediate cert + only, instead of needing the whole chain. */ + X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), X509_V_FLAG_PARTIAL_CHAIN); +# endif + } + else + YASIO_LOG("[global] load ca certifaction file failed!"); + } + else + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + } + else + { + if (yasio__valid_str(opts.crtfile_) && ::SSL_CTX_use_certificate_file(ctx, opts.crtfile_, SSL_FILETYPE_PEM) <= 0) + YASIO_LOG("[gobal] load server cert file failed!"); + + if (yasio__valid_str(opts.keyfile_) && ::SSL_CTX_use_PrivateKey_file(ctx, opts.keyfile_, SSL_FILETYPE_PEM) <= 0) + YASIO_LOG("[gobal] load server private key file failed!"); + } + + return ctx; +} + +YASIO__DECL void yssl_ctx_free(ssl_ctx_st*& ctx) +{ + ::SSL_CTX_free((SSL_CTX*)ctx); + ctx = nullptr; +} +YASIO__DECL ssl_st* yssl_new(ssl_ctx_st* ctx, int fd, const char* hostname, bool client) +{ + auto ssl = ::SSL_new(ctx); + ::SSL_set_fd(ssl, fd); + if (client) + { + ::SSL_set_connect_state(ssl); + ::SSL_set_tlsext_host_name(ssl, hostname); + } + else + ::SSL_set_accept_state(ssl); + return ssl; +} +YASIO__DECL void yssl_shutdown(ssl_st*& ssl) +{ + ::SSL_shutdown(ssl); + ::SSL_free(ssl); + ssl = nullptr; +} +YASIO__DECL int yssl_do_handshake(ssl_st* ssl, int& err) +{ + ERR_clear_error(); + int ret = ::SSL_do_handshake(ssl); + if (ret == 1) // handshake succeed + return 0; + + int sslerr = ::SSL_get_error(ssl, ret); + /* + When using a non-blocking socket, nothing is to be done, but select() can be used to check for + the required condition: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + */ + if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) + { + err = EWOULDBLOCK; + return -1; + } + + /* ssl handshake fail */ + err = yasio::errc::ssl_handshake_failed; // emit ssl handshake failed, continue handle close flow + return (sslerr != SSL_ERROR_SYSCALL) ? static_cast(ERR_get_error() | YSSL_ERR_MASK) : yasio::xxsocket::get_last_errno(); +} +YASIO__DECL const char* yssl_strerror(ssl_st* ssl, int sslerr, char* buf, size_t buflen) +{ + if (yasio__testbits(sslerr, YSSL_ERR_MASK)) + ::ERR_error_string_n((unsigned int)sslerr & ~YSSL_ERR_MASK, buf, buflen); + else + yasio::xxsocket::strerror_r(sslerr, buf, buflen); + return buf; +} +YASIO__DECL int yssl_write(ssl_st* ssl, const void* data, size_t len, int& err) +{ + ERR_clear_error(); + int n = ::SSL_write(ssl, data, static_cast(len)); + if (n > 0) + return n; + + int sslerr = ::SSL_get_error(ssl, n); + if (sslerr == SSL_ERROR_ZERO_RETURN) + { // SSL_ERROR_SYSCALL + err = yasio::xxsocket::get_last_errno(); + return 0; + } + switch (sslerr) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + err = EWOULDBLOCK; + break; + default: + err = yasio::errc::ssl_write_failed; + } + return -1; +} +YASIO__DECL int yssl_read(ssl_st* ssl, void* data, size_t len, int& err) +{ + ERR_clear_error(); + int n = ::SSL_read(ssl, data, static_cast(len)); + if (n > 0) + return n; + int sslerr = ::SSL_get_error(ssl, n); + switch (sslerr) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* The operation did not complete; the same TLS/SSL I/O function + should be called again later. This is basically an EWOULDBLOCK + equivalent. */ + err = EWOULDBLOCK; + break; + default: + err = sslerr == SSL_ERROR_SYSCALL ? yasio::xxsocket::get_last_errno() : yasio::errc::ssl_read_failed; + if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) // peer closed connection in SSL handshake + return 0; + } + return -1; +} +#endif + +#endif diff --git a/thirdparty/yasio/detail/poll_fd_set.hpp b/thirdparty/yasio/detail/poll_fd_set.hpp index eb3963f953..8b0126b122 100644 --- a/thirdparty/yasio/detail/poll_fd_set.hpp +++ b/thirdparty/yasio/detail/poll_fd_set.hpp @@ -26,7 +26,7 @@ public: void reset() { this->fd_set_.clear(); } - int poll_io(timeval& waitd_tv) { return ::poll(this->fd_set_.data(), static_cast(this->fd_set_.size()), waitd_tv.tv_sec * 1000 + waitd_tv.tv_usec / 1000); } + int poll_io(int wait_ms) { return ::poll(this->fd_set_.data(), static_cast(this->fd_set_.size()), wait_ms); } int is_set(socket_native_type fd, int events) const { diff --git a/thirdparty/yasio/detail/select_fd_set.hpp b/thirdparty/yasio/detail/select_fd_set.hpp index 94296e7f81..8cd5aa5fa3 100644 --- a/thirdparty/yasio/detail/select_fd_set.hpp +++ b/thirdparty/yasio/detail/select_fd_set.hpp @@ -36,7 +36,11 @@ public: return *this; } - int poll_io(timeval& waitd_tv) { return ::select(this->max_descriptor_, &(fd_set_[read_op]), &(fd_set_[write_op]), nullptr, &waitd_tv); } + int poll_io(int waitd_ms) + { + timeval waitd_tv = {(decltype(timeval::tv_sec))(waitd_ms / 1000), (decltype(timeval::tv_usec))(waitd_ms % 1000)}; + return ::select(this->max_descriptor_, &(fd_set_[read_op]), &(fd_set_[write_op]), nullptr, &waitd_tv); + } int is_set(socket_native_type fd, int events) const { diff --git a/thirdparty/yasio/detail/socket.hpp b/thirdparty/yasio/detail/socket.hpp index 85875ce402..7db5826a8a 100644 --- a/thirdparty/yasio/detail/socket.hpp +++ b/thirdparty/yasio/detail/socket.hpp @@ -241,12 +241,13 @@ namespace inet struct socket_event { enum { // event mask - read = 1, - write = 2, - error = 4, + null = 0, + read = 1, + write = 2, + error = 4, readwrite = read | write, }; }; -} +} // namespace inet } // namespace yasio #endif diff --git a/thirdparty/yasio/detail/ssl.hpp b/thirdparty/yasio/detail/ssl.hpp index ffdf455a97..c07413741d 100644 --- a/thirdparty/yasio/detail/ssl.hpp +++ b/thirdparty/yasio/detail/ssl.hpp @@ -29,6 +29,8 @@ SOFTWARE. #ifndef YASIO__SSL_HPP #define YASIO__SSL_HPP +#include "yasio/detail/config.hpp" + #if YASIO_SSL_BACKEND == 1 // OpenSSL # include # include @@ -41,27 +43,56 @@ SOFTWARE. # include "mbedtls/entropy.h" # include "mbedtls/ctr_drbg.h" # include "mbedtls/error.h" +# include "mbedtls/version.h" struct ssl_ctx_st { mbedtls_ctr_drbg_context ctr_drbg; mbedtls_entropy_context entropy; - mbedtls_x509_crt cacert; + mbedtls_x509_crt cert; + mbedtls_pk_context pkey; mbedtls_ssl_config conf; }; struct ssl_st : public mbedtls_ssl_context { mbedtls_net_context bio; }; -inline ssl_st* mbedtls_ssl_new(ssl_ctx_st* ctx) -{ - auto ssl = new ssl_st(); - ::mbedtls_ssl_init(ssl); - ::mbedtls_ssl_setup(ssl, &ctx->conf); - return ssl; -} -inline void mbedtls_ssl_set_fd(ssl_st* ssl, int fd) -{ - ssl->bio.fd = fd; - ::mbedtls_ssl_set_bio(ssl, &ssl->bio, ::mbedtls_net_send, ::mbedtls_net_recv, nullptr /* rev_timeout() */); -} +#endif + +#if defined(YASIO_SSL_BACKEND) +struct yssl_options { + const char* crtfile_; + const char* keyfile_; + bool client; +}; + +YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts); +YASIO__DECL void yssl_ctx_free(ssl_ctx_st*& ctx); + +YASIO__DECL ssl_st* yssl_new(ssl_ctx_st* ctx, int fd, const char* hostname, bool client); +YASIO__DECL void yssl_shutdown(ssl_st*&); + +/** +* @returns +* 0: succeed +* other: use yssl_strerror(ret & YSSL_ERROR_MASK, ...) get error message +* - err can bb + - EWOULDBLOCK: status ok, repeat call next time +* - yasio::errc::ssl_handshake_failed: failed +*/ +YASIO__DECL int yssl_do_handshake(ssl_st* ssl, int& err); +YASIO__DECL const char* yssl_strerror(ssl_st* ssl, int sslerr, char* buf, size_t buflen); + +YASIO__DECL int yssl_write(ssl_st* ssl, const void* data, size_t len, int& err); +YASIO__DECL int yssl_read(ssl_st* ssl, void* data, size_t len, int& err); +#endif + +/////////////////////////////////////////////////////////////////// +// --- Implement common yasio ssl api with different ssl backends + +#define yasio__valid_str(str) (str && *str) + +#if YASIO_SSL_BACKEND == 1 // openssl +# include "yasio/detail/openssl.inl" +#elif YASIO_SSL_BACKEND == 2 // mbedtls +# include "yasio/detail/mbedtls.inl" #else # error "yasio - Unsupported ssl backend provided!" #endif diff --git a/thirdparty/yasio/cxx17/memory.hpp b/thirdparty/yasio/stl/memory.hpp similarity index 100% rename from thirdparty/yasio/cxx17/memory.hpp rename to thirdparty/yasio/stl/memory.hpp diff --git a/thirdparty/yasio/cxx17/shared_mutex.hpp b/thirdparty/yasio/stl/shared_mutex.hpp similarity index 100% rename from thirdparty/yasio/cxx17/shared_mutex.hpp rename to thirdparty/yasio/stl/shared_mutex.hpp diff --git a/thirdparty/yasio/cxx17/string_view.hpp b/thirdparty/yasio/stl/string_view.hpp similarity index 99% rename from thirdparty/yasio/cxx17/string_view.hpp rename to thirdparty/yasio/stl/string_view.hpp index 5aaef7ba1b..0f2648de60 100644 --- a/thirdparty/yasio/cxx17/string_view.hpp +++ b/thirdparty/yasio/stl/string_view.hpp @@ -1436,6 +1436,15 @@ inline std::basic_string<_CharT, _Traits, Allocator>& assign(std::basic_string<_ lhs.clear(); return lhs; } +template +inline std::basic_string<_CharT, _Traits, Allocator>& append(std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs) +{ + if (!rhs.empty()) + lhs.append(rhs.data(), rhs.size()); + else + lhs.clear(); + return lhs; +} template > inline std::basic_string<_CharT, _Traits, Allocator> svtos(const basic_string_view<_CharT, _Traits>& value) { diff --git a/thirdparty/yasio/xxsocket.cpp b/thirdparty/yasio/xxsocket.cpp index 8b19aa13b3..dfbe8ac8d6 100644 --- a/thirdparty/yasio/xxsocket.cpp +++ b/thirdparty/yasio/xxsocket.cpp @@ -303,6 +303,7 @@ void xxsocket::traverse_local_address(std::function h endpoint ep; int iret = getaddrinfo(hostname, nullptr, &hint, &ailist); + (void)iret; if (ailist != nullptr) { for (auto aip = ailist; aip != nullptr; aip = aip->ai_next) @@ -362,17 +363,17 @@ void xxsocket::traverse_local_address(std::function h xxsocket::xxsocket(void) : fd(invalid_socket) {} xxsocket::xxsocket(socket_native_type h) : fd(h) {} -xxsocket::xxsocket(xxsocket&& right) : fd(invalid_socket) { swap(right); } +xxsocket::xxsocket(xxsocket&& right) YASIO__NOEXCEPT : fd(invalid_socket) { swap(right); } xxsocket::xxsocket(int af, int type, int protocol) : fd(invalid_socket) { open(af, type, protocol); } xxsocket::~xxsocket(void) { close(); } -xxsocket& xxsocket::operator=(socket_native_type handle) +xxsocket& xxsocket::operator=(socket_native_type handle) YASIO__NOEXCEPT { if (!this->is_open()) this->fd = handle; return *this; } -xxsocket& xxsocket::operator=(xxsocket&& right) { return swap(right); } +xxsocket& xxsocket::operator=(xxsocket&& right) YASIO__NOEXCEPT { return swap(right); } xxsocket& xxsocket::swap(xxsocket& rhs) { @@ -550,7 +551,7 @@ int xxsocket::connect_n(socket_native_type s, const endpoint& ep, const std::chr if (ret == 0) goto done; /* connect completed immediately */ - if ((ret = xxsocket::select(s, &rset, &wset, nullptr, wtimeout)) <= 0) + if (xxsocket::select(s, &rset, &wset, nullptr, wtimeout) <= 0) error = xxsocket::get_last_errno(); else if ((FD_ISSET(s, &rset) || FD_ISSET(s, &wset))) { /* Everythings are ok */ @@ -930,17 +931,27 @@ const char* xxsocket::strerror(int error) { #if defined(_WIN32) static char error_msg[256]; - ZeroMemory(error_msg, sizeof(error_msg)); - ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK /* remove line-end charactors \r\n */, NULL, - error, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), // english language - error_msg, sizeof(error_msg), nullptr); - - return error_msg; + return xxsocket::strerror_r(error, error_msg, sizeof(error_msg)); #else return ::strerror(error); #endif } +const char* xxsocket::strerror_r(int error, char* buf, size_t buflen) +{ +#if defined(_WIN32) + ZeroMemory(buf, buflen); + ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK /* remove line-end charactors \r\n */, NULL, + error, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), // english language + buf, static_cast(buflen), nullptr); + + return buf; +#else + (void)::strerror_r(error, buf, buflen); + return buf; +#endif +} + const char* xxsocket::gai_strerror(int error) { #if defined(_WIN32) @@ -960,7 +971,7 @@ struct ws2_32_gc { ws2_32_gc(void) { WSADATA dat = {0}; - WSAStartup(0x0202, &dat); + (void)WSAStartup(0x0202, &dat); } ~ws2_32_gc(void) { WSACleanup(); } }; diff --git a/thirdparty/yasio/xxsocket.hpp b/thirdparty/yasio/xxsocket.hpp index 7d4038011d..c607c8bbca 100644 --- a/thirdparty/yasio/xxsocket.hpp +++ b/thirdparty/yasio/xxsocket.hpp @@ -635,13 +635,13 @@ public: // Disable copy constructor YASIO__DECL xxsocket(const xxsocket&) = delete; // Construct with a exist socket, it will replace the source - YASIO__DECL xxsocket(xxsocket&&); + YASIO__DECL xxsocket(xxsocket&&) YASIO__NOEXCEPT; - YASIO__DECL xxsocket& operator=(socket_native_type handle); + YASIO__DECL xxsocket& operator=(socket_native_type handle) YASIO__NOEXCEPT; // Disable copy assign operator YASIO__DECL xxsocket& operator=(const xxsocket&) = delete; // Construct with a exist socket, it will replace the source - YASIO__DECL xxsocket& operator=(xxsocket&&); + YASIO__DECL xxsocket& operator=(xxsocket&&) YASIO__NOEXCEPT; // See also as function: open YASIO__DECL xxsocket(int af, int type, int protocol); @@ -1022,6 +1022,7 @@ public: YASIO__DECL static bool not_recv_error(int error); YASIO__DECL static const char* strerror(int error); + YASIO__DECL static const char* strerror_r(int error, char* buf, size_t buflen); YASIO__DECL static const char* gai_strerror(int error); /// diff --git a/thirdparty/yasio/yasio.cpp b/thirdparty/yasio/yasio.cpp index e75f246203..501bb57399 100644 --- a/thirdparty/yasio/yasio.cpp +++ b/thirdparty/yasio/yasio.cpp @@ -117,15 +117,15 @@ enum YCPF_NEEDS_QUERY = 1 << 21, /// below is byte2 of private flags (25~32) are mutable, and will be cleared automatically when connect flow done, see clear_mutable_flags. - - /* whether ssl client in handshaking */ - YCPF_SSL_HANDSHAKING = 1 << 25, }; namespace { // the minimal wait duration for select -static highp_time_t yasio__min_wait_duration = 0LL; +static highp_time_t yasio__min_wait_usec = 0LL; +// By default we will wait no longer than 5 minutes. This will ensure that +// any changes to the system clock are detected after no longer than this. +static const highp_time_t yasio__max_wait_usec = 5 * 60 * 1000 * 1000LL; // the max transport alloc size static const size_t yasio__max_tsize = (std::max)({sizeof(io_transport_tcp), sizeof(io_transport_udp), sizeof(io_transport_ssl), sizeof(io_transport_kcp)}); } // namespace @@ -139,11 +139,14 @@ struct yasio__global_state { { auto __get_cprint = [&]() -> const print_fn2_t& { return custom_print; }; // for single core CPU, we set minimal wait duration to 10us by default - yasio__min_wait_duration = std::thread::hardware_concurrency() > 1 ? 0LL : YASIO_MIN_WAIT_DURATION; + yasio__min_wait_usec = std::thread::hardware_concurrency() > 1 ? 0LL : 10LL; #if defined(YASIO_SSL_BACKEND) && YASIO_SSL_BACKEND == 1 # if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) if (OPENSSL_init_ssl(0, nullptr) == 1) + { yasio__setbits(this->init_flags_, INITF_SSL); + ERR_clear_error(); + } # endif #endif #if defined(YASIO_HAVE_CARES) @@ -190,27 +193,13 @@ void highp_timer::cancel(io_service& service) } /// io_send_op -int io_send_op::perform(io_transport* transport, const void* buf, int n) { return transport->write_cb_(buf, n, nullptr); } +int io_send_op::perform(io_transport* transport, const void* buf, int n, int& error) { return transport->write_cb_(buf, n, nullptr, error); } /// io_sendto_op -int io_sendto_op::perform(io_transport* transport, const void* buf, int n) { return transport->write_cb_(buf, n, std::addressof(destination_)); } - -#if defined(YASIO_SSL_BACKEND) -void ssl_auto_handle::destroy() +int io_sendto_op::perform(io_transport* transport, const void* buf, int n, int& error) { - if (ssl_) - { -# if YASIO_SSL_BACKEND == 1 - ::SSL_shutdown(ssl_); - ::SSL_free(ssl_); -# elif YASIO_SSL_BACKEND == 2 - ::mbedtls_ssl_free(ssl_); - delete ssl_; -# endif - ssl_ = nullptr; - } + return transport->write_cb_(buf, n, std::addressof(destination_), error); } -#endif /// io_channel io_channel::io_channel(io_service& service, int index) : io_base(), service_(service) @@ -220,6 +209,15 @@ io_channel::io_channel(io_service& service, int index) : io_base(), service_(ser index_ = index; decode_len_ = [=](void* ptr, int len) { return this->__builtin_decode_len(ptr, len); }; } +#if defined(YASIO_SSL_BACKEND) +SSL_CTX* io_channel::get_ssl_context(bool client) const +{ + if (client) + return service_.ssl_roles_[YSSL_CLIENT]; + auto& ctx = service_.ssl_roles_[YSSL_SERVER]; + return (ctx) ? ctx : service_.init_ssl_context(YSSL_SERVER); +} +#endif const print_fn2_t& io_channel::__get_cprint() const { return get_service().options_.print_; } std::string io_channel::format_destination() const { @@ -251,7 +249,6 @@ void io_channel::join_multicast_group() case AF_INET6: socket_->set_optval(IPPROTO_IPV6, IP_MULTICAST_IF, multiif_.in6_.sin6_scope_id); break; - default:; } int loopback = yasio__testbits(properties_, YCPF_MCAST_LOOPBACK) ? 1 : 0; @@ -340,14 +337,14 @@ io_transport::io_transport(io_channel* ctx, xxsocket_ptr&& s) : ctx_(ctx) #endif } const print_fn2_t& io_transport::__get_cprint() const { return ctx_->get_service().options_.print_; } -int io_transport::write(dynamic_buffer_t&& buffer, completion_cb_t&& handler) +int io_transport::write(sbyte_buffer&& buffer, completion_cb_t&& handler) { int n = static_cast(buffer.size()); send_queue_.emplace(cxx14::make_unique(std::move(buffer), std::move(handler))); get_service().interrupt(); return n; } -int io_transport::do_read(int revent, int& error, highp_time_t&) { return revent ? this->call_read(buffer_ + offset_, sizeof(buffer_) - offset_, error) : 0; } +int io_transport::do_read(int revent, int& error, highp_time_t&) { return this->call_read(buffer_ + offset_, sizeof(buffer_) - offset_, revent, error); } bool io_transport::do_write(highp_time_t& wait_duration) { bool ret = false; @@ -381,7 +378,7 @@ bool io_transport::do_write(highp_time_t& wait_duration) } } else - wait_duration = yasio__min_wait_duration; + wait_duration = yasio__min_wait_usec; } if (no_wevent && pollout_registerred_) { @@ -393,9 +390,9 @@ bool io_transport::do_write(highp_time_t& wait_duration) return ret; } -int io_transport::call_read(void* data, int size, int& error) +int io_transport::call_read(void* data, int size, int revent, int& error) { - int n = read_cb_(data, size); + int n = read_cb_(data, size, revent, error); if (n > 0) { ctx_->bytes_transferred_ += n; @@ -403,7 +400,6 @@ int io_transport::call_read(void* data, int size, int& error) } if (n < 0) { - error = xxsocket::get_last_errno(); if (xxsocket::not_recv_error(error)) return (error = 0); // status ok, clear error return n; @@ -417,7 +413,7 @@ int io_transport::call_read(void* data, int size, int& error) } int io_transport::call_write(io_send_op* op, int& error) { - int n = op->perform(this, op->buffer_.data() + op->offset_, static_cast(op->buffer_.size() - op->offset_)); + int n = op->perform(this, op->buffer_.data() + op->offset_, static_cast(op->buffer_.size() - op->offset_), error); if (n > 0) { // #performance: change offset only, remain data will be send at next frame. @@ -427,7 +423,6 @@ int io_transport::call_write(io_send_op* op, int& error) } else if (n < 0) { - error = xxsocket::get_last_errno(); if (xxsocket::not_send_error(error)) n = 0; else if (yasio__testbits(ctx_->properties_, YCM_UDP)) @@ -448,97 +443,81 @@ void io_transport::complete_op(io_send_op* op, int error) } void io_transport::set_primitives() { - this->write_cb_ = [=](const void* data, int len, const ip::endpoint*) { return socket_->send(data, len); }; - this->read_cb_ = [=](void* data, int len) { return socket_->recv(data, len, 0); }; + this->write_cb_ = [=](const void* data, int len, const ip::endpoint*, int& error) { + int n = socket_->send(data, len); + if (n < 0) + error = xxsocket::get_last_errno(); + return n; + }; + this->read_cb_ = [=](void* data, int len, int revent, int& error) { + if (revent) + { + int n = socket_->recv(data, len); + if (n < 0) + error = xxsocket::get_last_errno(); + return n; + } + + error = EWOULDBLOCK; + return -1; + }; } // -------------------- io_transport_tcp --------------------- inline io_transport_tcp::io_transport_tcp(io_channel* ctx, xxsocket_ptr&& s) : io_transport(ctx, std::forward(s)) {} // ----------------------- io_transport_ssl ---------------- #if defined(YASIO_SSL_BACKEND) -io_transport_ssl::io_transport_ssl(io_channel* ctx, xxsocket_ptr&& s) : io_transport_tcp(ctx, std::forward(s)), ssl_(std::move(ctx->ssl_)) {} +io_transport_ssl::io_transport_ssl(io_channel* ctx, xxsocket_ptr&& sock) : io_transport_tcp(ctx, std::forward(sock)) +{ + this->state_ = io_base::state::CONNECTING; // for ssl, inital state shoud be connecing for ssl handshake + bool client = yasio__testbits(ctx->properties_, YCM_CLIENT); + this->ssl_ = yssl_new(ctx->get_ssl_context(client), static_cast(this->socket_->native_handle()), ctx->remote_host_.c_str(), client); +} +int io_transport_ssl::do_ssl_handshake(int& error) +{ + int ret = yssl_do_handshake(ssl_, error); + if (ret == 0) // handshake succeed + { // because we invoke handshake in call_read, so we emit EWOULDBLOCK to mark ssl transport status `ok` + this->state_ = io_base::state::OPENED; + this->read_cb_ = [=](void* data, int len, int revent, int& error) { + if (revent) + return yssl_read(ssl_, data, len, error); + error = EWOULDBLOCK; + return -1; + }; + this->write_cb_ = [=](const void* data, int len, const ip::endpoint*, int& error) { return yssl_write(ssl_, data, len, error); }; + + YASIO_KLOGD("[index: %d] the connection #%u <%s> --> <%s> is established.", ctx_->index_, this->id_, this->local_endpoint().to_string().c_str(), + this->remote_endpoint().to_string().c_str()); + get_service().fire_event(ctx_->index_, YEK_ON_OPEN, 0, this); + + error = EWOULDBLOCK; + } + else + { + if (error == EWOULDBLOCK) + get_service().interrupt(); + else + { // handshake failed, print reason + char buf[256] = {0}; + YASIO_KLOGE("[index: %d] do_ssl_handshake fail with %s", ctx_->index_, yssl_strerror(ssl_, ret, buf, sizeof(buf))); + if (yasio__testbits(ctx_->properties_, YCM_CLIENT)) + { + YASIO_KLOGE("[index: %d] connect server %s failed, ec=%d, detail:%s", ctx_->index_, ctx_->format_destination().c_str(), error, + io_service::strerror(error)); + get_service().fire_event(ctx_->index(), YEK_ON_OPEN, error, ctx_); + } + } + } + return -1; +} +void io_transport_ssl::do_ssl_shutdown() +{ + if (ssl_) + yssl_shutdown(ssl_); +} void io_transport_ssl::set_primitives() { - this->read_cb_ = [=](void* data, int len) { -# if YASIO_SSL_BACKEND == 1 - ERR_clear_error(); - int n = ::SSL_read(ssl_, data, len); - if (n > 0) - return n; - int error = SSL_get_error(ssl_, n); - switch (error) - { - case SSL_ERROR_ZERO_RETURN: // n=0, the upper caller will regards as eof - break; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* The operation did not complete; the same TLS/SSL I/O function - should be called again later. This is basically an EWOULDBLOCK - equivalent. */ - if (xxsocket::get_last_errno() != EWOULDBLOCK) - xxsocket::set_last_errno(EWOULDBLOCK); - break; - default: - xxsocket::set_last_errno(yasio::errc::ssl_read_failed); - } - return n; -# elif YASIO_SSL_BACKEND == 2 - auto ssl = static_cast(ssl_); - int n = ::mbedtls_ssl_read(ssl, static_cast(data), len); - if (n > 0) - return n; - switch (n) - { - case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: // n=0, the upper caller will regards as eof - n = 0; - case 0: - ::mbedtls_ssl_close_notify(ssl); - break; - case MBEDTLS_ERR_SSL_WANT_READ: - case MBEDTLS_ERR_SSL_WANT_WRITE: - if (xxsocket::get_last_errno() != EWOULDBLOCK) - xxsocket::set_last_errno(EWOULDBLOCK); - break; - default: - xxsocket::set_last_errno(yasio::errc::ssl_read_failed); - } - return n; -# endif - }; - this->write_cb_ = [=](const void* data, int len, const ip::endpoint*) { -# if YASIO_SSL_BACKEND == 1 - ERR_clear_error(); - int n = ::SSL_write(ssl_, data, len); - if (n > 0) - return n; - - int error = SSL_get_error(ssl_, n); - switch (error) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - if (xxsocket::get_last_errno() != EWOULDBLOCK) - xxsocket::set_last_errno(EWOULDBLOCK); - break; - default: - xxsocket::set_last_errno(yasio::errc::ssl_write_failed); - } -# elif YASIO_SSL_BACKEND == 2 - int n = ::mbedtls_ssl_write(static_cast(ssl_), static_cast(data), len); - if (n > 0) - return n; - switch (n) - { - case MBEDTLS_ERR_SSL_WANT_READ: - case MBEDTLS_ERR_SSL_WANT_WRITE: - if (xxsocket::get_last_errno() != EWOULDBLOCK) - xxsocket::set_last_errno(EWOULDBLOCK); - break; - default: - xxsocket::set_last_errno(yasio::errc::ssl_write_failed); - } -# endif - return -1; - }; + this->read_cb_ = [=](void* /*data*/, int /*len*/, int /*revent*/, int& error) { return do_ssl_handshake(error); }; } #endif // ----------------------- io_transport_udp ---------------- @@ -593,11 +572,11 @@ void io_transport_udp::disconnect() connected_ = false; set_primitives(); } -int io_transport_udp::write(dynamic_buffer_t&& buffer, completion_cb_t&& handler) +int io_transport_udp::write(sbyte_buffer&& buffer, completion_cb_t&& handler) { return connected_ ? io_transport::write(std::move(buffer), std::move(handler)) : write_to(std::move(buffer), ensure_destination(), std::move(handler)); } -int io_transport_udp::write_to(dynamic_buffer_t&& buffer, const ip::endpoint& to, completion_cb_t&& handler) +int io_transport_udp::write_to(sbyte_buffer&& buffer, const ip::endpoint& to, completion_cb_t&& handler) { int n = static_cast(buffer.size()); send_queue_.emplace(cxx14::make_unique(std::move(buffer), std::move(handler), to)); @@ -610,23 +589,30 @@ void io_transport_udp::set_primitives() io_transport::set_primitives(); else { - this->write_cb_ = [=](const void* data, int len, const ip::endpoint* destination) { + this->write_cb_ = [=](const void* data, int len, const ip::endpoint* destination, int& error) { assert(destination); int n = socket_->sendto(data, len, *destination); if (n < 0) { - auto error = xxsocket::get_last_errno(); + error = xxsocket::get_last_errno(); if (!xxsocket::not_send_error(error)) YASIO_KLOGW("[index: %d] write udp socket failed, ec=%d, detail:%s", this->cindex(), error, io_service::strerror(error)); } return n; }; - this->read_cb_ = [=](void* data, int len) { - ip::endpoint peer; - int n = socket_->recvfrom(data, len, peer); - if (n > 0) - this->peer_ = peer; - return n; + this->read_cb_ = [=](void* data, int len, int revent, int& error) { + if (revent) + { + ip::endpoint peer; + int n = socket_->recvfrom(data, len, peer); + if (n > 0) + this->peer_ = peer; + if (n < 0) + error = xxsocket::get_last_errno(); + return n; + } + error = EWOULDBLOCK; + return -1; }; } } @@ -645,15 +631,18 @@ io_transport_kcp::io_transport_kcp(io_channel* ctx, xxsocket_ptr&& s) : io_trans ::ikcp_nodelay(this->kcp_, 1, 5000 /*kcp max interval is 5000(ms)*/, 2, 1); ::ikcp_setoutput(this->kcp_, [](const char* buf, int len, ::ikcpcb* /*kcp*/, void* user) { auto t = (io_transport_kcp*)user; - if (yasio__min_wait_duration == 0) - return t->write_cb_(buf, len, std::addressof(t->ensure_destination())); + if (yasio__min_wait_usec == 0) + { + int ignored_ec = 0; + return t->write_cb_(buf, len, std::addressof(t->ensure_destination()), ignored_ec); + } // Enqueue to transport queue - return t->io_transport_udp::write(dynamic_buffer_t{buf, buf + len}, nullptr); + return t->io_transport_udp::write(sbyte_buffer{buf, buf + len}, nullptr); }); } io_transport_kcp::~io_transport_kcp() { ::ikcp_release(this->kcp_); } -int io_transport_kcp::write(dynamic_buffer_t&& buffer, completion_cb_t&& /*handler*/) +int io_transport_kcp::write(sbyte_buffer&& buffer, completion_cb_t&& /*handler*/) { std::lock_guard lck(send_mtx_); int len = static_cast(buffer.size()); @@ -663,14 +652,14 @@ int io_transport_kcp::write(dynamic_buffer_t&& buffer, completion_cb_t&& /*handl } int io_transport_kcp::do_read(int revent, int& error, highp_time_t& wait_duration) { - int n = revent ? this->call_read(&rawbuf_.front(), static_cast(rawbuf_.size()), error) : 0; + int n = this->call_read(&rawbuf_.front(), static_cast(rawbuf_.size()), revent, error); if (n > 0) this->handle_input(rawbuf_.data(), n, error, wait_duration); if (!error) { // !important, should always try to call ikcp_recv when no error occured. n = ::ikcp_recv(kcp_, buffer_ + offset_, sizeof(buffer_) - offset_); if (n > 0) // If got data from kcp, don't wait - wait_duration = yasio__min_wait_duration; + wait_duration = yasio__min_wait_usec; else if (n < 0) n = 0; // EAGAIN/EWOULDBLOCK } @@ -696,7 +685,7 @@ bool io_transport_kcp::do_write(highp_time_t& wait_duration) ::ikcp_update(kcp_, static_cast(::yasio::clock())); ::ikcp_flush(kcp_); this->check_timeout(wait_duration); // call ikcp_check - if (yasio__min_wait_duration == 0) + if (yasio__min_wait_usec == 0) return true; // Call super do_write to perform low layer socket.send // benefit of transport queue: @@ -710,7 +699,7 @@ void io_transport_kcp::check_timeout(highp_time_t& wait_duration) const auto expire_time = ::ikcp_check(kcp_, current); highp_time_t duration = static_cast(expire_time - current) * std::milli::den; if (duration < 0) - duration = yasio__min_wait_duration; + duration = yasio__min_wait_usec; if (wait_duration > duration) wait_duration = duration; } @@ -806,6 +795,12 @@ void io_service::handle_stop() } void io_service::initialize(const io_hostent* channel_eps, int channel_count) { +#if defined(YASIO_SSL_BACKEND) + ssl_roles_[YSSL_CLIENT] = ssl_roles_[YSSL_SERVER] = nullptr; +#endif + + this->wait_duration_ = yasio__max_wait_usec; + // at least one channel if (channel_count < 1) channel_count = 1; @@ -884,12 +879,11 @@ void io_service::run() yasio::set_thread_name("yasio"); #if defined(YASIO_SSL_BACKEND) - init_ssl_context(); + init_ssl_context(YSSL_CLIENT); // by default, init ssl client context #endif #if defined(YASIO_HAVE_CARES) recreate_ares_channel(); ares_socket_t ares_socks[ARES_GETSOCK_MAXNUM] = {0}; - int ares_socks_count = 0; #endif // Call once at startup @@ -897,24 +891,30 @@ void io_service::run() // The core event loop fd_set_adapter fd_set; // The temp file descriptor set - this->wait_duration_ = YASIO_MAX_WAIT_DURATION; + do { auto wait_duration = get_timeout(this->wait_duration_); // Gets current wait duration - this->wait_duration_ = YASIO_MAX_WAIT_DURATION; // Reset next wait duration - if (wait_duration > 0) - { - fd_set = this->fd_set_; - timeval waitd_tv = {(decltype(timeval::tv_sec))(wait_duration / 1000000), (decltype(timeval::tv_usec))(wait_duration % 1000000)}; + this->wait_duration_ = yasio__max_wait_usec; // Reset next wait duration + + fd_set = this->fd_set_; + timeval waitd_tv = {(decltype(timeval::tv_sec))(wait_duration / 1000000), (decltype(timeval::tv_usec))(wait_duration % 1000000)}; #if defined(YASIO_HAVE_CARES) - if (ares_outstanding_work_) - { - ares_socks_count = register_ares_fds(ares_socks, fd_set); - ::ares_timeout(this->ares_, &waitd_tv, &waitd_tv); - } + /** + * retrieves the set of file descriptors which the calling application should poll io, + * after poll_io, for ares invoke flow, refer to: + * https://c-ares.org/ares_fds.html + * https://c-ares.org/ares_timeout.html + * https://c-ares.org/ares_process_fd.html + */ + auto ares_nfds = do_ares_fds(ares_socks, fd_set, waitd_tv); #endif - YASIO_KLOGV("[core] poll_io max_nfds=%d, waiting... %ld milliseconds", fd_set.max_descriptor(), waitd_tv.tv_sec * 1000 + waitd_tv.tv_usec / 1000); - int retval = fd_set.poll_io(waitd_tv); + + const int waitd_ms = static_cast(waitd_tv.tv_sec * 1000 + waitd_tv.tv_usec / 1000); + if (waitd_ms > 0) + { + YASIO_KLOGV("[core] poll_io max_nfds=%d, waiting... %ld milliseconds", fd_set.max_descriptor(), waitd_ms); + int retval = fd_set.poll_io(waitd_ms); YASIO_KLOGV("[core] poll_io waked up, retval=%d", retval); if (retval < 0) { @@ -936,8 +936,8 @@ void io_service::run() } #if defined(YASIO_HAVE_CARES) - // process possible async resolve requests. - process_ares_requests(ares_socks, ares_socks_count, fd_set); + // process events for name resolution. + do_ares_process_fds(ares_socks, ares_nfds, fd_set); #endif // process active transports @@ -954,7 +954,8 @@ void io_service::run() destroy_ares_channel(); #endif #if defined(YASIO_SSL_BACKEND) - cleanup_ssl_context(); + cleanup_ssl_context(YSSL_CLIENT); + cleanup_ssl_context(YSSL_SERVER); #endif this->state_ = io_service::state::AT_EXITING; @@ -974,7 +975,8 @@ void io_service::process_transports(fd_set_adapter& fd_set) ++iter; continue; } - shutdown_internal(transport); + if (transport->error_ == 0) + transport->error_ = yasio::errc::shutdown_by_localhost; } handle_close(transport); @@ -1081,15 +1083,25 @@ bool io_service::open(size_t index, int kind) io_channel* io_service::channel_at(size_t index) const { return (index < channels_.size()) ? channels_[index] : nullptr; } void io_service::handle_close(transport_handle_t thandle) { - auto ctx = thandle->ctx_; - auto ec = thandle->error_; - // @Because we can't retrive peer endpoint when connect reset by peer, so use id to trace. - YASIO_KLOGD("[index: %d] the connection #%u is lost, ec=%d, where=%d, detail:%s", ctx->index_, thandle->id_, ec, (int)thandle->error_stage_, - io_service::strerror(ec)); - this->fire_event(thandle->cindex(), YEK_ON_CLOSE, ec, thandle); + auto ctx = thandle->ctx_; + auto error = thandle->error_; + const bool client = yasio__testbits(ctx->properties_, YCM_CLIENT); + + if (thandle->state_ == io_base::state::OPENED) + { // @Because we can't retrive peer endpoint when connect reset by peer, so use id to trace. + YASIO_KLOGD("[index: %d] the connection #%u is lost, ec=%d, where=%d, detail:%s", ctx->index_, thandle->id_, error, (int)thandle->error_stage_, + io_service::strerror(error)); + this->fire_event(ctx->index(), YEK_ON_CLOSE, error, thandle); + } +#if defined(YASIO_SSL_BACKEND) + if (yasio__testbits(ctx->properties_, YCM_SSL)) + static_cast(thandle)->do_ssl_shutdown(); +#endif + if (yasio__testbits(ctx->properties_, YCM_TCP) && error == yasio::errc::shutdown_by_localhost) + thandle->socket_->shutdown(); cleanup_io(thandle); deallocate_transport(thandle); - if (yasio__testbits(ctx->properties_, YCM_CLIENT)) + if (client) { yasio__clearbits(ctx->opmask_, YOPM_CLOSE); cleanup_channel(ctx, false); @@ -1098,7 +1110,7 @@ void io_service::handle_close(transport_handle_t thandle) void io_service::register_descriptor(const socket_native_type fd, int events) { this->fd_set_.set(fd, events); } void io_service::deregister_descriptor(const socket_native_type fd, int events) { this->fd_set_.unset(fd, events); } -int io_service::write(transport_handle_t transport, dynamic_buffer_t buffer, completion_cb_t handler) +int io_service::write(transport_handle_t transport, sbyte_buffer buffer, completion_cb_t handler) { if (transport && transport->is_open()) return !buffer.empty() ? transport->write(std::move(buffer), std::move(handler)) : 0; @@ -1108,7 +1120,7 @@ int io_service::write(transport_handle_t transport, dynamic_buffer_t buffer, com return -1; } } -int io_service::write_to(transport_handle_t transport, dynamic_buffer_t buffer, const ip::endpoint& to, completion_cb_t handler) +int io_service::write_to(transport_handle_t transport, sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t handler) { if (transport && transport->is_open()) return !buffer.empty() ? transport->write_to(std::move(buffer), to, std::move(handler)) : 0; @@ -1189,10 +1201,9 @@ void io_service::do_connect_completion(io_channel* ctx, fd_set_adapter& fd_set) assert(ctx->state_ == io_base::state::CONNECTING); if (ctx->state_ == io_base::state::CONNECTING) { - int error = -1; -#if !defined(YASIO_SSL_BACKEND) if (fd_set.is_set(ctx->socket_->native_handle(), socket_event::readwrite)) { + int error = -1; if (ctx->socket_->get_optval(SOL_SOCKET, SO_ERROR, error) >= 0 && error == 0) { // The nonblocking tcp handshake complete, remove write event avoid high-CPU occupation @@ -1203,188 +1214,21 @@ void io_service::do_connect_completion(io_channel* ctx, fd_set_adapter& fd_set) handle_connect_failed(ctx, error); ctx->timer_.cancel(*this); } -#else - if (!yasio__testbits(ctx->properties_, YCPF_SSL_HANDSHAKING)) - { - if (fd_set.is_set(ctx->socket_->native_handle(), socket_event::readwrite)) - { - if (ctx->socket_->get_optval(SOL_SOCKET, SO_ERROR, error) >= 0 && error == 0) - { - // The nonblocking tcp handshake complete, remove write event avoid high-CPU occupation - deregister_descriptor(ctx->socket_->native_handle(), socket_event::write); - if (!yasio__testbits(ctx->properties_, YCM_SSL)) - handle_connect_succeed(ctx, ctx->socket_); - else - do_ssl_handshake(ctx); - } - else - handle_connect_failed(ctx, error); - } - } - else - do_ssl_handshake(ctx); - if (ctx->state_ != io_base::state::CONNECTING) - ctx->timer_.cancel(*this); -#endif } } #if defined(YASIO_SSL_BACKEND) -void io_service::init_ssl_context() +SSL_CTX* io_service::init_ssl_context(ssl_role role) { -# if YASIO_SSL_BACKEND == 1 -# if (OPENSSL_VERSION_NUMBER >= 0x10100000L) - auto req_method = ::TLS_client_method(); -# else - auto req_method = ::SSLv23_client_method(); -# endif - ssl_ctx_ = ::SSL_CTX_new(req_method); - -# if defined(SSL_MODE_RELEASE_BUFFERS) - ::SSL_CTX_set_mode(ssl_ctx_, SSL_MODE_RELEASE_BUFFERS); -# endif - - ::SSL_CTX_set_mode(ssl_ctx_, SSL_MODE_ENABLE_PARTIAL_WRITE); - if (!this->options_.cafile_.empty()) - { - if (::SSL_CTX_load_verify_locations(ssl_ctx_, this->options_.cafile_.c_str(), nullptr) == 1) - { - ::SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_PEER, ::SSL_CTX_get_verify_callback(ssl_ctx_)); -# if OPENSSL_VERSION_NUMBER >= 0x10101000L - ::SSL_CTX_set_post_handshake_auth(ssl_ctx_, 1); -# endif -# if defined(X509_V_FLAG_PARTIAL_CHAIN) - /* Have intermediate certificates in the trust store be treated as - trust-anchors, in the same way as self-signed root CA certificates - are. This allows users to verify servers using the intermediate cert - only, instead of needing the whole chain. */ - X509_STORE_set_flags(SSL_CTX_get_cert_store(ssl_ctx_), X509_V_FLAG_PARTIAL_CHAIN); -# endif - } - else - YASIO_KLOGE("[global] load ca certifaction file failed!"); - } - else - SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_NONE, nullptr); -# elif YASIO_SSL_BACKEND == 2 - ssl_ctx_ = new SSL_CTX(); - ::mbedtls_ssl_config_init(&ssl_ctx_->conf); - ::mbedtls_x509_crt_init(&ssl_ctx_->cacert); - ::mbedtls_ctr_drbg_init(&ssl_ctx_->ctr_drbg); - ::mbedtls_entropy_init(&ssl_ctx_->entropy); - cxx17::string_view pers{YASIO_SSL_PIN, YASIO_SSL_PIN_LEN}; - int ret = ::mbedtls_ctr_drbg_seed(&ssl_ctx_->ctr_drbg, ::mbedtls_entropy_func, &ssl_ctx_->entropy, (const unsigned char*)pers.data(), pers.length()); - if (ret != 0) - YASIO_KLOGE("mbedtls_ctr_drbg_seed fail with ret=%d", ret); - - int authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; - if (!this->options_.cafile_.empty()) // the cafile_ must be full path - { - if ((ret = ::mbedtls_x509_crt_parse_file(&ssl_ctx_->cacert, this->options_.cafile_.c_str())) == 0) - authmode = MBEDTLS_SSL_VERIFY_REQUIRED; - else - YASIO_KLOGE("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret); - } - - if ((ret = ::mbedtls_ssl_config_defaults(&ssl_ctx_->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) - YASIO_KLOGE("mbedtls_ssl_config_defaults fail with ret=%d", ret); - - ::mbedtls_ssl_conf_authmode(&ssl_ctx_->conf, authmode); - ::mbedtls_ssl_conf_ca_chain(&ssl_ctx_->conf, &ssl_ctx_->cacert, nullptr); - ::mbedtls_ssl_conf_rng(&ssl_ctx_->conf, ::mbedtls_ctr_drbg_random, &ssl_ctx_->ctr_drbg); -# endif + auto ctx = role == YSSL_CLIENT ? yssl_ctx_new(yssl_options{options_.cafile_.c_str(), nullptr, true}) + : yssl_ctx_new(yssl_options{options_.crtfile_.c_str(), options_.keyfile_.c_str(), false}); + ssl_roles_[role] = ctx; + return ctx; } -void io_service::cleanup_ssl_context() +void io_service::cleanup_ssl_context(ssl_role role) { - if (ssl_ctx_) - { -# if YASIO_SSL_BACKEND == 1 - SSL_CTX_free((SSL_CTX*)ssl_ctx_); -# elif YASIO_SSL_BACKEND == 2 - ::mbedtls_x509_crt_free(&ssl_ctx_->cacert); - ::mbedtls_ssl_config_free(&ssl_ctx_->conf); - ::mbedtls_ctr_drbg_free(&ssl_ctx_->ctr_drbg); - ::mbedtls_entropy_free(&ssl_ctx_->entropy); - delete ssl_ctx_; -# endif - ssl_ctx_ = nullptr; - } -} -void io_service::do_ssl_handshake(io_channel* ctx) -{ - if (!ctx->ssl_) - { -# if YASIO_SSL_BACKEND == 1 - auto ssl = ::SSL_new(ssl_ctx_); - ::SSL_set_fd(ssl, static_cast(ctx->socket_->native_handle())); - ::SSL_set_connect_state(ssl); - ::SSL_set_tlsext_host_name(ssl, ctx->remote_host_.c_str()); -# elif YASIO_SSL_BACKEND == 2 - auto ssl = ::mbedtls_ssl_new(ssl_ctx_); - ::mbedtls_ssl_set_fd(ssl, static_cast(ctx->socket_->native_handle())); - ::mbedtls_ssl_set_hostname(ssl, ctx->remote_host_.c_str()); -# endif - yasio__setbits(ctx->properties_, YCPF_SSL_HANDSHAKING); // start ssl handshake - ctx->ssl_.reset(ssl); - } - -# if YASIO_SSL_BACKEND == 1 - int ret = ::SSL_do_handshake(ctx->ssl_); - if (ret != 1) - { - int status = ::SSL_get_error(ctx->ssl_, ret); - /* - When using a non-blocking socket, nothing is to be done, but select() can be used to check for - the required condition: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html - */ - if (status == SSL_ERROR_WANT_READ || status == SSL_ERROR_WANT_WRITE) - return; -# if defined(SSL_ERROR_WANT_ASYNC) - if (status == SSL_ERROR_WANT_ASYNC) - return; -# endif - int error = static_cast(ERR_get_error()); - if (error) - { - char errstring[256] = {0}; - ERR_error_string_n(error, errstring, sizeof(errstring)); - YASIO_KLOGE("[index: %d] SSL_do_handshake fail with ret=%d,error=%X, detail:%s", ctx->index_, ret, error, errstring); - } - else - { - error = xxsocket::get_last_errno(); - YASIO_KLOGE("[index: %d] SSL_do_handshake fail with ret=%d,status=%d, error=%d, detail:%s", ctx->index_, ret, status, error, xxsocket::strerror(error)); - } - ctx->ssl_.destroy(); - handle_connect_failed(ctx, yasio::errc::ssl_handshake_failed); - } - else - handle_connect_succeed(ctx, ctx->socket_); -# elif YASIO_SSL_BACKEND == 2 - auto ssl = static_cast(ctx->ssl_); - int ret = ::mbedtls_ssl_handshake_step(ssl); - if (ret == 0) - { - if (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) - interrupt(); - else // mbedtls_ssl_get_verify_result return 0 when valid cacert provided - handle_connect_succeed(ctx, ctx->socket_); - } - else - { - char errstring[256] = {0}; - switch (ret) - { - case MBEDTLS_ERR_SSL_WANT_READ: - case MBEDTLS_ERR_SSL_WANT_WRITE: - break; // Nothing need to do - default: - ::mbedtls_strerror(ret, errstring, sizeof(errstring)); - YASIO_KLOGE("[index: %d] mbedtls_ssl_handshake_step fail with ret=%d, detail:%s", ctx->index_, ret, errstring); - ctx->ssl_.destroy(); - handle_connect_failed(ctx, yasio::errc::ssl_handshake_failed); - } - } -# endif + auto& ctx = ssl_roles_[role]; + if (ctx) + yssl_ctx_free(ctx); } #endif #if defined(YASIO_HAVE_CARES) @@ -1425,33 +1269,39 @@ void io_service::ares_getaddrinfo_cb(void* arg, int status, int /*timeouts*/, ar } current_service.interrupt(); } -int io_service::register_ares_fds(socket_native_type* ares_socks, fd_set_adapter& fd_set) +int io_service::do_ares_fds(socket_native_type* socks, fd_set_adapter& fd_set, timeval& waitd_tv) { - int count = 0; - int bitmask = ::ares_getsock(this->ares_, ares_socks, ARES_GETSOCK_MAXNUM); - for (int i = 0; i < ARES_GETSOCK_MAXNUM; ++i) + int nfds = 0; + if (ares_outstanding_work_) { - if (ARES_GETSOCK_READABLE(bitmask, i) || ARES_GETSOCK_WRITABLE(bitmask, i)) + int bitmask = ::ares_getsock(this->ares_, socks, ARES_GETSOCK_MAXNUM); + for (int i = 0; i < ARES_GETSOCK_MAXNUM; ++i) { - auto fd = ares_socks[i]; - ++count; - fd_set.set(fd, socket_event::readwrite); + int events = socket_event::null; + if (ARES_GETSOCK_READABLE(bitmask, i)) + events |= socket_event::read; + if (ARES_GETSOCK_WRITABLE(bitmask, i)) + events |= socket_event::write; + if (events) + { + ++nfds; + fd_set.set(socks[i], events); + } + else + break; } - else - break; + + if (nfds) + ::ares_timeout(this->ares_, &waitd_tv, &waitd_tv); } - return count; + return nfds; } -void io_service::process_ares_requests(socket_native_type* socks, int count, fd_set_adapter& fd_set) +void io_service::do_ares_process_fds(socket_native_type* socks, int nfds, fd_set_adapter& fd_set) { - if (this->ares_outstanding_work_ > 0) + for (auto i = 0; i < nfds; ++i) { - for (auto i = 0; i < count; ++i) - { - auto fd = socks[i]; - ::ares_process_fd(this->ares_, fd_set.is_set(fd, socket_event::read) ? fd : ARES_SOCKET_BAD, - fd_set.is_set(fd, socket_event::write) ? fd : ARES_SOCKET_BAD); - } + auto fd = socks[i]; + ::ares_process_fd(this->ares_, fd_set.is_set(fd, socket_event::read) ? fd : ARES_SOCKET_BAD, fd_set.is_set(fd, socket_event::write) ? fd : ARES_SOCKET_BAD); } } void io_service::recreate_ares_channel() @@ -1668,7 +1518,7 @@ transport_handle_t io_service::do_dgram_accept(io_channel* ctx, const ip::endpoi // We always establish 4 tuple with clients transport->confgure_remote(peer); if (user_route) - notify_connect_succeed(transport); + active_transport(transport); else handle_connect_succeed(transport); return transport; @@ -1705,18 +1555,23 @@ void io_service::handle_connect_succeed(transport_handle_t transport) connection->set_keepalive(options_.tcp_keepalive_.onoff, options_.tcp_keepalive_.idle, options_.tcp_keepalive_.interval, options_.tcp_keepalive_.probs); } - notify_connect_succeed(transport); + active_transport(transport); } -void io_service::notify_connect_succeed(transport_handle_t t) +void io_service::active_transport(transport_handle_t t) { auto ctx = t->ctx_; auto& s = t->socket_; this->transports_.push_back(t); - YASIO__UNUSED_PARAM(s); - YASIO_KLOGV("[index: %d] sndbuf=%d, rcvbuf=%d", ctx->index_, s->get_optval(SOL_SOCKET, SO_SNDBUF), s->get_optval(SOL_SOCKET, SO_RCVBUF)); - YASIO_KLOGD("[index: %d] the connection #%u <%s> --> <%s> is established.", ctx->index_, t->id_, t->local_endpoint().to_string().c_str(), - t->remote_endpoint().to_string().c_str()); - this->fire_event(ctx->index_, YEK_ON_OPEN, 0, t); + if (!yasio__testbits(ctx->properties_, YCM_SSL)) + { + YASIO__UNUSED_PARAM(s); + YASIO_KLOGV("[index: %d] sndbuf=%d, rcvbuf=%d", ctx->index_, s->get_optval(SOL_SOCKET, SO_SNDBUF), s->get_optval(SOL_SOCKET, SO_RCVBUF)); + YASIO_KLOGD("[index: %d] the connection #%u <%s> --> <%s> is established.", ctx->index_, t->id_, t->local_endpoint().to_string().c_str(), + t->remote_endpoint().to_string().c_str()); + this->fire_event(ctx->index_, YEK_ON_OPEN, 0, t); + } + else if (yasio__testbits(ctx->properties_, YCM_CLIENT)) + this->interrupt(); } transport_handle_t io_service::allocate_transport(io_channel* ctx, xxsocket_ptr&& s) { @@ -1836,7 +1691,7 @@ void io_service::unpack(transport_handle_t transport, int bytes_expected, int by if (offset > 0) { /* move remain data to head of buffer and hold 'offset'. */ ::memmove(transport->buffer_, transport->buffer_ + bytes_expected, offset); - this->wait_duration_ = yasio__min_wait_duration; + this->wait_duration_ = yasio__min_wait_usec; } // move properly pdu to ready queue, the other thread who care about will retrieve it. YASIO_KLOGV("[index: %d] received a properly packet from peer, packet size:%d", transport->cindex(), transport->expected_size_); @@ -1909,13 +1764,6 @@ bool io_service::open_internal(io_channel* ctx) this->interrupt(); return true; } -void io_service::shutdown_internal(transport_handle_t transport) -{ - if (transport->error_ == 0) - transport->error_ = yasio::errc::shutdown_by_localhost; - if (yasio__testbits(transport->ctx_->properties_, YCM_TCP)) - transport->socket_->shutdown(); -} bool io_service::close_internal(io_channel* ctx) { yasio__clearbits(ctx->opmask_, YOPM_OPEN); @@ -1973,9 +1821,6 @@ highp_time_t io_service::get_timeout(highp_time_t usec) } bool io_service::cleanup_channel(io_channel* ctx, bool clear_mask) { -#if YASIO_SSL_BACKEND != 0 - ctx->ssl_.destroy(); -#endif ctx->clear_mutable_flags(); bool bret = cleanup_io(ctx, clear_mask); #if defined(YAISO_ENABLE_PASSIVE_EVENT) @@ -2238,9 +2083,21 @@ void io_service::set_option_internal(int opt, va_list ap) // lgtm [cpp/poorly-do options_.dns_dirty_ = true; break; #endif + case YOPT_S_EVENT_CB: + options_.on_event_ = *va_arg(ap, event_cb_t*); + break; + case YOPT_S_DEFER_EVENT_CB: + options_.on_defer_event_ = *va_arg(ap, defer_event_cb_t*); + break; case YOPT_S_FORWARD_EVENT: options_.forward_event_ = !!va_arg(ap, int); break; +#if defined(YASIO_SSL_BACKEND) + case YOPT_S_SSL_CERT: + options_.crtfile_ = va_arg(ap, const char*); + options_.keyfile_ = va_arg(ap, const char*); + break; +#endif case YOPT_C_UNPACK_PARAMS: { auto channel = channel_at(static_cast(va_arg(ap, int))); if (channel) @@ -2264,12 +2121,6 @@ void io_service::set_option_internal(int opt, va_list ap) // lgtm [cpp/poorly-do channel->uparams_.no_bswap = va_arg(ap, int); break; } - case YOPT_S_EVENT_CB: - options_.on_event_ = *va_arg(ap, event_cb_t*); - break; - case YOPT_S_DEFER_EVENT_CB: - options_.on_defer_event_ = *va_arg(ap, defer_event_cb_t*); - break; case YOPT_C_LFBFD_FN: { auto channel = channel_at(static_cast(va_arg(ap, int))); if (channel) diff --git a/thirdparty/yasio/yasio.hpp b/thirdparty/yasio/yasio.hpp index 20f873dd78..2adbbc52db 100644 --- a/thirdparty/yasio/yasio.hpp +++ b/thirdparty/yasio/yasio.hpp @@ -49,12 +49,12 @@ SOFTWARE. #include "yasio/detail/errc.hpp" #include "yasio/detail/byte_buffer.hpp" #include "yasio/detail/fd_set_adapter.hpp" -#include "yasio/cxx17/memory.hpp" -#include "yasio/cxx17/string_view.hpp" +#include "yasio/stl/memory.hpp" +#include "yasio/stl/string_view.hpp" #include "yasio/xxsocket.hpp" #if !defined(YASIO_HAVE_CARES) -# include "yasio/cxx17/shared_mutex.hpp" +# include "yasio/stl/shared_mutex.hpp" #endif #if defined(YASIO_HAVE_KCP) @@ -180,6 +180,12 @@ enum // when forward event enabled, the option YOPT_S_DEFERRED_EVENT was ignored YOPT_S_FORWARD_EVENT, + // Set ssl server cert and private key file + // params: + // crtfile: const char* + // keyfile: const char* + YOPT_S_SSL_CERT, + // Sets channel length field based frame decode function, native C++ ONLY // params: index:int, func:decode_len_fn_t* YOPT_C_UNPACK_FN = 101, @@ -291,7 +297,8 @@ enum YCK_UDP_SERVER = YCM_UDP | YCM_SERVER, YCK_KCP_CLIENT = YCM_KCP | YCM_CLIENT | YCM_UDP, YCK_KCP_SERVER = YCM_KCP | YCM_SERVER | YCM_UDP, - YCK_SSL_CLIENT = YCM_SSL | YCM_CLIENT | YCM_TCP, + YCK_SSL_CLIENT = YCK_TCP_CLIENT | YCM_SSL, + YCK_SSL_SERVER = YCK_TCP_SERVER | YCM_SSL, }; // channel flags @@ -368,14 +375,17 @@ typedef highp_timer_ptr deadline_timer_ptr; typedef event_cb_t io_event_cb_t; typedef completion_cb_t io_completion_cb_t; -typedef sbyte_buffer dynamic_buffer_t; - -inline dynamic_buffer_t make_dynamic_buffer(const void* p, size_t n) { return dynamic_buffer_t{(const char*)p, (const char*)p + n, std::true_type{}}; } +// the ssl role +enum ssl_role +{ + YSSL_CLIENT, + YSSL_SERVER, +}; struct io_hostent { io_hostent() = default; io_hostent(cxx17::string_view ip, u_short port) : host_(cxx17::svtos(ip)), port_(port) {} - io_hostent(io_hostent&& rhs) : host_(std::move(rhs.host_)), port_(rhs.port_) {} + io_hostent(io_hostent&& rhs) YASIO__NOEXCEPT : host_(std::move(rhs.host_)), port_(rhs.port_) {} io_hostent(const io_hostent& rhs) : host_(rhs.host_), port_(rhs.port_) {} void set_ip(cxx17::string_view ip) { cxx17::assign(host_, ip); } const std::string& get_ip() const { return host_; } @@ -483,37 +493,6 @@ public: unsigned int id() const { return id_; } }; -#if defined(YASIO_SSL_BACKEND) -class ssl_auto_handle { -public: - ssl_auto_handle() : ssl_(nullptr) {} - ~ssl_auto_handle() { destroy(); } - ssl_auto_handle(ssl_auto_handle&& rhs) : ssl_(rhs.release()) {} - ssl_auto_handle& operator=(ssl_auto_handle&& rhs) - { - this->reset(rhs.release()); - return *this; - } - SSL* release() - { - auto tmp = ssl_; - ssl_ = nullptr; - return tmp; - } - void reset(SSL* ssl) - { - if (ssl_) - destroy(); - ssl_ = ssl; - } - operator SSL*() { return ssl_; } - YASIO__DECL void destroy(); - -protected: - SSL* ssl_ = nullptr; -}; -#endif - class YASIO_API io_channel : public io_base { friend class io_service; friend class io_transport; @@ -524,6 +503,9 @@ class YASIO_API io_channel : public io_base { public: io_service& get_service() const { return service_; } +#if defined(YASIO_SSL_BACKEND) + YASIO__DECL SSL_CTX* get_ssl_context(bool client) const; +#endif int index() const { return index_; } u_short remote_port() const { return remote_port_; } YASIO__DECL std::string format_destination() const; @@ -626,7 +608,7 @@ private: ip::endpoint multiaddr_, multiif_; // Current it's only for UDP - dynamic_buffer_t buffer_; + sbyte_buffer buffer_; // The bytes transferred from socket low layer, currently, only works for client channel long long bytes_transferred_ = 0; @@ -636,23 +618,19 @@ private: #if defined(YASIO_HAVE_KCP) int kcp_conv_ = 0; #endif - -#if defined(YASIO_SSL_BACKEND) - ssl_auto_handle ssl_; -#endif }; // for tcp transport only class YASIO_API io_send_op { public: - io_send_op(dynamic_buffer_t&& buffer, completion_cb_t&& handler) : offset_(0), buffer_(std::move(buffer)), handler_(std::move(handler)) {} + io_send_op(sbyte_buffer&& buffer, completion_cb_t&& handler) : offset_(0), buffer_(std::move(buffer)), handler_(std::move(handler)) {} virtual ~io_send_op() {} - size_t offset_; // read pos from sending buffer - dynamic_buffer_t buffer_; // sending data buffer + size_t offset_; // read pos from sending buffer + sbyte_buffer buffer_; // sending data buffer completion_cb_t handler_; - YASIO__DECL virtual int perform(transport_handle_t transport, const void* buf, int n); + YASIO__DECL virtual int perform(transport_handle_t transport, const void* buf, int n, int& error); #if !defined(YASIO_DISABLE_OBJECT_POOL) DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_send_op, 512) @@ -662,11 +640,11 @@ public: // for udp transport only class YASIO_API io_sendto_op : public io_send_op { public: - io_sendto_op(dynamic_buffer_t&& buffer, completion_cb_t&& handler, const ip::endpoint& destination) + io_sendto_op(sbyte_buffer&& buffer, completion_cb_t&& handler, const ip::endpoint& destination) : io_send_op(std::move(buffer), std::move(handler)), destination_(destination) {} - YASIO__DECL int perform(transport_handle_t transport, const void* buf, int n) override; + YASIO__DECL int perform(transport_handle_t transport, const void* buf, int n, int& error) override; #if !defined(YASIO_DISABLE_OBJECT_POOL) DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_sendto_op, 512) #endif @@ -698,7 +676,7 @@ public: protected: io_service& get_service() const { return ctx_->get_service(); } bool is_open() const { return state_ == state::OPENED && socket_ && socket_->is_open(); } - dynamic_buffer_t fetch_packet() + sbyte_buffer fetch_packet() { expected_size_ = -1; return std::move(expected_packet_); @@ -708,16 +686,16 @@ protected: YASIO__DECL const print_fn2_t& __get_cprint() const; // Call at user thread - YASIO__DECL virtual int write(dynamic_buffer_t&&, completion_cb_t&&); + YASIO__DECL virtual int write(sbyte_buffer&&, completion_cb_t&&); // Call at user thread - virtual int write_to(dynamic_buffer_t&&, const ip::endpoint&, completion_cb_t&&) + virtual int write_to(sbyte_buffer&&, const ip::endpoint&, completion_cb_t&&) { YASIO_LOG("[warning] io_transport doesn't support 'write_to' operation!"); return 0; } - YASIO__DECL int call_read(void* data, int size, int& error); + YASIO__DECL int call_read(void* data, int size, int revent, int& error); YASIO__DECL int call_write(io_send_op*, int& error); YASIO__DECL void complete_op(io_send_op*, int error); @@ -738,12 +716,12 @@ protected: int offset_ = 0; // recv buffer offset int expected_size_ = -1; - dynamic_buffer_t expected_packet_; + sbyte_buffer expected_packet_; io_channel* ctx_; - std::function write_cb_; - std::function read_cb_; + std::function write_cb_; + std::function read_cb_; privacy::concurrent_queue send_queue_; }; @@ -760,10 +738,11 @@ public: YASIO__DECL io_transport_ssl(io_channel* ctx, xxsocket_ptr&& s); YASIO__DECL void set_primitives() override; -# if defined(YASIO_SSL_BACKEND) + YASIO__DECL void do_ssl_shutdown(); + protected: - ssl_auto_handle ssl_; -# endif + YASIO__DECL int do_ssl_handshake(int& error); // always invoke at do_read + SSL* ssl_ = nullptr; }; #else class io_transport_ssl {}; @@ -781,8 +760,8 @@ protected: YASIO__DECL void connect(); YASIO__DECL void disconnect(); - YASIO__DECL int write(dynamic_buffer_t&&, completion_cb_t&&) override; - YASIO__DECL int write_to(dynamic_buffer_t&&, const ip::endpoint&, completion_cb_t&&) override; + YASIO__DECL int write(sbyte_buffer&&, completion_cb_t&&) override; + YASIO__DECL int write_to(sbyte_buffer&&, const ip::endpoint&, completion_cb_t&&) override; YASIO__DECL void set_primitives() override; @@ -807,7 +786,7 @@ public: ikcpcb* internal_object() { return kcp_; } protected: - YASIO__DECL int write(dynamic_buffer_t&&, completion_cb_t&&) override; + YASIO__DECL int write(sbyte_buffer&&, completion_cb_t&&) override; YASIO__DECL int do_read(int revent, int& error, highp_time_t& wait_duration) override; YASIO__DECL bool do_write(highp_time_t& wait_duration) override; @@ -816,7 +795,7 @@ protected: YASIO__DECL void check_timeout(highp_time_t& wait_duration) const; - dynamic_buffer_t rawbuf_; // the low level raw buffer + sbyte_buffer rawbuf_; // the low level raw buffer ikcpcb* kcp_; std::recursive_mutex send_mtx_; }; @@ -824,7 +803,7 @@ protected: class io_transport_kcp {}; #endif -using io_packet = dynamic_buffer_t; +using io_packet = sbyte_buffer; #if !defined(YASIO_USE_SHARED_PACKET) using packet_t = io_packet; inline packet_t wrap_packet(io_packet& raw_packet) { return std::move(raw_packet); } @@ -959,6 +938,9 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] friend class io_transport_tcp; friend class io_transport_udp; friend class io_transport_kcp; +#if defined(YASIO_SSL_BACKEND) + friend class io_transport_ssl; +#endif friend class io_channel; public: @@ -1041,9 +1023,9 @@ public: */ int write(transport_handle_t thandle, const void* buf, size_t len, completion_cb_t completion_handler = nullptr) { - return write(thandle, make_dynamic_buffer(buf, len), std::move(completion_handler)); + return write(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len, std::true_type{}}, std::move(completion_handler)); } - YASIO__DECL int write(transport_handle_t thandle, dynamic_buffer_t buffer, completion_cb_t completion_handler = nullptr); + YASIO__DECL int write(transport_handle_t thandle, sbyte_buffer buffer, completion_cb_t completion_handler = nullptr); /* ** Summary: Write data to unconnected UDP transport with specified address. @@ -1054,9 +1036,9 @@ public: */ int write_to(transport_handle_t thandle, const void* buf, size_t len, const ip::endpoint& to, completion_cb_t completion_handler = nullptr) { - return write_to(thandle, make_dynamic_buffer(buf, len), to, std::move(completion_handler)); + return write_to(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len, std::true_type{}}, to, std::move(completion_handler)); } - YASIO__DECL int write_to(transport_handle_t thandle, dynamic_buffer_t buffer, const ip::endpoint& to, completion_cb_t completion_handler = nullptr); + YASIO__DECL int write_to(transport_handle_t thandle, sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t completion_handler = nullptr); // The highp_timer support, !important, the callback is called on the thread of io_service YASIO__DECL highp_timer_ptr schedule(const std::chrono::microseconds& duration, timer_cb_t); @@ -1105,17 +1087,16 @@ private: YASIO__DECL void do_connect_completion(io_channel*, fd_set_adapter& fd_set); #if defined(YASIO_SSL_BACKEND) - YASIO__DECL void init_ssl_context(); - YASIO__DECL void cleanup_ssl_context(); - YASIO__DECL void do_ssl_handshake(io_channel*); + YASIO__DECL SSL_CTX* init_ssl_context(ssl_role role); + YASIO__DECL void cleanup_ssl_context(ssl_role role); #endif #if defined(YASIO_HAVE_CARES) YASIO__DECL static void ares_getaddrinfo_cb(void* arg, int status, int timeouts, ares_addrinfo* answerlist); YASIO__DECL void ares_work_started(); YASIO__DECL void ares_work_finished(); - YASIO__DECL int register_ares_fds(socket_native_type* ares_socks, fd_set_adapter& fd_set); - YASIO__DECL void process_ares_requests(socket_native_type* socks, int count, fd_set_adapter& fd_set); + YASIO__DECL int do_ares_fds(socket_native_type* socks, fd_set_adapter& fd_set, timeval& waitd_tv); + YASIO__DECL void do_ares_process_fds(socket_native_type* socks, int count, fd_set_adapter& fd_set); YASIO__DECL void recreate_ares_channel(); YASIO__DECL void config_ares_name_servers(); YASIO__DECL void destroy_ares_channel(); @@ -1124,7 +1105,7 @@ private: void handle_connect_succeed(io_channel* ctx, xxsocket_ptr s) { handle_connect_succeed(allocate_transport(ctx, std::move(s))); } YASIO__DECL void handle_connect_succeed(transport_handle_t); YASIO__DECL void handle_connect_failed(io_channel*, int ec); - YASIO__DECL void notify_connect_succeed(transport_handle_t); + YASIO__DECL void active_transport(transport_handle_t); YASIO__DECL transport_handle_t allocate_transport(io_channel*, xxsocket_ptr&&); YASIO__DECL void deallocate_transport(transport_handle_t); @@ -1166,9 +1147,6 @@ private: YASIO__DECL void clear_transports(); // clear all transports YASIO__DECL bool close_internal(io_channel*); - // shutdown a tcp-connection if possible - YASIO__DECL void shutdown_internal(transport_handle_t); - // supporting server YASIO__DECL void do_accept(io_channel*); YASIO__DECL void do_accept_completion(io_channel*, fd_set_adapter& fd_set); @@ -1248,8 +1226,12 @@ private: print_fn2_t print_; #if defined(YASIO_SSL_BACKEND) - // The full path cacert(.pem) file for ssl verifaction + // SSL client, the full path cacert(.pem) file for ssl verifaction std::string cafile_; + + // SSL server + std::string crtfile_; + std::string keyfile_; #endif #if defined(YASIO_HAVE_CARES) @@ -1262,7 +1244,7 @@ private: // The stop flag to notify all transports needs close uint8_t stop_flag_ = 0; #if defined(YASIO_SSL_BACKEND) - SSL_CTX* ssl_ctx_ = nullptr; + SSL_CTX* ssl_roles_[2]; #endif #if defined(YASIO_HAVE_CARES) ares_channel ares_ = nullptr; // the ares handle for non blocking io dns resolve support