Update yasio to latest

This commit is contained in:
halx99 2023-01-10 22:19:30 +08:00
parent b032ba2f56
commit 1464009e63
12 changed files with 193 additions and 130 deletions

View File

@ -238,7 +238,7 @@
## yasio
- [![Upstream](https://img.shields.io/github/v/release/yasio/yasio?label=Upstream)](https://github.com/yasio/yasio)
- Version: 3.39.6
- Version: git 3.39.7-2e7723f
- License: MIT WITH Anti-996
## zlib

View File

@ -227,6 +227,12 @@ SOFTWARE.
// https://github.com/c-ares/c-ares/pull/148
#define YASIO_FALLBACK_NAME_SERVERS "8.8.8.8,223.5.5.5,114.114.114.114"
// Since ubuntu 16.04, the /etc/resolv.conf control by systemd-resolved service,
// and the preferred non loopback name server was store to /run/systemd/resolve/resolv.conf
// refer to: https://unix.stackexchange.com/questions/612416/why-does-etc-resolv-conf-point-at-127-0-0-53
#define YASIO_SYSTEMD_RESOLV_PATH "/run/systemd/resolve/resolv.conf"
#define YASIO_SYSTEMD_RESOLV_PATH_LEN (sizeof(YASIO_SYSTEMD_RESOLV_PATH) - 1)
// The yasio ssl client PIN for server to recognize
#define YASIO_SSL_PIN "yasio_ssl_client"
#define YASIO_SSL_PIN_LEN (sizeof(YASIO_SSL_PIN) - 1)

View File

@ -62,13 +62,20 @@ YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts)
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
int fail_count = 0;
yssl_splitpath(opts.crtfile_, [&](char* first, char* last) {
yssl_split_term null_term(last);
if ((ret = ::mbedtls_x509_crt_parse_file(&ctx->cert, first)) != 0)
{
++fail_count;
YASIO_LOG("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret);
break;
}
return !!ret;
});
if (!fail_count)
authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
}
if (opts.client)
@ -127,6 +134,7 @@ YASIO__DECL ssl_st* yssl_new(ssl_ctx_st* ctx, int fd, const char* hostname, bool
// ssl_set_fd
ssl->bio.fd = fd;
::mbedtls_ssl_set_bio(ssl, &ssl->bio, ::mbedtls_net_send, ::mbedtls_net_recv, nullptr /* rev_timeout() */);
if (client)
::mbedtls_ssl_set_hostname(ssl, hostname);
return ssl;
}

View File

@ -47,9 +47,29 @@ YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts)
if (opts.client)
{
int fail_count = -1;
if (yasio__valid_str(opts.crtfile_))
{
if (::SSL_CTX_load_verify_locations(ctx, opts.crtfile_, nullptr) == 1)
fail_count = 0;
yssl_splitpath(opts.crtfile_, [&](char* first, char* last) {
yssl_split_term null_term(last);
# if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
/* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
bool ok = ::SSL_CTX_load_verify_file(ctx, first) == 1;
# else
bool ok = ::SSL_CTX_load_verify_locations(ctx, first, nullptr) == 1;
# endif
if (!ok)
{
++fail_count;
YASIO_LOG("[global] load ca certifaction file failed!");
}
return !ok;
});
}
if (!fail_count)
{
::SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, ::SSL_CTX_get_verify_callback(ctx));
# if OPENSSL_VERSION_NUMBER >= 0x10101000L
@ -60,14 +80,11 @@ YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts)
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);
::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);
::SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr);
}
else
{

View File

@ -10,6 +10,7 @@
#pragma once
#include <vector>
#include <chrono>
#include "yasio/detail/socket.hpp"
namespace yasio
@ -38,7 +39,7 @@ public:
int poll_io(int waitd_ms)
{
timeval waitd_tv = {(decltype(timeval::tv_sec))(waitd_ms / 1000), (decltype(timeval::tv_usec))(waitd_ms % 1000)};
timeval waitd_tv = {(decltype(timeval::tv_sec))(waitd_ms / std::milli::den), (decltype(timeval::tv_usec))(waitd_ms % std::milli::den)};
return ::select(this->max_descriptor_, &(fd_set_[read_op]), &(fd_set_[write_op]), nullptr, &waitd_tv);
}

View File

@ -1,55 +0,0 @@
//////////////////////////////////////////////////////////////////////////////////////////
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
// client application.
//////////////////////////////////////////////////////////////////////////////////////////
//
// detail/signal_blocker.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2012-2023 HALX99 (halx99 at live dot com)
#pragma once
#include "yasio/compiler/feature_test.hpp"
#if !defined(_WIN32)
#include <pthread.h>
#include <signal.h>
#endif
namespace yasio
{
YASIO__NS_INLINE
namespace inet
{
#if defined(_WIN32)
class signal_blocker {};
#else
class signal_blocker {
public:
// Constructor blocks all signals for the calling thread.
signal_blocker() : blocked_(false)
{
sigset_t new_mask;
sigfillset(&new_mask);
blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
}
// Destructor restores the previous signal mask.
~signal_blocker()
{
if (blocked_)
pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
}
private:
// Have signals been blocked.
bool blocked_;
// The previous signal mask.
sigset_t old_mask_;
};
#endif
} // namespace inet
} // namespace yasio

View File

@ -58,8 +58,8 @@ struct ssl_st : public mbedtls_ssl_context {
#if defined(YASIO_SSL_BACKEND)
struct yssl_options {
const char* crtfile_;
const char* keyfile_;
char* crtfile_;
char* keyfile_;
bool client;
};
@ -87,7 +87,50 @@ YASIO__DECL int yssl_read(ssl_st* ssl, void* data, size_t len, int& err);
///////////////////////////////////////////////////////////////////
// --- Implement common yasio ssl api with different ssl backends
#define yasio__valid_str(str) (str && *str)
#define yasio__valid_str(cstr) (cstr && *cstr)
#define yasio__c_str(str) (!str.empty() ? &str.front() : nullptr)
/* private use for split cert files */
template <typename _Fty>
inline bool yssl_splitpath(char* str, _Fty&& func)
{
auto _Start = str; // the start of every string
auto _Ptr = str; // source string iterator
bool aborted = false;
while ((_Ptr = strchr(_Ptr, ',')))
{
if (_Start <= _Ptr)
{
if ((aborted = func(_Start, _Ptr)))
break;
}
_Start = _Ptr + 1;
++_Ptr;
}
if (!aborted)
aborted = func(_Start, nullptr); // last one
return aborted;
}
struct yssl_split_term {
yssl_split_term(char* end)
{
if (end) {
this->val_ = *end;
*end = '\0';
this->end_ = end;
}
}
~yssl_split_term()
{
if (this->end_)
*this->end_ = this->val_;
}
private:
char* end_ = nullptr;
char val_ = '\0';
};
#if YASIO_SSL_BACKEND == 1 // openssl
# include "yasio/detail/openssl.inl"

View File

@ -28,6 +28,7 @@ SOFTWARE.
#ifndef YASIO__UTILS_HPP
#define YASIO__UTILS_HPP
#include <assert.h>
#include <sys/stat.h>
#include <chrono>
#include <algorithm>
#include "yasio/compiler/feature_test.hpp"
@ -40,18 +41,21 @@ typedef std::chrono::high_resolution_clock steady_clock_t;
typedef std::chrono::system_clock system_clock_t;
// The high precision nano seconds timestamp since epoch
template <typename _Ty = steady_clock_t> inline highp_time_t xhighp_clock()
template <typename _Ty = steady_clock_t>
inline highp_time_t xhighp_clock()
{
auto duration = _Ty::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
}
// The high precision micro seconds timestamp since epoch
template <typename _Ty = steady_clock_t> inline highp_time_t highp_clock()
template <typename _Ty = steady_clock_t>
inline highp_time_t highp_clock()
{
return xhighp_clock<_Ty>() / std::milli::den;
}
// The normal precision milli seconds timestamp since epoch
template <typename _Ty = steady_clock_t> inline highp_time_t clock()
template <typename _Ty = steady_clock_t>
inline highp_time_t clock()
{
return xhighp_clock<_Ty>() / std::micro::den;
}
@ -63,14 +67,26 @@ inline highp_time_t time_now() { return ::time(nullptr); }
#if YASIO__HAS_CXX17
using std::clamp;
#else
template <typename _Ty> const _Ty& clamp(const _Ty& v, const _Ty& lo, const _Ty& hi)
template <typename _Ty>
const _Ty& clamp(const _Ty& v, const _Ty& lo, const _Ty& hi)
{
assert(!(hi < lo));
return v < lo ? lo : hi < v ? hi : v;
}
#endif
template <typename _Ty> inline void invoke_dtor(_Ty* p) { p->~_Ty(); }
template <typename _Ty>
inline void invoke_dtor(_Ty* p)
{
p->~_Ty();
}
inline bool is_regular_file(const char* path)
{
struct stat st;
return (::stat(path, &st) == 0) && (st.st_mode & S_IFREG);
}
} // namespace yasio
#endif

View File

@ -384,7 +384,26 @@ xxsocket& xxsocket::swap(xxsocket& rhs)
bool xxsocket::open(int af, int type, int protocol)
{
if (invalid_socket == this->fd)
{
# if defined(_WIN32)
this->fd = ::socket(af, type, protocol);
//socket_native_type s = ::WSASocketW(af, type, protocol, 0, 0, WSA_FLAG_OVERLAPPED);
////get_last_error(ec, s == invalid_socket);
//if (s == invalid_socket)
// return false;
//this->fd = s;
//if (af == ASIO_OS_DEF(AF_INET6))
//{
// // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to
// // false. This will only succeed on Windows Vista and later versions of
// // Windows, where a dual-stack IPv4/v6 implementation is available.
// DWORD optval = 0;
// ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&optval), sizeof(optval));
//}
#else
this->fd = ::socket(af, type, protocol);
#endif
}
return is_open();
}

View File

@ -62,7 +62,7 @@ namespace ip
{
#pragma pack(push, 1)
// ip packet
struct ip_header {
struct ip_hdr_st {
// header size; 5+
unsigned char header_length : 4;
@ -106,7 +106,7 @@ struct ip_header {
unsigned char protocol; // TCP / UDP / Other
// check header of IP-PACKET 's correctness.
unsigned short checksum;
unsigned short sum;
typedef union {
unsigned int value;
@ -122,7 +122,7 @@ struct ip_header {
dotted_decimal_t dst_ip;
};
struct psd_header {
struct psd_hdr_st {
unsigned long src_addr;
unsigned long dst_addr;
char mbz;
@ -130,7 +130,7 @@ struct psd_header {
unsigned short tcp_length;
};
struct tcp_header {
struct tcp_hdr_st {
unsigned short src_port; // lgtm [cpp/class-many-fields]
unsigned short dst_port;
unsigned int seqno;
@ -139,32 +139,32 @@ struct tcp_header {
unsigned char reserved : 4;
unsigned char flg_fin : 1, flg_syn : 1, flg_rst : 1, flg_psh : 1, flg_ack : 1, flg_urg : 1, flg_reserved : 2;
unsigned short win_length;
unsigned short checksum;
unsigned short sum;
unsigned short urp;
};
struct udp_header {
struct udp_hdr_st {
unsigned short src_port;
unsigned short dst_port;
unsigned short length;
unsigned short checksum;
unsigned short sum;
};
struct icmp_header {
struct icmp_hdr_st {
unsigned char type; // 8bit type
unsigned char code; // 8bit code
unsigned short checksum; // 16bit check sum
unsigned short sum; // 16bit check sum
unsigned short id; // identifier: usually use process id
unsigned short seqno; // message sequence NO.
};
struct eth_header {
struct eth_hdr_st {
unsigned dst_eth[6];
unsigned src_eth[6];
unsigned eth_type;
};
struct arp_header {
struct arp_hdr_st {
unsigned short arp_hw; // format of hardware address
unsigned short arp_pro; // format of protocol address
unsigned char arp_hlen; // length of hardware address
@ -176,9 +176,9 @@ struct arp_header {
unsigned long arp_tpa; // target protocol address;
};
struct arp_packet {
eth_header ethhdr;
arp_header arphdr;
struct arp_packet_st {
eth_hdr_st ethhdr;
arp_hdr_st arphdr;
};
#pragma pack(pop)

View File

@ -39,7 +39,6 @@ SOFTWARE.
#include <sys/stat.h>
#include <fcntl.h>
#include "yasio/detail/thread_name.hpp"
#include "yasio/detail/signal_blocker.hpp"
#if defined(YASIO_SSL_BACKEND)
# include "yasio/detail/ssl.hpp"
@ -207,7 +206,7 @@ io_channel::io_channel(io_service& service, int index) : io_base(), service_(ser
socket_ = std::make_shared<xxsocket>();
state_ = io_base::state::CLOSED;
index_ = index;
decode_len_ = [=](void* ptr, int len) { return this->__builtin_decode_len(ptr, len); };
decode_len_ = [this](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
@ -443,13 +442,13 @@ 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*, int& error) {
this->write_cb_ = [this](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) {
this->read_cb_ = [this](void* data, int len, int revent, int& error) {
if (revent)
{
int n = socket_->recv(data, len);
@ -478,13 +477,13 @@ int io_transport_ssl::do_ssl_handshake(int& 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) {
this->read_cb_ = [this](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); };
this->write_cb_ = [this](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());
@ -517,7 +516,7 @@ void io_transport_ssl::do_ssl_shutdown()
}
void io_transport_ssl::set_primitives()
{
this->read_cb_ = [=](void* /*data*/, int /*len*/, int /*revent*/, int& error) { return do_ssl_handshake(error); };
this->read_cb_ = [this](void* /*data*/, int /*len*/, int /*revent*/, int& error) { return do_ssl_handshake(error); };
}
#endif
// ----------------------- io_transport_udp ----------------
@ -589,7 +588,7 @@ void io_transport_udp::set_primitives()
io_transport::set_primitives();
else
{
this->write_cb_ = [=](const void* data, int len, const ip::endpoint* destination, int& error) {
this->write_cb_ = [this](const void* data, int len, const ip::endpoint* destination, int& error) {
assert(destination);
int n = socket_->sendto(data, len, *destination);
if (n < 0)
@ -600,7 +599,7 @@ void io_transport_udp::set_primitives()
}
return n;
};
this->read_cb_ = [=](void* data, int len, int revent, int& error) {
this->read_cb_ = [this](void* data, int len, int revent, int& error) {
if (revent)
{
ip::endpoint peer;
@ -735,8 +734,6 @@ void io_service::start(event_cb_t cb)
this->state_ = io_service::state::RUNNING;
if (!options_.no_new_thread_)
{
signal_blocker sb;
(void)sb;
this->worker_ = std::thread(&io_service::run, this);
this->worker_id_ = worker_.get_id();
}
@ -805,7 +802,7 @@ void io_service::initialize(const io_hostent* channel_eps, int channel_count)
if (channel_count < 1)
channel_count = 1;
options_.resolv_ = [=](std::vector<ip::endpoint>& eps, const char* host, unsigned short port) { return this->resolve(eps, host, port); };
options_.resolv_ = [this](std::vector<ip::endpoint>& eps, const char* host, unsigned short port) { return this->resolve(eps, host, port); };
register_descriptor(interrupter_.read_descriptor(), socket_event::read);
// create channels
@ -894,11 +891,9 @@ void io_service::run()
do
{
auto wait_duration = get_timeout(this->wait_duration_); // Gets current wait duration
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)};
const auto waitd_usec = get_timeout(this->wait_duration_); // Gets current wait duration
#if defined(YASIO_HAVE_CARES)
/**
* retrieves the set of file descriptors which the calling application should poll io,
@ -907,10 +902,13 @@ void io_service::run()
* https://c-ares.org/ares_timeout.html
* https://c-ares.org/ares_process_fd.html
*/
timeval waitd_tv = {(decltype(timeval::tv_sec))(waitd_usec / std::micro::den), (decltype(timeval::tv_usec))(waitd_usec % std::micro::den)};
auto ares_nfds = do_ares_fds(ares_socks, fd_set, waitd_tv);
const auto waitd_ms = static_cast<int>(waitd_tv.tv_sec * std::milli::den + waitd_tv.tv_usec / std::milli::den);
#else
const auto waitd_ms = static_cast<int>(waitd_usec / std::milli::den);
#endif
const int waitd_ms = static_cast<int>(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);
@ -1219,8 +1217,8 @@ void io_service::do_connect_completion(io_channel* ctx, fd_set_adapter& fd_set)
#if defined(YASIO_SSL_BACKEND)
SSL_CTX* io_service::init_ssl_context(ssl_role role)
{
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});
auto ctx = role == YSSL_CLIENT ? yssl_ctx_new(yssl_options{yasio__c_str(options_.cafile_), nullptr, true})
: yssl_ctx_new(yssl_options{yasio__c_str(options_.crtfile_), yasio__c_str(options_.keyfile_), false});
ssl_roles_[role] = ctx;
return ctx;
}
@ -1309,10 +1307,18 @@ void io_service::recreate_ares_channel()
if (ares_)
destroy_ares_channel();
int optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES /* | ARES_OPT_LOOKUPS*/;
ares_options options = {};
options.timeout = static_cast<int>(this->options_.dns_queries_timeout_ / std::milli::den);
options.tries = this->options_.dns_queries_tries_;
int status = ::ares_init_options(&ares_, &options, ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES /* | ARES_OPT_LOOKUPS*/);
# if defined(__linux__) && !defined(__ANDROID__)
if (yasio::is_regular_file(YASIO_SYSTEMD_RESOLV_PATH))
{
options.resolvconf_path = strndup(YASIO_SYSTEMD_RESOLV_PATH, YASIO_SYSTEMD_RESOLV_PATH_LEN);
optmask |= ARES_OPT_RESOLVCONF;
}
# endif
int status = ::ares_init_options(&ares_, &options, optmask);
if (status == ARES_SUCCESS)
{
YASIO_KLOGD("[c-ares] create channel succeed");
@ -1806,6 +1812,8 @@ void io_service::process_timers()
}
highp_time_t io_service::get_timeout(highp_time_t usec)
{
this->wait_duration_ = yasio__max_wait_usec; // Reset next wait duration per frame
if (this->timer_queue_.empty())
return usec;
@ -1905,11 +1913,11 @@ void io_service::start_query(io_channel* ctx)
#endif
#if !defined(YASIO_HAVE_CARES)
// init async name query thread state
std::string resolving_host = ctx->remote_host_;
u_short resolving_port = ctx->remote_port_;
auto resolving_host = ctx->remote_host_;
auto resolving_port = ctx->remote_port_;
std::weak_ptr<cxx17::shared_mutex> weak_mutex = life_mutex_;
std::weak_ptr<life_token> life_token = life_token_;
std::thread async_resolv_thread([=] {
std::thread async_resolv_thread([this, life_token, weak_mutex, resolving_host, resolving_port, ctx] {
// check life token
if (life_token.use_count() < 1)
return;

View File

@ -1048,6 +1048,8 @@ public:
// Gets channel by index
YASIO__DECL io_channel* channel_at(size_t index) const;
YASIO__DECL static const char* strerror(int error);
private:
YASIO__DECL void do_stop(uint8_t flags);
YASIO__DECL void schedule_timer(highp_timer*, timer_cb_t&&);
@ -1151,8 +1153,6 @@ private:
YASIO__DECL void do_accept(io_channel*);
YASIO__DECL void do_accept_completion(io_channel*, fd_set_adapter& fd_set);
YASIO__DECL static const char* strerror(int error);
/*
** summary: For udp-server only, make dgram handle to communicate with client
*/