From 315cbcfc3d21c4dfe15e1d8f62b752b6838b6926 Mon Sep 17 00:00:00 2001 From: Hevake Date: Tue, 16 Jun 2026 22:57:09 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat(network),=20=E5=88=9D=E6=AD=A5?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20tls=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/http/Makefile | 2 +- modules/main/Makefile | 1 + modules/network/CMakeLists.txt | 27 ++- modules/network/Makefile | 23 ++- modules/network/buffered_fd.cpp | 67 +++++--- modules/network/buffered_fd.h | 5 + modules/network/buffered_ssl_fd.cpp | 193 +++++++++++++++++++++ modules/network/buffered_ssl_fd.h | 63 +++++++ modules/network/tcp_acceptor.cpp | 13 +- modules/network/tcp_acceptor.h | 12 +- modules/network/tcp_client.cpp | 29 +++- modules/network/tcp_client.h | 5 + modules/network/tcp_connection.cpp | 25 +-- modules/network/tcp_connection.h | 32 +++- modules/network/tcp_connector.cpp | 13 +- modules/network/tcp_connector.h | 12 +- modules/network/tcp_factory.h | 43 +++++ modules/network/tcp_raw_acceptor.cpp | 58 +++++++ modules/network/tcp_raw_acceptor.h | 42 +++++ modules/network/tcp_raw_connection.cpp | 60 +++++++ modules/network/tcp_raw_connection.h | 41 +++++ modules/network/tcp_raw_connector.cpp | 58 +++++++ modules/network/tcp_raw_connector.h | 42 +++++ modules/network/tcp_raw_factory.cpp | 38 +++++ modules/network/tcp_raw_factory.h | 38 +++++ modules/network/tcp_server.cpp | 27 ++- modules/network/tcp_server.h | 5 + modules/network/tcp_tls_acceptor.cpp | 192 +++++++++++++++++++++ modules/network/tcp_tls_acceptor.h | 68 ++++++++ modules/network/tcp_tls_connection.cpp | 83 +++++++++ modules/network/tcp_tls_connection.h | 50 ++++++ modules/network/tcp_tls_connector.cpp | 203 +++++++++++++++++++++++ modules/network/tcp_tls_connector.h | 68 ++++++++ modules/network/tcp_tls_factory.cpp | 183 ++++++++++++++++++++ modules/network/tcp_tls_factory.h | 53 ++++++ modules/network/tls_config.cpp | 42 +++++ modules/network/tls_config.h | 53 ++++++ modules/run/Makefile | 1 + modules/terminal/Makefile | 2 +- modules/websocket/Makefile | 2 +- modules/websocket/client/client_impl.cpp | 4 +- modules/websocket/client/client_impl.h | 1 + 42 files changed, 1892 insertions(+), 87 deletions(-) create mode 100644 modules/network/buffered_ssl_fd.cpp create mode 100644 modules/network/buffered_ssl_fd.h create mode 100644 modules/network/tcp_factory.h create mode 100644 modules/network/tcp_raw_acceptor.cpp create mode 100644 modules/network/tcp_raw_acceptor.h create mode 100644 modules/network/tcp_raw_connection.cpp create mode 100644 modules/network/tcp_raw_connection.h create mode 100644 modules/network/tcp_raw_connector.cpp create mode 100644 modules/network/tcp_raw_connector.h create mode 100644 modules/network/tcp_raw_factory.cpp create mode 100644 modules/network/tcp_raw_factory.h create mode 100644 modules/network/tcp_tls_acceptor.cpp create mode 100644 modules/network/tcp_tls_acceptor.h create mode 100644 modules/network/tcp_tls_connection.cpp create mode 100644 modules/network/tcp_tls_connection.h create mode 100644 modules/network/tcp_tls_connector.cpp create mode 100644 modules/network/tcp_tls_connector.h create mode 100644 modules/network/tcp_tls_factory.cpp create mode 100644 modules/network/tcp_tls_factory.h create mode 100644 modules/network/tls_config.cpp create mode 100644 modules/network/tls_config.h diff --git a/modules/http/Makefile b/modules/http/Makefile index 775eadba..fd90a328 100644 --- a/modules/http/Makefile +++ b/modules/http/Makefile @@ -66,7 +66,7 @@ TEST_CPP_SRC_FILES = \ url_test.cpp \ server/request_parser_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl ENABLE_SHARED_LIB = no diff --git a/modules/main/Makefile b/modules/main/Makefile index b99bd5da..8ed0a5a9 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -53,6 +53,7 @@ TEST_LDFLAGS := $(LDFLAGS) \ -ltbox_log \ -ltbox_util \ -ltbox_base \ + -lssl -lcrypto \ -lpthread -ldl ENABLE_SHARED_LIB = no diff --git a/modules/network/CMakeLists.txt b/modules/network/CMakeLists.txt index a2fe0b85..c0aec3ca 100644 --- a/modules/network/CMakeLists.txt +++ b/modules/network/CMakeLists.txt @@ -27,10 +27,13 @@ set(TBOX_NETWORK_VERSION ${TBOX_NETWORK_VERSION_MAJOR}.${TBOX_NETWORK_VERSION_MI add_definitions(-DMODULE_ID="tbox.network") +find_package(OpenSSL REQUIRED) + set(TBOX_LIBRARY_NAME tbox_network) set(TBOX_NETWORK_HEADERS buffered_fd.h + buffered_ssl_fd.h byte_stream.h stdio_stream.h uart.h @@ -39,16 +42,27 @@ set(TBOX_NETWORK_HEADERS sockaddr.h udp_socket.h tcp_connection.h + tcp_raw_connection.h + tcp_tls_connection.h tcp_acceptor.h + tcp_raw_acceptor.h + tcp_tls_acceptor.h tcp_connector.h + tcp_raw_connector.h + tcp_tls_connector.h tcp_client.h tcp_server.h + tcp_factory.h + tcp_raw_factory.h + tcp_tls_factory.h + tls_config.h net_if.h domain_name.h dns_request.h) set(TBOX_NETWORK_SOURCES buffered_fd.cpp + buffered_ssl_fd.cpp stdio_stream.cpp uart.cpp socket_fd.cpp @@ -56,10 +70,19 @@ set(TBOX_NETWORK_SOURCES sockaddr.cpp udp_socket.cpp tcp_connection.cpp + tcp_raw_connection.cpp + tcp_tls_connection.cpp tcp_acceptor.cpp + tcp_raw_acceptor.cpp + tcp_tls_acceptor.cpp tcp_connector.cpp + tcp_raw_connector.cpp + tcp_tls_connector.cpp tcp_client.cpp tcp_server.cpp + tcp_raw_factory.cpp + tcp_tls_factory.cpp + tls_config.cpp net_if.cpp dns_request.cpp) @@ -81,9 +104,11 @@ set_target_properties( SOVERSION ${TBOX_NETWORK_VERSION_MAJOR} ) +target_link_libraries(${TBOX_LIBRARY_NAME} OpenSSL::SSL OpenSSL::Crypto) + if(${TBOX_ENABLE_TEST}) add_executable(${TBOX_LIBRARY_NAME}_test ${TBOX_NETWORK_TEST_SOURCES}) - target_link_libraries(${TBOX_LIBRARY_NAME}_test gmock_main gmock gtest pthread ${TBOX_LIBRARY_NAME} tbox_base tbox_util tbox_event tbox_eventx rt dl) + target_link_libraries(${TBOX_LIBRARY_NAME}_test gmock_main gmock gtest pthread ${TBOX_LIBRARY_NAME} tbox_base tbox_util tbox_event tbox_eventx OpenSSL::SSL OpenSSL::Crypto rt dl) add_test(NAME ${TBOX_LIBRARY_NAME}_test COMMAND ${TBOX_LIBRARY_NAME}_test) endif() diff --git a/modules/network/Makefile b/modules/network/Makefile index e5823adf..57498426 100644 --- a/modules/network/Makefile +++ b/modules/network/Makefile @@ -26,6 +26,7 @@ LIB_VERSION_Z = 1 HEAD_FILES = \ buffered_fd.h \ + buffered_ssl_fd.h \ byte_stream.h \ stdio_stream.h \ uart.h \ @@ -34,16 +35,27 @@ HEAD_FILES = \ sockaddr.h \ udp_socket.h \ tcp_connection.h \ + tcp_raw_connection.h \ + tcp_tls_connection.h \ tcp_acceptor.h \ + tcp_raw_acceptor.h \ + tcp_tls_acceptor.h \ tcp_connector.h \ + tcp_raw_connector.h \ + tcp_tls_connector.h \ tcp_client.h \ tcp_server.h \ + tcp_factory.h \ + tcp_raw_factory.h \ + tcp_tls_factory.h \ + tls_config.h \ net_if.h \ domain_name.h \ dns_request.h \ CPP_SRC_FILES = \ buffered_fd.cpp \ + buffered_ssl_fd.cpp \ stdio_stream.cpp \ uart.cpp \ socket_fd.cpp \ @@ -51,10 +63,19 @@ CPP_SRC_FILES = \ sockaddr.cpp \ udp_socket.cpp \ tcp_connection.cpp \ + tcp_raw_connection.cpp \ + tcp_tls_connection.cpp \ tcp_acceptor.cpp \ + tcp_raw_acceptor.cpp \ + tcp_tls_acceptor.cpp \ tcp_connector.cpp \ + tcp_raw_connector.cpp \ + tcp_tls_connector.cpp \ tcp_client.cpp \ tcp_server.cpp \ + tcp_raw_factory.cpp \ + tcp_tls_factory.cpp \ + tls_config.cpp \ net_if.cpp \ dns_request.cpp \ @@ -70,7 +91,7 @@ TEST_CPP_SRC_FILES = \ net_if_test.cpp \ dns_request_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl ENABLE_SHARED_LIB = no diff --git a/modules/network/buffered_fd.cpp b/modules/network/buffered_fd.cpp index e9d1bedd..096d966d 100644 --- a/modules/network/buffered_fd.cpp +++ b/modules/network/buffered_fd.cpp @@ -145,7 +145,7 @@ bool BufferedFd::send(const void *data_ptr, size_t data_size) send_buff_.append(data_ptr, data_size); } else { //! 否则尝试发送 - ssize_t wsize = fd_.write(data_ptr, data_size); + ssize_t wsize = doWrite(data_ptr, data_size); if (wsize >= 0) { //! 如果发送正常 //! 如果没有发送完,还有剩余的数据 if (static_cast(wsize) < data_size) { @@ -179,38 +179,53 @@ void BufferedFd::shrinkSendBuffer() send_buff_.shrink(); } +ssize_t BufferedFd::doRead(void *buffer, size_t size) +{ + return fd_.read(buffer, size); +} + +ssize_t BufferedFd::doWrite(const void *data, size_t size) +{ + return fd_.write(data, size); +} + void BufferedFd::onReadCallback(short) { RECORD_SCOPE(); - struct iovec rbuf[2]; - char extbuf[1024]; //! 扩展存储空间 + char extbuf[1024]; //! 扩展存储空间,当 recv_buff_ 写满时使用 + //! 循环读取数据,直到读不到为止 + ssize_t rsize; size_t writable_size = recv_buff_.writableSize(); - //! 优先将数据读入到 recv_buff_ 中去,如果它装不下就再存到 extbuff 中 - rbuf[0].iov_base = recv_buff_.writableBegin(); - rbuf[0].iov_len = writable_size; - rbuf[1].iov_base = extbuf; - rbuf[1].iov_len = sizeof(extbuf); - - ssize_t rsize = fd_.readv(rbuf, 2); - if (rsize > 0) { //! 读到了数据 - do { - if (static_cast(rsize) > writable_size) { - //! 如果实际读出的数据比 recv_buff_ 的可写区还大,说明有部分数据是写到了 extbuf 中去了 - recv_buff_.hasWritten(writable_size); - size_t remain_size = rsize - writable_size; //! 计算 extbuf 中的数据大小 - recv_buff_.append(extbuf, remain_size); - } else { - recv_buff_.hasWritten(rsize); - } - - //! 继续读,直到 rsize == 0,表示读完为止 + if (writable_size > 0) { + //! 优先读入 recv_buff_ 可写区域 + while ((rsize = doRead(recv_buff_.writableBegin(), writable_size)) > 0) { + recv_buff_.hasWritten(rsize); + writable_size = recv_buff_.writableSize(); + } + } else { + //! recv_buff_ 可写空间不足时,先读入 extbuf 再 append + while ((rsize = doRead(extbuf, sizeof(extbuf))) > 0) { + recv_buff_.append(extbuf, rsize); writable_size = recv_buff_.writableSize(); - rbuf[0].iov_base = recv_buff_.writableBegin(); - rbuf[0].iov_len = writable_size; - } while ((rsize = fd_.readv(rbuf, 2)) > 0); + //! 如果 recv_buff_ 又有了可写空间,切换到直接读入模式 + if (writable_size > 0) { + ssize_t r2; + while ((r2 = doRead(recv_buff_.writableBegin(), writable_size)) > 0) { + recv_buff_.hasWritten(r2); + writable_size = recv_buff_.writableSize(); + } + //! 用 r2 更新 rsize 用于后续状态判断 + if (r2 != 0) + rsize = r2; + break; + } + } + } + //! 检查读取结果 + if (recv_buff_.readableSize() > 0) { //! 读到了数据 //! 如果有绑定接收者,则应将数据直接转发给接收者 if (wp_receiver_ != nullptr) { wp_receiver_->send(recv_buff_.readableBegin(), recv_buff_.readableSize()); @@ -261,7 +276,7 @@ void BufferedFd::onWriteCallback(short) } //! 下面是有数据要发送的 - ssize_t wsize = fd_.write(send_buff_.readableBegin(), send_buff_.readableSize()); + ssize_t wsize = doWrite(send_buff_.readableBegin(), send_buff_.readableSize()); if (wsize >= 0) { send_buff_.hasRead(wsize); } else { diff --git a/modules/network/buffered_fd.h b/modules/network/buffered_fd.h index fa2b13f1..618583f6 100644 --- a/modules/network/buffered_fd.h +++ b/modules/network/buffered_fd.h @@ -87,6 +87,11 @@ class BufferedFd : public ByteStream { inline Fd fd() const { return fd_; } inline State state() const { return state_; } + protected: + //! 可被子类覆写的底层I/O方法(如 BufferedSslFd 使用 SSL_read/SSL_write) + virtual ssize_t doRead(void *buffer, size_t size); + virtual ssize_t doWrite(const void *data, size_t size); + private: void onReadCallback(short); void onWriteCallback(short); diff --git a/modules/network/buffered_ssl_fd.cpp b/modules/network/buffered_ssl_fd.cpp new file mode 100644 index 00000000..70247b7c --- /dev/null +++ b/modules/network/buffered_ssl_fd.cpp @@ -0,0 +1,193 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "buffered_ssl_fd.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace tbox { +namespace network { + +BufferedSslFd::BufferedSslFd(event::Loop *wp_loop) : + BufferedFd(wp_loop) +{ } + +BufferedSslFd::~BufferedSslFd() +{ + if (ssl_ != nullptr) { + //! 尝试优雅关闭 SSL 连接 + SSL_shutdown(ssl_); + SSL_free(ssl_); + ssl_ = nullptr; + } +} + +bool BufferedSslFd::initialize(Fd fd, SSL *ssl, short events) +{ + if (ssl == nullptr) { + LogWarn("ssl is null"); + return false; + } + + ssl_ = ssl; + + //! 设置 SSL 模式:允许写缓冲区移动(因为我们的 send buffer 在发送过程中可能被修改) + SSL_set_mode(ssl_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + //! 调用父类 initialize + if (!BufferedFd::initialize(fd, events)) + return false; + + return true; +} + +ssize_t BufferedSslFd::doRead(void *buffer, size_t size) +{ + RECORD_SCOPE(); + + if (ssl_ == nullptr) + return -1; + + ERR_clear_error(); + ssize_t rsize = SSL_read(ssl_, buffer, size); + + if (rsize > 0) { + //! 读取成功,清除 renegotiation 标记 + ssl_read_wants_write_ = false; + ssl_write_wants_read_ = false; + return rsize; + } + + int ssl_error = SSL_get_error(ssl_, rsize); + + if (ssl_error == SSL_ERROR_ZERO_RETURN) { + //! 对端关闭连接(close_notify) + errno = 0; //! 这不是错误,类似 read 返回 0 + return 0; + } + + if (ssl_error == SSL_ERROR_WANT_READ) { + //! 需要更多网络数据才能完成 SSL_read,等下次读事件触发即可 + //! 返回 -1 并设置 errno = EAGAIN,让上层 BufferedFd::onReadCallback 知道暂时没数据 + errno = EAGAIN; + return -1; + } + + if (ssl_error == SSL_ERROR_WANT_WRITE) { + //! renegotiation:SSL_read 需要写入数据 + //! 需要临时启用写事件 + handleSslRenegotiation(ssl_error, true); + errno = EAGAIN; + return -1; + } + + if (ssl_error == SSL_ERROR_SYSCALL) { + //! 系统调用错误 + if (errno == 0) { + //! EOF:对端关闭了连接 + return 0; + } + //! 其他系统错误,errno 已由系统设置 + return -1; + } + + //! 其他 SSL 错误 + LogWarn("SSL_read error: %d, errno:%d", ssl_error, errno); + errno = ECONNRESET; //! 将 SSL 错误映射为连接错误 + return -1; +} + +ssize_t BufferedSslFd::doWrite(const void *data, size_t size) +{ + RECORD_SCOPE(); + + if (ssl_ == nullptr) + return -1; + + ERR_clear_error(); + ssize_t wsize = SSL_write(ssl_, data, size); + + if (wsize > 0) { + //! 写入成功,清除 renegotiation 标记 + ssl_read_wants_write_ = false; + ssl_write_wants_read_ = false; + return wsize; + } + + int ssl_error = SSL_get_error(ssl_, wsize); + + if (ssl_error == SSL_ERROR_WANT_WRITE) { + //! 需要等 fd 可写才能继续 SSL_write + //! 返回 0 表示"没写成功但不是错误",让 BufferedFd 保持数据在 send buffer + //! 注意:不能返回 -1 因为 BufferedFd 的 send() 会把 -1 当成错误丢弃数据 + errno = EAGAIN; + //! 返回 0 让 BufferedFd::send() 认为"未发送",数据留在 send buffer + return 0; + } + + if (ssl_error == SSL_ERROR_WANT_READ) { + //! renegotiation:SSL_write 需要读取数据 + handleSslRenegotiation(ssl_error, false); + errno = EAGAIN; + return 0; + } + + if (ssl_error == SSL_ERROR_ZERO_RETURN) { + //! 对端发送了 close_notify + errno = EPIPE; + return -1; + } + + if (ssl_error == SSL_ERROR_SYSCALL) { + if (errno == 0) { + errno = EPIPE; + return -1; + } + return -1; + } + + //! 其他 SSL 错误 + LogWarn("SSL_write error: %d", ssl_error); + errno = ECONNRESET; + return -1; +} + +void BufferedSslFd::handleSslRenegotiation(int ssl_error, bool is_read_op) +{ + if (is_read_op && ssl_error == SSL_ERROR_WANT_WRITE) { + //! SSL_read 需要 fd 可写(renegotiation) + ssl_read_wants_write_ = true; + //! 临时启用写事件,即使 send buffer 为空 + //! 注意:onWriteCallback 在 send buffer 为空时会检查此标记, + //! 如果为 true 则不清除标记、不关闭写事件,而是继续驱动 SSL_read + } else if (!is_read_op && ssl_error == SSL_ERROR_WANT_READ) { + //! SSL_write 需要 fd 可读(renegotiation) + ssl_write_wants_read_ = true; + //! 读事件已经持续启用,无需额外操作 + } +} + +} +} diff --git a/modules/network/buffered_ssl_fd.h b/modules/network/buffered_ssl_fd.h new file mode 100644 index 00000000..006178fa --- /dev/null +++ b/modules/network/buffered_ssl_fd.h @@ -0,0 +1,63 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_BUFFERED_SSL_FD_H_20260616 +#define TBOX_NETWORK_BUFFERED_SSL_FD_H_20260616 + +#include + +#include "buffered_fd.h" + +namespace tbox { +namespace network { + +//! 基于 SSL 的 BufferedFd,用于 TLS 加密通信 +//! 继承自 BufferedFd,覆写 doRead()/doWrite() 使用 SSL_read/SSL_write +//! SSL 握手由外部(TcpTlsConnector/TcpTlsAcceptor)负责,本类只处理已建立 SSL 连接的 I/O +class BufferedSslFd : public BufferedFd { + public: + explicit BufferedSslFd(event::Loop *wp_loop); + virtual ~BufferedSslFd(); + + NONCOPYABLE(BufferedSslFd); + IMMOVABLE(BufferedSslFd); + + //! 初始化,传入已完成握手的 SSL 对象 + //! 注意:SSL 对象的生命期由本对象管理,析构时会调用 SSL_free() + bool initialize(Fd fd, SSL *ssl, short events = kReadWrite); + + protected: + //! 覆写底层 I/O 方法 + virtual ssize_t doRead(void *buffer, size_t size) override; + virtual ssize_t doWrite(const void *data, size_t size) override; + + private: + //! 处理 SSL 读写过程中的 WANT_READ/WANT_WRITE(renegotiation) + void handleSslRenegotiation(int ssl_error, bool is_read_op); + + SSL *ssl_ = nullptr; + + //! renegotiation 状态标记 + bool ssl_read_wants_write_ = false; //!< SSL_read 返回了 WANT_WRITE + bool ssl_write_wants_read_ = false; //!< SSL_write 返回了 WANT_READ +}; + +} +} +#endif //TBOX_NETWORK_BUFFERED_SSL_FD_H_20260616 diff --git a/modules/network/tcp_acceptor.cpp b/modules/network/tcp_acceptor.cpp index f41c1db5..403b9b11 100644 --- a/modules/network/tcp_acceptor.cpp +++ b/modules/network/tcp_acceptor.cpp @@ -29,8 +29,6 @@ #include #include -#include "tcp_connection.h" - #undef MODULE_ID #define MODULE_ID "tbox.tcp" @@ -162,15 +160,8 @@ void TcpAcceptor::onClientConnected() SockAddr peer_addr(addr, addr_len); LogInfo("%s accepted new connection: %s", bind_addr_.toString().c_str(), peer_addr.toString().c_str()); - if (new_conn_cb_) { - auto sp_connection = new TcpConnection(wp_loop_, peer_sock, peer_addr); - sp_connection->enable(); - ++cb_level_; - new_conn_cb_(sp_connection); - --cb_level_; - } else { - LogWarn("%s need connect cb", bind_addr_.toString().c_str()); - } + //! 调用子类方法处理新连接 + onClientAccepted(peer_sock, peer_addr); } } diff --git a/modules/network/tcp_acceptor.h b/modules/network/tcp_acceptor.h index 7a398de3..05b3083d 100644 --- a/modules/network/tcp_acceptor.h +++ b/modules/network/tcp_acceptor.h @@ -56,10 +56,19 @@ class TcpAcceptor { virtual SocketFd createSocket(SockAddr::Type addr_type); virtual int bindAddress(SocketFd sock_fd, const SockAddr &bind_addr); + //! 子类覆写:创建对应类型的 TcpConnection + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) = 0; + + //! 子类覆写:接受新连接后的处理 + //! TcpRawAcceptor: 立即 enable + 触发 new_conn_cb_ + //! TcpTlsAcceptor: 开始 SSL 握手,握手成功后才触发 new_conn_cb_ + virtual void onClientAccepted(SocketFd fd, const SockAddr &peer_addr) = 0; + void onSocketRead(short events); //! 处理新的连接请求 void onClientConnected(); - private: + protected: event::Loop *wp_loop_ = nullptr; SockAddr bind_addr_; @@ -73,5 +82,4 @@ class TcpAcceptor { } } - #endif //TBOX_NETWORK_TCP_ACCEPTOR_20180114 diff --git a/modules/network/tcp_client.cpp b/modules/network/tcp_client.cpp index 07d0a190..1b5693ae 100644 --- a/modules/network/tcp_client.cpp +++ b/modules/network/tcp_client.cpp @@ -26,6 +26,9 @@ #include "tcp_connector.h" #include "tcp_connection.h" +#include "tcp_factory.h" +#include "tcp_raw_factory.h" +#include "tcp_tls_factory.h" #undef MODULE_ID #define MODULE_ID "tbox.tcp" @@ -45,7 +48,8 @@ struct TcpClient::Data { ByteStream *wp_receiver = nullptr; bool reconnect_enabled = true; - TcpConnector *sp_connector = nullptr; + TcpFactory *sp_factory = nullptr; + TcpConnector *sp_connector = nullptr; TcpConnection *sp_connection = nullptr; int cb_level = 0; @@ -57,7 +61,8 @@ TcpClient::TcpClient(event::Loop *wp_loop) : TBOX_ASSERT(d_ != nullptr); d_->wp_loop = wp_loop; - d_->sp_connector = new TcpConnector(wp_loop); + d_->sp_factory = new TcpRawFactory; + d_->sp_connector = d_->sp_factory->createConnector(wp_loop); } TcpClient::~TcpClient() @@ -68,10 +73,30 @@ TcpClient::~TcpClient() CHECK_DELETE_RESET_OBJ(d_->sp_connection); CHECK_DELETE_RESET_OBJ(d_->sp_connector); + CHECK_DELETE_RESET_OBJ(d_->sp_factory); delete d_; } +void TcpClient::setTlsConfig(const TlsConfig &config) +{ + if (d_->state != State::kNone) { + LogWarn("cannot set TLS config after initialization"); + return; + } + + if (!config.isValid()) { + LogWarn("invalid TLS config"); + return; + } + + //! 替换 factory 和 connector + CHECK_DELETE_RESET_OBJ(d_->sp_connector); + CHECK_DELETE_RESET_OBJ(d_->sp_factory); + d_->sp_factory = new TcpTlsFactory(config); + d_->sp_connector = d_->sp_factory->createConnector(d_->wp_loop); +} + bool TcpClient::initialize(const SockAddr &server_addr) { if (d_->state != State::kNone) { diff --git a/modules/network/tcp_client.h b/modules/network/tcp_client.h index 9cbcca1c..44d352de 100644 --- a/modules/network/tcp_client.h +++ b/modules/network/tcp_client.h @@ -27,6 +27,7 @@ #include "byte_stream.h" #include "sockaddr.h" +#include "tls_config.h" #include #include @@ -36,6 +37,7 @@ namespace network { class TcpConnector; class TcpConnection; +class TcpFactory; class TcpClient : public ByteStream { public: @@ -64,6 +66,9 @@ class TcpClient : public ByteStream { void setAutoReconnect(bool enable); void setReconnectDelayCalcFunc(const ReconnectDelayCalc &func); + //! 设置 TLS 配置(必须在 initialize() 之前调用) + void setTlsConfig(const TlsConfig &config); + bool start(); //!< 开始连接服务端 void stop(); //!< 如果没有连接则成,则停止连接;否则断开连接 diff --git a/modules/network/tcp_connection.cpp b/modules/network/tcp_connection.cpp index 8cdb1e03..d481872b 100644 --- a/modules/network/tcp_connection.cpp +++ b/modules/network/tcp_connection.cpp @@ -32,14 +32,16 @@ using namespace std::placeholders; TcpConnection::TcpConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) : wp_loop_(wp_loop), - sp_buffered_fd_(new BufferedFd(wp_loop)), + sp_buffered_fd_(nullptr), peer_addr_(peer_addr) { - sp_buffered_fd_->initialize(fd); + //! sp_buffered_fd_ 由子类在构造函数中创建,然后调用 setupBufferedFd() +} + +void TcpConnection::setupBufferedFd() +{ sp_buffered_fd_->setReadZeroCallback(std::bind(&TcpConnection::onSocketClosed, this)); sp_buffered_fd_->setReadErrorCallback(std::bind(&TcpConnection::onReadError, this, _1)); - - sp_buffered_fd_->enable(); } TcpConnection::~TcpConnection() @@ -62,17 +64,7 @@ bool TcpConnection::disconnect() if (sp_buffered_fd_ == nullptr) return false; - sp_buffered_fd_->disable(); - - BufferedFd *tmp = nullptr; - std::swap(tmp, sp_buffered_fd_); - - wp_loop_->runNext( - [tmp] { CHECK_DELETE_OBJ(tmp); }, - "TcpConnection::disconnect, delete tmp" - ); - - return true; + return doDisconnect(); } bool TcpConnection::shutdown(int howto) @@ -81,8 +73,7 @@ bool TcpConnection::shutdown(int howto) if (sp_buffered_fd_ == nullptr) return false; - SocketFd socket_fd(sp_buffered_fd_->fd()); - return socket_fd.shutdown(howto) == 0; + return doShutdown(howto); } SocketFd TcpConnection::socketFd() const diff --git a/modules/network/tcp_connection.h b/modules/network/tcp_connection.h index 5c290a53..6e5d7db9 100644 --- a/modules/network/tcp_connection.h +++ b/modules/network/tcp_connection.h @@ -3,7 +3,7 @@ * // M A K E / \ * // C++ DEV / \ * // E A S Y / \/ \ - * ++ ----------. \/\ . +6 * ++ ----------. \/\ . * \\ \ \ /\ / * \\ \ \ / * \\ \ \ / @@ -33,6 +33,7 @@ namespace network { class TcpConnection : public ByteStream { friend class TcpAcceptor; friend class TcpConnector; + friend class TcpFactory; public: virtual ~TcpConnection(); @@ -43,7 +44,7 @@ class TcpConnection : public ByteStream { public: using DisconnectedCallback = std::function; void setDisconnectedCallback(const DisconnectedCallback &cb) { disconnected_cb_ = cb; } - bool disconnect(); //! 主动断开 + bool disconnect(); bool shutdown(int howto); SockAddr peerAddr() const { return peer_addr_; } @@ -56,6 +57,10 @@ class TcpConnection : public ByteStream { void setContext(void *context, ContextDeleter &&deleter = nullptr); void* getContext() const { return sp_context_; } + //! 启用连接(开始 I/O 事件驱动) + //! 由 TcpAcceptor/TcpConnector 在创建后调用 + void enable(); + public: //! 实现ByteStream的接口 virtual void setReceiveCallback(const ReceiveCallback &cb, size_t threshold) override; @@ -66,14 +71,20 @@ class TcpConnection : public ByteStream { virtual Buffer* getReceiveBuffer() override; protected: - void onSocketClosed(); - void onReadError(int errnum); - - private: + //! 基类构造函数,子类需在构造后自行创建 sp_buffered_fd_ 并调用 setupBufferedFd() explicit TcpConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr); - void enable(); - private: + //! 初始化 BufferedFd 的回调(在子类创建 sp_buffered_fd_ 后调用) + void setupBufferedFd(); + + //! 断开连接的具体操作 + //! 子类可覆写此方法以在断开前执行额外操作(如 SSL_shutdown) + virtual bool doDisconnect() = 0; + + //! 子类可覆写此方法以在 shutdown 时执行额外操作 + virtual bool doShutdown(int howto) = 0; + + protected: event::Loop *wp_loop_; BufferedFd *sp_buffered_fd_; SockAddr peer_addr_; @@ -83,9 +94,12 @@ class TcpConnection : public ByteStream { ContextDeleter context_deleter_; int cb_level_ = 0; + + private: + void onSocketClosed(); + void onReadError(int errnum); }; } } - #endif //TBOX_NETWORK_TCP_CONNECTION_H_20180113 diff --git a/modules/network/tcp_connector.cpp b/modules/network/tcp_connector.cpp index 47baf61f..6cb25186 100644 --- a/modules/network/tcp_connector.cpp +++ b/modules/network/tcp_connector.cpp @@ -25,8 +25,6 @@ #include #include -#include "tcp_connection.h" - #undef MODULE_ID #define MODULE_ID "tbox.tcp" @@ -286,14 +284,9 @@ void TcpConnector::onSocketWritable() state_ = State::kInited; LogInfo("connect to %s success", server_addr_.toString().c_str()); - if (connected_cb_) { - auto sp_conn = new TcpConnection(wp_loop_, conn_sock_fd, server_addr_); - sp_conn->enable(); - ++cb_level_; - connected_cb_(sp_conn); - --cb_level_; - } else - LogWarn("connected callback is not set"); + + //! 调用子类方法处理连接成功 + onTcpConnected(conn_sock_fd, server_addr_); } else { //! 连接失败 LogNotice("connect fail, errno:%d, %s", sock_errno, strerror(sock_errno)); diff --git a/modules/network/tcp_connector.h b/modules/network/tcp_connector.h index c303bf2c..43f7bc8e 100644 --- a/modules/network/tcp_connector.h +++ b/modules/network/tcp_connector.h @@ -74,6 +74,15 @@ class TcpConnector { virtual SocketFd createSocket(SockAddr::Type addr_type) const; virtual int connect(SocketFd sock_fd, const SockAddr &addr) const; + //! 子类覆写:创建对应类型的 TcpConnection + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) = 0; + + //! 子类覆写:TCP 连接成功后的处理 + //! TcpRawConnector: 立即 enable + 触发 connected_cb_ + //! TcpTlsConnector: 开始 SSL 握手,握手成功后才触发 connected_cb_ + virtual void onTcpConnected(SocketFd fd, const SockAddr &peer_addr) = 0; + void checkSettingAndTryEnterIdleState(); void enterConnectingState(); //!< 进入连接状态的操作 @@ -85,7 +94,7 @@ class TcpConnector { void onSocketWritable(); //!< 当连接成功时的处理 void onDelayTimeout(); //!< 当等待延时到期后的处理 - private: + protected: event::Loop *wp_loop_ = nullptr; State state_ = State::kNone; //! 当前状态 @@ -108,5 +117,4 @@ class TcpConnector { } } - #endif //TBOX_NETWORK_TCP_CONNECTOR_H_20180115 diff --git a/modules/network/tcp_factory.h b/modules/network/tcp_factory.h new file mode 100644 index 00000000..8c2ed65c --- /dev/null +++ b/modules/network/tcp_factory.h @@ -0,0 +1,43 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_FACTORY_H_20260616 +#define TBOX_NETWORK_TCP_FACTORY_H_20260616 + +#include + +namespace tbox { +namespace network { + +class TcpConnector; +class TcpAcceptor; + +//! TCP 抽象工厂,用于创建 Connector 和 Acceptor +//! TcpServer 和 TcpClient 通过工厂决定使用 raw 还是 TLS 传输 +class TcpFactory { + public: + virtual ~TcpFactory() {} + + virtual TcpConnector* createConnector(event::Loop *wp_loop) = 0; + virtual TcpAcceptor* createAcceptor(event::Loop *wp_loop) = 0; +}; + +} +} +#endif //TBOX_NETWORK_TCP_FACTORY_H_20260616 diff --git a/modules/network/tcp_raw_acceptor.cpp b/modules/network/tcp_raw_acceptor.cpp new file mode 100644 index 00000000..54e275f8 --- /dev/null +++ b/modules/network/tcp_raw_acceptor.cpp @@ -0,0 +1,58 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_raw_acceptor.h" +#include "tcp_raw_connection.h" + +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp" + +namespace tbox { +namespace network { + +TcpRawAcceptor::TcpRawAcceptor(event::Loop *wp_loop) : + TcpAcceptor(wp_loop) +{ } + +TcpConnection* TcpRawAcceptor::createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) +{ + return new TcpRawConnection(wp_loop, fd, peer_addr); +} + +void TcpRawAcceptor::onClientAccepted(SocketFd fd, const SockAddr &peer_addr) +{ + //! accept 后立即创建 TcpRawConnection 并触发回调 + if (new_conn_cb_) { + auto sp_connection = createConnection(wp_loop_, fd, peer_addr); + sp_connection->enable(); + ++cb_level_; + new_conn_cb_(sp_connection); + --cb_level_; + } else { + LogWarn("%s need connect cb", bind_addr_.toString().c_str()); + //! 没有回调,需要关闭 fd + fd.close(); + } +} + +} +} diff --git a/modules/network/tcp_raw_acceptor.h b/modules/network/tcp_raw_acceptor.h new file mode 100644 index 00000000..29e00da0 --- /dev/null +++ b/modules/network/tcp_raw_acceptor.h @@ -0,0 +1,42 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_RAW_ACCEPTOR_H_20260616 +#define TBOX_NETWORK_TCP_RAW_ACCEPTOR_H_20260616 + +#include "tcp_acceptor.h" + +namespace tbox { +namespace network { + +//! 原始 TCP 接收器(无 TLS) +//! accept 后立即创建 TcpRawConnection 并触发 new_conn callback +class TcpRawAcceptor : public TcpAcceptor { + public: + explicit TcpRawAcceptor(event::Loop *wp_loop); + + protected: + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) override; + virtual void onClientAccepted(SocketFd fd, const SockAddr &peer_addr) override; +}; + +} +} +#endif //TBOX_NETWORK_TCP_RAW_ACCEPTOR_H_20260616 diff --git a/modules/network/tcp_raw_connection.cpp b/modules/network/tcp_raw_connection.cpp new file mode 100644 index 00000000..53a8c174 --- /dev/null +++ b/modules/network/tcp_raw_connection.cpp @@ -0,0 +1,60 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_raw_connection.h" + +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp" + +namespace tbox { +namespace network { + +TcpRawConnection::TcpRawConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) : + TcpConnection(wp_loop, fd, peer_addr) +{ + sp_buffered_fd_ = new BufferedFd(wp_loop_); + sp_buffered_fd_->initialize(fd); + setupBufferedFd(); +} + +bool TcpRawConnection::doDisconnect() +{ + sp_buffered_fd_->disable(); + + BufferedFd *tmp = nullptr; + std::swap(tmp, sp_buffered_fd_); + + wp_loop_->runNext( + [tmp] { CHECK_DELETE_OBJ(tmp); }, + "TcpRawConnection::doDisconnect, delete tmp" + ); + + return true; +} + +bool TcpRawConnection::doShutdown(int howto) +{ + SocketFd socket_fd(sp_buffered_fd_->fd()); + return socket_fd.shutdown(howto) == 0; +} + +} +} diff --git a/modules/network/tcp_raw_connection.h b/modules/network/tcp_raw_connection.h new file mode 100644 index 00000000..b5b221d8 --- /dev/null +++ b/modules/network/tcp_raw_connection.h @@ -0,0 +1,41 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_RAW_CONNECTION_H_20260616 +#define TBOX_NETWORK_TCP_RAW_CONNECTION_H_20260616 + +#include "tcp_connection.h" + +namespace tbox { +namespace network { + +//! 原始 TCP 连接(无 TLS) +//! 使用 BufferedFd 进行普通 socket I/O +class TcpRawConnection : public TcpConnection { + public: + explicit TcpRawConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr); + + protected: + virtual bool doDisconnect() override; + virtual bool doShutdown(int howto) override; +}; + +} +} +#endif //TBOX_NETWORK_TCP_RAW_CONNECTION_H_20260616 diff --git a/modules/network/tcp_raw_connector.cpp b/modules/network/tcp_raw_connector.cpp new file mode 100644 index 00000000..201059ca --- /dev/null +++ b/modules/network/tcp_raw_connector.cpp @@ -0,0 +1,58 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_raw_connector.h" +#include "tcp_raw_connection.h" + +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp" + +namespace tbox { +namespace network { + +TcpRawConnector::TcpRawConnector(event::Loop *wp_loop) : + TcpConnector(wp_loop) +{ } + +TcpConnection* TcpRawConnector::createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) +{ + return new TcpRawConnection(wp_loop, fd, peer_addr); +} + +void TcpRawConnector::onTcpConnected(SocketFd fd, const SockAddr &peer_addr) +{ + //! TCP 连接成功后,立即创建 TcpRawConnection 并触发回调 + if (connected_cb_) { + auto sp_conn = createConnection(wp_loop_, fd, peer_addr); + sp_conn->enable(); + ++cb_level_; + connected_cb_(sp_conn); + --cb_level_; + } else { + LogWarn("connected callback is not set"); + //! 没有回调,需要关闭 fd + fd.close(); + } +} + +} +} diff --git a/modules/network/tcp_raw_connector.h b/modules/network/tcp_raw_connector.h new file mode 100644 index 00000000..d5cb9d8a --- /dev/null +++ b/modules/network/tcp_raw_connector.h @@ -0,0 +1,42 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_RAW_CONNECTOR_H_20260616 +#define TBOX_NETWORK_TCP_RAW_CONNECTOR_H_20260616 + +#include "tcp_connector.h" + +namespace tbox { +namespace network { + +//! 原始 TCP 连接器(无 TLS) +//! TCP 连接成功后立即创建 TcpRawConnection 并触发 connected callback +class TcpRawConnector : public TcpConnector { + public: + explicit TcpRawConnector(event::Loop *wp_loop); + + protected: + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) override; + virtual void onTcpConnected(SocketFd fd, const SockAddr &peer_addr) override; +}; + +} +} +#endif //TBOX_NETWORK_TCP_RAW_CONNECTOR_H_20260616 diff --git a/modules/network/tcp_raw_factory.cpp b/modules/network/tcp_raw_factory.cpp new file mode 100644 index 00000000..d2f74f81 --- /dev/null +++ b/modules/network/tcp_raw_factory.cpp @@ -0,0 +1,38 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_raw_factory.h" +#include "tcp_raw_connector.h" +#include "tcp_raw_acceptor.h" + +namespace tbox { +namespace network { + +TcpConnector* TcpRawFactory::createConnector(event::Loop *wp_loop) +{ + return new TcpRawConnector(wp_loop); +} + +TcpAcceptor* TcpRawFactory::createAcceptor(event::Loop *wp_loop) +{ + return new TcpRawAcceptor(wp_loop); +} + +} +} diff --git a/modules/network/tcp_raw_factory.h b/modules/network/tcp_raw_factory.h new file mode 100644 index 00000000..5d11d32a --- /dev/null +++ b/modules/network/tcp_raw_factory.h @@ -0,0 +1,38 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_RAW_FACTORY_H_20260616 +#define TBOX_NETWORK_TCP_RAW_FACTORY_H_20260616 + +#include "tcp_factory.h" + +namespace tbox { +namespace network { + +//! 原始 TCP 工厂(无 TLS) +//! 创建 TcpRawConnector 和 TcpRawAcceptor +class TcpRawFactory : public TcpFactory { + public: + virtual TcpConnector* createConnector(event::Loop *wp_loop) override; + virtual TcpAcceptor* createAcceptor(event::Loop *wp_loop) override; +}; + +} +} +#endif //TBOX_NETWORK_TCP_RAW_FACTORY_H_20260616 diff --git a/modules/network/tcp_server.cpp b/modules/network/tcp_server.cpp index b8704419..b44675fd 100644 --- a/modules/network/tcp_server.cpp +++ b/modules/network/tcp_server.cpp @@ -28,6 +28,9 @@ #include "tcp_acceptor.h" #include "tcp_connection.h" +#include "tcp_factory.h" +#include "tcp_raw_factory.h" +#include "tcp_tls_factory.h" #undef MODULE_ID #define MODULE_ID "tbox.tcp" @@ -49,6 +52,7 @@ struct TcpServer::Data { size_t receive_threshold = 0; SendCompleteCallback send_complete_cb; + TcpFactory *sp_factory = nullptr; TcpAcceptor *sp_acceptor = nullptr; TcpConns conns; //!< TcpConnection 容器 @@ -62,7 +66,8 @@ TcpServer::TcpServer(event::Loop *wp_loop) : TBOX_ASSERT(d_ != nullptr); d_->wp_loop = wp_loop; - d_->sp_acceptor = new TcpAcceptor(wp_loop); + d_->sp_factory = new TcpRawFactory; + d_->sp_acceptor = d_->sp_factory->createAcceptor(wp_loop); } TcpServer::~TcpServer() @@ -71,10 +76,30 @@ TcpServer::~TcpServer() cleanup(); CHECK_DELETE_RESET_OBJ(d_->sp_acceptor); + CHECK_DELETE_RESET_OBJ(d_->sp_factory); delete d_; } +void TcpServer::setTlsConfig(const TlsConfig &config) +{ + if (d_->state != State::kNone) { + LogWarn("cannot set TLS config after initialization"); + return; + } + + if (!config.isValid()) { + LogWarn("invalid TLS config"); + return; + } + + //! 替换 factory 和 acceptor + CHECK_DELETE_RESET_OBJ(d_->sp_acceptor); + CHECK_DELETE_RESET_OBJ(d_->sp_factory); + d_->sp_factory = new TcpTlsFactory(config); + d_->sp_acceptor = d_->sp_factory->createAcceptor(d_->wp_loop); +} + bool TcpServer::initialize(const SockAddr &bind_addr, int listen_backlog) { if (d_->state != State::kNone) diff --git a/modules/network/tcp_server.h b/modules/network/tcp_server.h index 8a7c1216..de732863 100644 --- a/modules/network/tcp_server.h +++ b/modules/network/tcp_server.h @@ -26,6 +26,7 @@ #include #include "sockaddr.h" +#include "tls_config.h" namespace tbox { namespace network { @@ -34,6 +35,7 @@ using Buffer = util::Buffer; class TcpAcceptor; class TcpConnection; +class TcpFactory; class TcpServer { public: @@ -55,6 +57,9 @@ class TcpServer { //! 设置绑定地址与backlog bool initialize(const SockAddr &bind_addr, int listen_backlog); + //! 设置 TLS 配置(必须在 initialize() 之前调用) + void setTlsConfig(const TlsConfig &config); + using ConnectedCallback = std::function; using DisconnectedCallback = std::function; using ReceiveCallback = std::function; diff --git a/modules/network/tcp_tls_acceptor.cpp b/modules/network/tcp_tls_acceptor.cpp new file mode 100644 index 00000000..cfb5c5e0 --- /dev/null +++ b/modules/network/tcp_tls_acceptor.cpp @@ -0,0 +1,192 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_tls_acceptor.h" +#include "tcp_tls_connection.h" + +#include +#include +#include +#include +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp" + +namespace tbox { +namespace network { + +TcpTlsAcceptor::TcpTlsAcceptor(event::Loop *wp_loop, SSL_CTX *ssl_ctx, const TlsConfig &tls_config) : + TcpAcceptor(wp_loop), + ssl_ctx_(ssl_ctx), + tls_config_(tls_config) +{ } + +TcpTlsAcceptor::~TcpTlsAcceptor() +{ + //! 如果握手还在进行中,需要清理 + if (sp_handshake_ev_ != nullptr) { + sp_handshake_ev_->disable(); + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + } + + if (handshake_ssl_ != nullptr) { + SSL_free(handshake_ssl_); + handshake_ssl_ = nullptr; + } + + handshake_fd_.close(); +} + +TcpConnection* TcpTlsAcceptor::createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) +{ + //! 注意:此方法不直接使用,由 onSslHandshakeSuccess 创建 TcpTlsConnection + return nullptr; +} + +void TcpTlsAcceptor::onClientAccepted(SocketFd fd, const SockAddr &peer_addr) +{ + //! accept 后不立即创建 Connection,而是开始 SSL 握手 + startSslHandshake(fd, peer_addr); +} + +void TcpTlsAcceptor::startSslHandshake(SocketFd fd, const SockAddr &peer_addr) +{ + //! 创建 SSL 对象(server 端使用 SSL_accept) + handshake_ssl_ = SSL_new(ssl_ctx_); + if (handshake_ssl_ == nullptr) { + LogErr("SSL_new fail"); + fd.close(); + return; + } + + //! 将 fd 绑定到 SSL + SSL_set_fd(handshake_ssl_, fd.get()); + + //! 设置 SSL 为 server 模式 + SSL_set_accept_state(handshake_ssl_); + + //! 保存握手需要的参数 + handshake_fd_ = fd; + handshake_peer_addr_ = peer_addr; + + //! 开始 SSL_accept + ERR_clear_error(); + int ret = SSL_accept(handshake_ssl_); + + if (ret == 1) { + //! SSL 握手立即完成 + onSslHandshakeSuccess(); + return; + } + + int ssl_error = SSL_get_error(handshake_ssl_, ret); + if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) { + //! 正常的异步握手过程 + short events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; + + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + sp_handshake_ev_ = wp_loop_->newFdEvent("TcpTlsAcceptor::sp_handshake_ev_"); + sp_handshake_ev_->initialize(fd.get(), events, event::Event::Mode::kOneshot); + sp_handshake_ev_->setCallback(std::bind(&TcpTlsAcceptor::onSslHandshakeEvent, this, std::placeholders::_1)); + sp_handshake_ev_->enable(); + + LogDbg("SSL accept in progress, waiting for %s", (ssl_error == SSL_ERROR_WANT_READ) ? "READ" : "WRITE"); + } else { + //! SSL 握手失败 + LogErr("SSL_accept fail, error:%d", ssl_error); + onSslHandshakeFail(); + } +} + +void TcpTlsAcceptor::onSslHandshakeEvent(short events) +{ + //! 继续 SSL_accept + ERR_clear_error(); + int ret = SSL_accept(handshake_ssl_); + + if (ret == 1) { + //! 握手成功 + onSslHandshakeSuccess(); + return; + } + + int ssl_error = SSL_get_error(handshake_ssl_, ret); + if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) { + //! 需要继续等待 + short next_events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; + + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + sp_handshake_ev_ = wp_loop_->newFdEvent("TcpTlsAcceptor::sp_handshake_ev_"); + sp_handshake_ev_->initialize(handshake_fd_.get(), next_events, event::Event::Mode::kOneshot); + sp_handshake_ev_->setCallback(std::bind(&TcpTlsAcceptor::onSslHandshakeEvent, this, std::placeholders::_1)); + sp_handshake_ev_->enable(); + } else { + //! 握手失败 + LogErr("SSL accept fail, error:%d", ssl_error); + onSslHandshakeFail(); + } +} + +void TcpTlsAcceptor::onSslHandshakeSuccess() +{ + RECORD_SCOPE(); + + //! 清理握手相关的 FdEvent + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + + //! 将 SSL 和 fd 从握手状态转移到 TcpTlsConnection + SSL *ssl = handshake_ssl_; + handshake_ssl_ = nullptr; + SocketFd fd = handshake_fd_; + handshake_fd_.reset(); + SockAddr peer_addr = handshake_peer_addr_; + + LogInfo("TLS handshake from %s success", peer_addr.toString().c_str()); + + //! 创建 TcpTlsConnection 并触发回调 + if (new_conn_cb_) { + auto sp_connection = new TcpTlsConnection(wp_loop_, fd, peer_addr, ssl); + sp_connection->enable(); + ++cb_level_; + new_conn_cb_(sp_connection); + --cb_level_; + } else { + LogWarn("%s need connect cb", bind_addr_.toString().c_str()); + SSL_free(ssl); + fd.close(); + } +} + +void TcpTlsAcceptor::onSslHandshakeFail() +{ + //! 清理握手相关的资源 + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + + SSL_free(handshake_ssl_); + handshake_ssl_ = nullptr; + handshake_fd_.close(); + + //! 握手失败不影响其他连接,仅关闭当前 fd + LogNotice("TLS handshake fail, close connection"); +} + +} +} diff --git a/modules/network/tcp_tls_acceptor.h b/modules/network/tcp_tls_acceptor.h new file mode 100644 index 00000000..cdb25748 --- /dev/null +++ b/modules/network/tcp_tls_acceptor.h @@ -0,0 +1,68 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_TLS_ACCEPTOR_H_20260616 +#define TBOX_NETWORK_TCP_TLS_ACCEPTOR_H_20260616 + +#include + +#include "tcp_acceptor.h" +#include "tls_config.h" +#include "socket_fd.h" + +namespace tbox { +namespace network { + +//! TLS 接收器 +//! accept 后进行 SSL 握手,握手成功才创建 TcpTlsConnection 并触发 new_conn callback +//! 握手失败则关闭 fd,不影响其他连接 +class TcpTlsAcceptor : public TcpAcceptor { + public: + explicit TcpTlsAcceptor(event::Loop *wp_loop, SSL_CTX *ssl_ctx, const TlsConfig &tls_config); + ~TcpTlsAcceptor(); + + protected: + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) override; + virtual void onClientAccepted(SocketFd fd, const SockAddr &peer_addr) override; + + private: + //! 开始 SSL 握手 + void startSslHandshake(SocketFd fd, const SockAddr &peer_addr); + //! SSL 握手事件处理 + void onSslHandshakeEvent(short events); + //! SSL 握手成功 + void onSslHandshakeSuccess(); + //! SSL 握手失败 + void onSslHandshakeFail(); + + private: + SSL_CTX *ssl_ctx_ = nullptr; + TlsConfig tls_config_; + + //! 握手期间的临时状态 + SSL *handshake_ssl_ = nullptr; + SocketFd handshake_fd_; + SockAddr handshake_peer_addr_; + event::FdEvent *sp_handshake_ev_ = nullptr; +}; + +} +} +#endif //TBOX_NETWORK_TCP_TLS_ACCEPTOR_H_20260616 diff --git a/modules/network/tcp_tls_connection.cpp b/modules/network/tcp_tls_connection.cpp new file mode 100644 index 00000000..7712200f --- /dev/null +++ b/modules/network/tcp_tls_connection.cpp @@ -0,0 +1,83 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S E / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_tls_connection.h" + +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp" + +namespace tbox { +namespace network { + +TcpTlsConnection::TcpTlsConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr, SSL *ssl) : + TcpConnection(wp_loop, fd, peer_addr), + ssl_(ssl) +{ + //! 创建 BufferedSslFd 并初始化 + auto *ssl_fd = new BufferedSslFd(wp_loop_); + ssl_fd->initialize(fd, ssl_); + sp_buffered_fd_ = ssl_fd; + setupBufferedFd(); + + //! SSL 对象已由 BufferedSslFd 接管,本类不再单独管理 + ssl_ = nullptr; +} + +bool TcpTlsConnection::doDisconnect() +{ + //! 先尝试 SSL_shutdown(发送 close_notify) + if (sp_buffered_fd_ != nullptr) { + //! 获取 BufferedSslFd 中的 SSL 对象 + //! 通过 fd() 可以获取底层 fd,但我们需要 SSL 对象来 shutdown + //! BufferedSslFd 的析构函数会处理 SSL_shutdown 和 SSL_free + //! 所以这里只需要 disable 和 delete BufferedSslFd 即可 + //! 但为了优雅关闭,先调用一次 SSL_shutdown + //! 注意:由于 SSL 已在 BufferedSslFd 中,我们需要另一种方式 + + //! 禁用事件驱动,停止 I/O + sp_buffered_fd_->disable(); + + BufferedFd *tmp = nullptr; + std::swap(tmp, sp_buffered_fd_); + + //! 延后删除,让 SSL_shutdown 在析构中完成 + wp_loop_->runNext( + [tmp] { CHECK_DELETE_OBJ(tmp); }, + "TcpTlsConnection::doDisconnect, delete tmp" + ); + } + + return true; +} + +bool TcpTlsConnection::doShutdown(int howto) +{ + //! TLS 不支持半关闭的 shutdown(SSL 层面) + //! 只能对底层 socket 执行 shutdown + if (sp_buffered_fd_ != nullptr) { + SocketFd socket_fd(sp_buffered_fd_->fd()); + return socket_fd.shutdown(howto) == 0; + } + return false; +} + +} +} diff --git a/modules/network/tcp_tls_connection.h b/modules/network/tcp_tls_connection.h new file mode 100644 index 00000000..07699931 --- /dev/null +++ b/modules/network/tcp_tls_connection.h @@ -0,0 +1,50 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S E / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_TLS_CONNECTION_H_20260616 +#define TBOX_NETWORK_TCP_TLS_CONNECTION_H_20260616 + +#include + +#include "tcp_connection.h" +#include "buffered_ssl_fd.h" + +namespace tbox { +namespace network { + +//! TLS 加密 TCP 连接 +//! 使用 BufferedSslFd 进行 SSL I/O +//! SSL 握手由 TcpTlsConnector/TcpTlsAcceptor 在创建本对象之前完成 +class TcpTlsConnection : public TcpConnection { + public: + //! 构造函数,传入已完成握手的 SSL 对象 + //! 注意:本对象接管 SSL 的生命周期,析构时会 SSL_free() + explicit TcpTlsConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr, SSL *ssl); + + protected: + virtual bool doDisconnect() override; + virtual bool doShutdown(int howto) override; + + private: + SSL *ssl_ = nullptr; +}; + +} +} +#endif //TBOX_NETWORK_TCP_TLS_CONNECTION_H_20260616 diff --git a/modules/network/tcp_tls_connector.cpp b/modules/network/tcp_tls_connector.cpp new file mode 100644 index 00000000..560c41af --- /dev/null +++ b/modules/network/tcp_tls_connector.cpp @@ -0,0 +1,203 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_tls_connector.h" +#include "tcp_tls_connection.h" + +#include +#include +#include +#include +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp" + +namespace tbox { +namespace network { + +TcpTlsConnector::TcpTlsConnector(event::Loop *wp_loop, SSL_CTX *ssl_ctx, const TlsConfig &tls_config) : + TcpConnector(wp_loop), + ssl_ctx_(ssl_ctx), + tls_config_(tls_config) +{ } + +TcpTlsConnector::~TcpTlsConnector() +{ + //! 如果握手还在进行中,需要清理 + if (sp_handshake_ev_ != nullptr) { + sp_handshake_ev_->disable(); + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + } + + if (handshake_ssl_ != nullptr) { + SSL_free(handshake_ssl_); + handshake_ssl_ = nullptr; + } + + handshake_fd_.close(); +} + +TcpConnection* TcpTlsConnector::createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) +{ + //! 注意:此方法只在 SSL 握手成功后由 onSslHandshakeSuccess 调用 + //! 此时 handshake_ssl_ 已经是完全建立的 SSL 连接 + //! 但 handshake_ssl_ 已在 onSslHandshakeSuccess 中置 nullptr,需要通过参数传入 + //! 实际上不直接使用此方法,而是在 onSslHandshakeSuccess 中直接创建 TcpTlsConnection + return nullptr; //! 不直接使用此方法 +} + +void TcpTlsConnector::onTcpConnected(SocketFd fd, const SockAddr &peer_addr) +{ + //! TCP 连接成功后,不立即创建 Connection,而是开始 SSL 握手 + startSslHandshake(fd, peer_addr); +} + +void TcpTlsConnector::startSslHandshake(SocketFd fd, const SockAddr &peer_addr) +{ + //! 创建 SSL 对象 + handshake_ssl_ = SSL_new(ssl_ctx_); + if (handshake_ssl_ == nullptr) { + LogErr("SSL_new fail"); + fd.close(); + onConnectFail(); + return; + } + + //! 设置 SNI (Server Name Indication) + if (!tls_config_.hostname.empty()) { + SSL_set_tlsext_host_name(handshake_ssl_, tls_config_.hostname.c_str()); + } + + //! 将 fd 绑定到 SSL + SSL_set_fd(handshake_ssl_, fd.get()); + + //! 保存握手需要的参数 + handshake_fd_ = fd; + handshake_peer_addr_ = peer_addr; + + //! 开始 SSL_connect + ERR_clear_error(); + int ret = SSL_connect(handshake_ssl_); + + if (ret == 1) { + //! SSL 握手立即完成(罕见,通常需要多次 WANT_READ/WANT_WRITE) + onSslHandshakeSuccess(); + return; + } + + int ssl_error = SSL_get_error(handshake_ssl_, ret); + if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) { + //! 正常的异步握手过程,需要等待 fd 事件 + short events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; + + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + sp_handshake_ev_ = wp_loop_->newFdEvent("TcpTlsConnector::sp_handshake_ev_"); + sp_handshake_ev_->initialize(fd.get(), events, event::Event::Mode::kOneshot); + sp_handshake_ev_->setCallback(std::bind(&TcpTlsConnector::onSslHandshakeEvent, this, std::placeholders::_1)); + sp_handshake_ev_->enable(); + + LogDbg("SSL handshake in progress, waiting for %s", (ssl_error == SSL_ERROR_WANT_READ) ? "READ" : "WRITE"); + } else { + //! SSL 握手失败 + LogErr("SSL_connect fail, error:%d", ssl_error); + SSL_free(handshake_ssl_); + handshake_ssl_ = nullptr; + handshake_fd_.close(); + onConnectFail(); + } +} + +void TcpTlsConnector::onSslHandshakeEvent(short events) +{ + //! 继续 SSL_connect + ERR_clear_error(); + int ret = SSL_connect(handshake_ssl_); + + if (ret == 1) { + //! 握手成功 + onSslHandshakeSuccess(); + return; + } + + int ssl_error = SSL_get_error(handshake_ssl_, ret); + if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) { + //! 需要继续等待 + short next_events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; + + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + sp_handshake_ev_ = wp_loop_->newFdEvent("TcpTlsConnector::sp_handshake_ev_"); + sp_handshake_ev_->initialize(handshake_fd_.get(), next_events, event::Event::Mode::kOneshot); + sp_handshake_ev_->setCallback(std::bind(&TcpTlsConnector::onSslHandshakeEvent, this, std::placeholders::_1)); + sp_handshake_ev_->enable(); + } else { + //! 握手失败 + LogErr("SSL handshake fail, error:%d", ssl_error); + onSslHandshakeFail(); + } +} + +void TcpTlsConnector::onSslHandshakeSuccess() +{ + RECORD_SCOPE(); + + //! 清理握手相关的 FdEvent + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + + //! 将 SSL 和 fd 从握手状态转移到 TcpTlsConnection + SSL *ssl = handshake_ssl_; + handshake_ssl_ = nullptr; //! 防止析构时重复释放 + SocketFd fd = handshake_fd_; + handshake_fd_.reset(); //! 防止析构时重复关闭 + SockAddr peer_addr = handshake_peer_addr_; + + LogInfo("TLS handshake to %s success", peer_addr.toString().c_str()); + + //! 创建 TcpTlsConnection 并触发回调 + if (connected_cb_) { + auto sp_conn = new TcpTlsConnection(wp_loop_, fd, peer_addr, ssl); + sp_conn->enable(); + ++cb_level_; + connected_cb_(sp_conn); + --cb_level_; + } else { + LogWarn("connected callback is not set"); + //! 没有回调,需要释放 SSL 和关闭 fd + SSL_free(ssl); + fd.close(); + } +} + +void TcpTlsConnector::onSslHandshakeFail() +{ + //! 清理握手相关的资源 + CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + + SSL_free(handshake_ssl_); + handshake_ssl_ = nullptr; + handshake_fd_.close(); + + //! SSL 握手失败视为连接失败,触发重连逻辑 + LogNotice("TLS handshake fail, treat as connection fail"); + onConnectFail(); +} + +} +} diff --git a/modules/network/tcp_tls_connector.h b/modules/network/tcp_tls_connector.h new file mode 100644 index 00000000..480cf191 --- /dev/null +++ b/modules/network/tcp_tls_connector.h @@ -0,0 +1,68 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_TLS_CONNECTOR_H_20260616 +#define TBOX_NETWORK_TCP_TLS_CONNECTOR_H_20260616 + +#include + +#include "tcp_connector.h" +#include "tls_config.h" +#include "socket_fd.h" + +namespace tbox { +namespace network { + +//! TLS 连接器 +//! TCP 连接成功后,先进行 SSL 握手,握手成功后才创建 TcpTlsConnection 并触发 connected callback +//! 握手失败视为连接失败,触发重连逻辑 +class TcpTlsConnector : public TcpConnector { + public: + explicit TcpTlsConnector(event::Loop *wp_loop, SSL_CTX *ssl_ctx, const TlsConfig &tls_config); + ~TcpTlsConnector(); + + protected: + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, + const SockAddr &peer_addr) override; + virtual void onTcpConnected(SocketFd fd, const SockAddr &peer_addr) override; + + private: + //! 开始 SSL 握手 + void startSslHandshake(SocketFd fd, const SockAddr &peer_addr); + //! SSL 握手事件处理 + void onSslHandshakeEvent(short events); + //! SSL 握手成功 + void onSslHandshakeSuccess(); + //! SSL 握手失败 + void onSslHandshakeFail(); + + private: + SSL_CTX *ssl_ctx_ = nullptr; + TlsConfig tls_config_; + + //! 握手期间的临时状态 + SSL *handshake_ssl_ = nullptr; + SocketFd handshake_fd_; + SockAddr handshake_peer_addr_; + event::FdEvent *sp_handshake_ev_ = nullptr; +}; + +} +} +#endif //TBOX_NETWORK_TCP_TLS_CONNECTOR_H_20260616 diff --git a/modules/network/tcp_tls_factory.cpp b/modules/network/tcp_tls_factory.cpp new file mode 100644 index 00000000..f499b194 --- /dev/null +++ b/modules/network/tcp_tls_factory.cpp @@ -0,0 +1,183 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tcp_tls_factory.h" +#include "tcp_tls_connector.h" +#include "tcp_tls_acceptor.h" + +#include +#include + +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp" + +namespace tbox { +namespace network { + +TcpTlsFactory::TcpTlsFactory(const TlsConfig &config) : + tls_config_(config) +{ + //! 创建 client 端 SSL_CTX + client_ssl_ctx_ = createClientSslCtx(); + //! 创建 server 端 SSL_CTX + server_ssl_ctx_ = createServerSslCtx(); +} + +TcpTlsFactory::~TcpTlsFactory() +{ + if (client_ssl_ctx_ != nullptr) + SSL_CTX_free(client_ssl_ctx_); + if (server_ssl_ctx_ != nullptr) + SSL_CTX_free(server_ssl_ctx_); +} + +TcpConnector* TcpTlsFactory::createConnector(event::Loop *wp_loop) +{ + return new TcpTlsConnector(wp_loop, client_ssl_ctx_, tls_config_); +} + +TcpAcceptor* TcpTlsFactory::createAcceptor(event::Loop *wp_loop) +{ + return new TcpTlsAcceptor(wp_loop, server_ssl_ctx_, tls_config_); +} + +SSL_CTX* TcpTlsFactory::createClientSslCtx() const +{ + SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); + if (ctx == nullptr) { + LogErr("SSL_CTX_new(TLS_client_method) fail"); + return nullptr; + } + + //! 设置最低 TLS 版本为 1.2 + SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); + + //! 加载 CA 证书(用于验证 server) + if (!tls_config_.ca_file.empty() || !tls_config_.ca_path.empty()) { + const char *ca_file = tls_config_.ca_file.empty() ? nullptr : tls_config_.ca_file.c_str(); + const char *ca_path = tls_config_.ca_path.empty() ? nullptr : tls_config_.ca_path.c_str(); + + if (SSL_CTX_load_verify_locations(ctx, ca_file, ca_path) != 1) { + LogErr("SSL_CTX_load_verify_locations fail, ca_file:%s, ca_path:%s", + tls_config_.ca_file.c_str(), tls_config_.ca_path.c_str()); + ERR_print_errors_fp(stderr); + SSL_CTX_free(ctx); + return nullptr; + } + + if (tls_config_.verify_peer) { + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + } + } else if (tls_config_.verify_peer) { + //! 使用系统默认 CA 证书 + if (SSL_CTX_set_default_verify_paths(ctx) != 1) { + LogErr("SSL_CTX_set_default_verify_paths fail"); + SSL_CTX_free(ctx); + return nullptr; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + } else { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + } + + //! 加载 client 证书和密钥(可选,用于双向 TLS) + if (!tls_config_.client_cert_file.empty() && !tls_config_.client_key_file.empty()) { + if (SSL_CTX_use_certificate_file(ctx, tls_config_.client_cert_file.c_str(), SSL_FILETYPE_PEM) != 1) { + LogErr("SSL_CTX_use_certificate_file fail, file:%s", tls_config_.client_cert_file.c_str()); + ERR_print_errors_fp(stderr); + SSL_CTX_free(ctx); + return nullptr; + } + if (SSL_CTX_use_PrivateKey_file(ctx, tls_config_.client_key_file.c_str(), SSL_FILETYPE_PEM) != 1) { + LogErr("SSL_CTX_use_PrivateKey_file fail, file:%s", tls_config_.client_key_file.c_str()); + ERR_print_errors_fp(stderr); + SSL_CTX_free(ctx); + return nullptr; + } + if (SSL_CTX_check_private_key(ctx) != 1) { + LogErr("SSL_CTX_check_private_key fail"); + SSL_CTX_free(ctx); + return nullptr; + } + } + + return ctx; +} + +SSL_CTX* TcpTlsFactory::createServerSslCtx() const +{ + SSL_CTX *ctx = SSL_CTX_new(TLS_server_method()); + if (ctx == nullptr) { + LogErr("SSL_CTX_new(TLS_server_method) fail"); + return nullptr; + } + + //! 设置最低 TLS 版本为 1.2 + SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); + + //! 加载 server 证书和密钥 + if (!tls_config_.cert_file.empty() && !tls_config_.key_file.empty()) { + if (SSL_CTX_use_certificate_file(ctx, tls_config_.cert_file.c_str(), SSL_FILETYPE_PEM) != 1) { + LogErr("SSL_CTX_use_certificate_file fail, file:%s", tls_config_.cert_file.c_str()); + ERR_print_errors_fp(stderr); + SSL_CTX_free(ctx); + return nullptr; + } + if (SSL_CTX_use_PrivateKey_file(ctx, tls_config_.key_file.c_str(), SSL_FILETYPE_PEM) != 1) { + LogErr("SSL_CTX_use_PrivateKey_file fail, file:%s", tls_config_.key_file.c_str()); + ERR_print_errors_fp(stderr); + SSL_CTX_free(ctx); + return nullptr; + } + if (SSL_CTX_check_private_key(ctx) != 1) { + LogErr("SSL_CTX_check_private_key fail"); + SSL_CTX_free(ctx); + return nullptr; + } + } else { + LogErr("server cert_file and key_file must be set"); + SSL_CTX_free(ctx); + return nullptr; + } + + //! 加载 CA 证书(可选,用于验证 client - 双向 TLS) + if (!tls_config_.ca_file.empty() || !tls_config_.ca_path.empty()) { + const char *ca_file = tls_config_.ca_file.empty() ? nullptr : tls_config_.ca_file.c_str(); + const char *ca_path = tls_config_.ca_path.empty() ? nullptr : tls_config_.ca_path.c_str(); + + if (SSL_CTX_load_verify_locations(ctx, ca_file, ca_path) != 1) { + LogErr("SSL_CTX_load_verify_locations fail"); + ERR_print_errors_fp(stderr); + SSL_CTX_free(ctx); + return nullptr; + } + + if (tls_config_.verify_peer) { + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + SSL_CTX_set_verify_depth(ctx, tls_config_.verify_depth); + } + } + + return ctx; +} + +} +} diff --git a/modules/network/tcp_tls_factory.h b/modules/network/tcp_tls_factory.h new file mode 100644 index 00000000..6cd6df31 --- /dev/null +++ b/modules/network/tcp_tls_factory.h @@ -0,0 +1,53 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TCP_TLS_FACTORY_H_20260616 +#define TBOX_NETWORK_TCP_TLS_FACTORY_H_20260616 + +#include + +#include "tcp_factory.h" +#include "tls_config.h" + +namespace tbox { +namespace network { + +//! TLS 工厂 +//! 创建 TcpTlsConnector 和 TcpTlsAcceptor +//! 管理 SSL_CTX 的生命周期(分别创建 client 和 server 的 SSL_CTX) +class TcpTlsFactory : public TcpFactory { + public: + explicit TcpTlsFactory(const TlsConfig &config); + ~TcpTlsFactory(); + + virtual TcpConnector* createConnector(event::Loop *wp_loop) override; + virtual TcpAcceptor* createAcceptor(event::Loop *wp_loop) override; + + private: + SSL_CTX* createClientSslCtx() const; + SSL_CTX* createServerSslCtx() const; + + TlsConfig tls_config_; + SSL_CTX *client_ssl_ctx_ = nullptr; //!< client 端 SSL_CTX + SSL_CTX *server_ssl_ctx_ = nullptr; //!< server 端 SSL_CTX +}; + +} +} +#endif //TBOX_NETWORK_TCP_TLS_FACTORY_H_20260616 diff --git a/modules/network/tls_config.cpp b/modules/network/tls_config.cpp new file mode 100644 index 00000000..9745683a --- /dev/null +++ b/modules/network/tls_config.cpp @@ -0,0 +1,42 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tls_config.h" + +#include + +namespace tbox { +namespace network { + +bool TlsConfig::isValid() const +{ + //! 至少需要有证书和密钥文件(server 场景)或 CA 文件(client 场景) + if (!cert_file.empty() && key_file.empty()) { + LogErr("cert_file is set but key_file is not"); + return false; + } + if (key_file.empty() && cert_file.empty() && ca_file.empty() && ca_path.empty()) { + LogErr("no TLS configuration provided"); + return false; + } + return true; +} + +} +} diff --git a/modules/network/tls_config.h b/modules/network/tls_config.h new file mode 100644 index 00000000..3f0f8389 --- /dev/null +++ b/modules/network/tls_config.h @@ -0,0 +1,53 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TLS_CONFIG_H_20260616 +#define TBOX_NETWORK_TLS_CONFIG_H_20260616 + +#include + +namespace tbox { +namespace network { + +//! TLS 配置结构体 +struct TlsConfig { + //! 通用配置 + std::string ca_file; //!< CA 证书文件路径 + std::string ca_path; //!< CA 证书目录路径 + bool verify_peer = true; //!< 是否验证对端证书 + int verify_depth = 1; //!< 证书链验证深度 + + //! Server 专用配置 + std::string cert_file; //!< 服务器/客户端证书文件 + std::string key_file; //!< 服务器/客户端私钥文件 + + //! Client 专用配置(用于双向 TLS) + std::string client_cert_file; //!< 客户端证书文件(可选) + std::string client_key_file; //!< 客户端私钥文件(可选) + + //! Client SNI 配置 + std::string hostname; //!< 用于 SNI (Server Name Indication) 的主机名 + + //! 检查配置是否有效 + bool isValid() const; +}; + +} +} +#endif //TBOX_NETWORK_TLS_CONFIG_H_20260616 diff --git a/modules/run/Makefile b/modules/run/Makefile index a9e994dc..92758a95 100644 --- a/modules/run/Makefile +++ b/modules/run/Makefile @@ -35,6 +35,7 @@ LDFLAGS += \ -ltbox_log \ -ltbox_util \ -ltbox_base \ + -lssl -lcrypto \ -lpthread \ -ldl \ -rdynamic diff --git a/modules/terminal/Makefile b/modules/terminal/Makefile index 89f1dc39..bfcf83f2 100644 --- a/modules/terminal/Makefile +++ b/modules/terminal/Makefile @@ -60,7 +60,7 @@ TEST_CPP_SRC_FILES = \ impl/key_event_scanner.cpp \ impl/key_event_scanner_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_event -ltbox_util -ltbox_base -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl ENABLE_SHARED_LIB = no include $(TOP_DIR)/mk/lib_tbox_common.mk diff --git a/modules/websocket/Makefile b/modules/websocket/Makefile index 96fb9531..4e0df631 100644 --- a/modules/websocket/Makefile +++ b/modules/websocket/Makefile @@ -47,7 +47,7 @@ TEST_CPP_SRC_FILES = \ ws_frame_parser_test.cpp \ ws_frame_builder_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_crypto -ltbox_http -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_crypto -ltbox_http -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl ENABLE_SHARED_LIB = no diff --git a/modules/websocket/client/client_impl.cpp b/modules/websocket/client/client_impl.cpp index b17392ea..995b0af4 100644 --- a/modules/websocket/client/client_impl.cpp +++ b/modules/websocket/client/client_impl.cpp @@ -87,8 +87,8 @@ bool Client::Impl::initialize(const network::SockAddr &server_addr, const std::s server_addr_ = server_addr; url_path_ = url_path; - //! 创建 TcpConnector(保持存活,供重连使用) - sp_connector_ = new network::TcpConnector(wp_loop_); + //! 创建 TcpRawConnector(保持存活,供重连使用) + sp_connector_ = new network::TcpRawConnector(wp_loop_); sp_connector_->initialize(server_addr_); sp_connector_->setConnectedCallback(std::bind(&Client::Impl::onTcpConnected, this, _1)); diff --git a/modules/websocket/client/client_impl.h b/modules/websocket/client/client_impl.h index dd0e62f9..3e996887 100644 --- a/modules/websocket/client/client_impl.h +++ b/modules/websocket/client/client_impl.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "client.h" -- Gitee From d417bac887437abfa34aae7ee67823b15fe52b8b Mon Sep 17 00:00:00 2001 From: "chunjun.li" Date: Fri, 26 Jun 2026 13:46:50 +0800 Subject: [PATCH 2/9] ver:1.15.1 --- version.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.mk b/version.mk index 74ca0d82..da06a085 100644 --- a/version.mk +++ b/version.mk @@ -20,5 +20,5 @@ # TBOX版本号 TBOX_VERSION_MAJOR := 1 -TBOX_VERSION_MINOR := 14 -TBOX_VERSION_REVISION := 2 +TBOX_VERSION_MINOR := 15 +TBOX_VERSION_REVISION := 1 -- Gitee From 211e539eb6e5af88683c69c8e9a8a144aab913cc Mon Sep 17 00:00:00 2001 From: Hevake Date: Sat, 27 Jun 2026 18:39:47 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat:=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0TC?= =?UTF-8?q?P=E7=9A=84TLS=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=BF=98=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.mk | 1 + examples/http/server/https_simple/Makefile | 37 ++++ .../http/server/https_simple/https_simple.cpp | 134 +++++++++++++ examples/http/server/https_simple/server.crt | 21 ++ examples/http/server/https_simple/server.key | 28 +++ .../tcp_acceptor/tcp_echo/tcp_echo.cpp | 4 +- .../tcp_nc_server/tcp_nc_server.cpp | 4 +- .../tcp_client/tls_echo_client/Makefile | 34 ++++ .../tls_echo_client/tls_echo_client.cpp | 141 ++++++++++++++ .../tcp_connector/tcp_echo/tcp_echo.cpp | 4 +- .../tcp_nc_client/tcp_nc_client.cpp | 4 +- .../tcp_server/tls_echo_server/Makefile | 34 ++++ .../tcp_server/tls_echo_server/server.crt | 21 ++ .../tcp_server/tls_echo_server/server.key | 28 +++ .../tls_echo_server/tls_echo_server.cpp | 128 ++++++++++++ modules/http/Makefile | 2 +- modules/http/client/client.cpp | 5 + modules/http/client/client.h | 5 + modules/http/client/client_impl.cpp | 5 + modules/http/client/client_impl.h | 2 + modules/http/server/server.cpp | 5 + modules/http/server/server.h | 4 + modules/http/server/server_imp.cpp | 5 + modules/http/server/server_imp.h | 2 + modules/main/Makefile | 1 - modules/network/Makefile | 14 +- modules/network/stdio_stream.cpp | 4 +- modules/network/tcp_acceptor.cpp | 4 +- modules/network/tcp_client.cpp | 28 ++- modules/network/tcp_client.h | 2 +- modules/network/tcp_connection.cpp | 8 +- modules/network/tcp_connector.cpp | 11 +- modules/network/tcp_factory.h | 1 + modules/network/tcp_raw_acceptor.cpp | 4 +- modules/network/tcp_raw_connection.cpp | 4 +- modules/network/tcp_raw_connector.cpp | 7 +- modules/network/tcp_raw_connector.h | 3 +- modules/network/tcp_raw_factory.h | 1 + modules/network/tcp_server.cpp | 24 ++- modules/network/tcp_server.h | 2 +- modules/network/tls_config.cpp | 14 +- modules/network/tls_config.h | 13 +- modules/network/tls_factory_entry.cpp | 34 ++++ modules/network/tls_factory_entry.h | 43 ++++ modules/network_tls/Makefile | 54 ++++++ .../buffered_ssl_fd.cpp | 19 +- .../buffered_ssl_fd.h | 2 +- .../tcp_tls_acceptor.cpp | 40 ++-- .../tcp_tls_acceptor.h | 6 +- .../tcp_tls_connection.cpp | 8 +- .../tcp_tls_connection.h | 2 +- .../tcp_tls_connector.cpp | 34 +++- .../tcp_tls_connector.h | 8 +- .../tcp_tls_factory.cpp | 183 ++++++++++-------- .../tcp_tls_factory.h | 22 +-- modules/network_tls/tls_factory_entry.cpp | 32 +++ modules/run/Makefile | 5 +- modules/terminal/Makefile | 2 +- modules/websocket/Makefile | 2 +- modules/websocket/client/ws_client.cpp | 5 + modules/websocket/client/ws_client.h | 5 + modules/websocket/client/ws_client_impl.cpp | 37 +++- modules/websocket/client/ws_client_impl.h | 5 +- 63 files changed, 1144 insertions(+), 207 deletions(-) create mode 100644 examples/http/server/https_simple/Makefile create mode 100644 examples/http/server/https_simple/https_simple.cpp create mode 100644 examples/http/server/https_simple/server.crt create mode 100644 examples/http/server/https_simple/server.key create mode 100644 examples/network/tcp_client/tls_echo_client/Makefile create mode 100644 examples/network/tcp_client/tls_echo_client/tls_echo_client.cpp create mode 100644 examples/network/tcp_server/tls_echo_server/Makefile create mode 100644 examples/network/tcp_server/tls_echo_server/server.crt create mode 100644 examples/network/tcp_server/tls_echo_server/server.key create mode 100644 examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp create mode 100644 modules/network/tls_factory_entry.cpp create mode 100644 modules/network/tls_factory_entry.h create mode 100644 modules/network_tls/Makefile rename modules/{network => network_tls}/buffered_ssl_fd.cpp (89%) rename modules/{network => network_tls}/buffered_ssl_fd.h (98%) rename modules/{network => network_tls}/tcp_tls_acceptor.cpp (81%) rename modules/{network => network_tls}/tcp_tls_acceptor.h (94%) rename modules/{network => network_tls}/tcp_tls_connection.cpp (94%) rename modules/{network => network_tls}/tcp_tls_connection.h (97%) rename modules/{network => network_tls}/tcp_tls_connector.cpp (84%) rename modules/{network => network_tls}/tcp_tls_connector.h (93%) rename modules/{network => network_tls}/tcp_tls_factory.cpp (38%) rename modules/{network => network_tls}/tcp_tls_factory.h (67%) create mode 100644 modules/network_tls/tls_factory_entry.cpp diff --git a/config.mk b/config.mk index 94768fc3..b5865ef3 100644 --- a/config.mk +++ b/config.mk @@ -25,6 +25,7 @@ MODULES += event MODULES += eventx MODULES += log MODULES += network +MODULES += network_tls ## 需要 TLS 时取消注释,不需要时注释掉即可,不链接 libssl/libcrypto MODULES += terminal MODULES += trace MODULES += coroutine diff --git a/examples/http/server/https_simple/Makefile b/examples/http/server/https_simple/Makefile new file mode 100644 index 00000000..bc10b125 --- /dev/null +++ b/examples/http/server/https_simple/Makefile @@ -0,0 +1,37 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. +# + +PROJECT := examples/http/server/https_simple +EXE_NAME := ${PROJECT} + +CPP_SRC_FILES := https_simple.cpp + +CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_http \ + -ltbox_network \ + -Wl,--whole-archive -ltbox_network_tls -Wl,--no-whole-archive \ + -ltbox_eventx \ + -ltbox_event \ + -ltbox_log \ + -ltbox_util \ + -ltbox_base \ + -lssl -lcrypto \ + -lpthread -ldl + +include $(TOP_DIR)/mk/exe_common.mk diff --git a/examples/http/server/https_simple/https_simple.cpp b/examples/http/server/https_simple/https_simple.cpp new file mode 100644 index 00000000..bc7487b1 --- /dev/null +++ b/examples/http/server/https_simple/https_simple.cpp @@ -0,0 +1,134 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +/** + * HTTPS 版 simple server 示例 + * 用法:https_simple --cert --key + * 必须指定证书文件和密钥文件 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace tbox; +using namespace tbox::event; +using namespace tbox::http; +using namespace tbox::http::server; +using namespace tbox::network; + +void PrintUsage(const char *prog) +{ + cout << "Usage: " << prog << " --cert --key " << endl + << "Exp : " << prog << " 0.0.0.0:12345 --cert server.crt --key server.key" << endl; +} + +int main(int argc, char **argv) +{ + string bind_addr_str = "0.0.0.0:12345"; + string cert_file; + string key_file; + + //! 解析命令行参数 + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--cert") == 0 && i + 1 < argc) { + cert_file = argv[++i]; + } else if (strcmp(argv[i], "--key") == 0 && i + 1 < argc) { + key_file = argv[++i]; + } else if (argv[i][0] != '-') { + bind_addr_str = argv[i]; + } else { + cerr << "Error: invalid option `" << argv[i] << "'" << endl; + PrintUsage(argv[0]); + return 0; + } + } + + if (cert_file.empty() || key_file.empty()) { + PrintUsage(argv[0]); + return 0; + } + + LogOutput_Enable(); + + LogInfo("enter"); + + auto sp_loop = Loop::New(); + auto sp_sig_event = sp_loop->newSignalEvent(); + + SetScopeExitAction( + [=] { + delete sp_sig_event; + delete sp_loop; + } + ); + + sp_sig_event->initialize(SIGINT, Event::Mode::kPersist); + sp_sig_event->enable(); + + Server srv(sp_loop); + + //! 设置 TLS 配置(必须在 initialize() 之前调用) + TlsConfig tls_config; + tls_config.cert_file = cert_file; + tls_config.key_file = key_file; + tls_config.verify_peer = false; //! 测试环境不验证 client 证书 + if (!srv.setTlsConfig(tls_config)) { + LogErr("set tls config fail, need network_tls module"); + return 0; + } + + if (!srv.initialize(SockAddr::FromString(bind_addr_str), 1)) { + LogErr("init srv fail"); + return 0; + } + + srv.start(); + //srv.setContextLogEnable(true); //! 调试时需要看详细收发数据时可以打开 + + //! 添加请求处理 + srv.use( + [&](ContextSptr ctx, const NextFunc &next) { + ctx->res().status_code = StatusCode::k200_OK; + ctx->res().body = "Hello HTTPS!"; + } + ); + + sp_sig_event->setCallback( + [&] (int) { + srv.stop(); + sp_loop->exitLoop(); + } + ); + + LogInfo("start"); + sp_loop->runLoop(); + LogInfo("stop"); + srv.cleanup(); + + LogInfo("exit"); + return 0; +} diff --git a/examples/http/server/https_simple/server.crt b/examples/http/server/https_simple/server.crt new file mode 100644 index 00000000..aafbc0be --- /dev/null +++ b/examples/http/server/https_simple/server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDiTCCAnGgAwIBAgIUfLUbGuJjk5Wg1+v6hnOVyfPJrpEwDQYJKoZIhvcNAQEL +BQAwWDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxETAPBgNVBAoMCGNwcC10Ym94MRIwEAYDVQQDDAkxMjcuMC4wLjEwHhcN +MjYwNjI2MTMzMjQ5WhcNMzYwNjIzMTMzMjQ5WjBYMQswCQYDVQQGEwJDTjEQMA4G +A1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzERMA8GA1UECgwIY3BwLXRi +b3gxEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALlvcZ7VBhp0S3tB1ho2Qb0qCHQ8Ezdf3X3OOfviXBPg4IogXZSWy0RM +CBbP7PaxpksX9DHVtsjydss1vb3ts+DU4vFtc57wq8Sf+NQ7xvGJffH12BuYUjyn +1cE0ENdzv7FuruZ1Q/c2wtll2WwL5Bo+ggqyF7Cr+Ja/reHEN1eN26oLtTJQcph0 +YCbJJlmJjLO51sDmweaNe9i8Ck7y7EHypL1ecMMMyhnsTavZmntNavIPdX9CnB9d +MPsQT/yG4FUBve3ZOVL8+MawQ/bbiAlTPYuEjiooBJiZD2+6GmbMfu3tVO/yA+Wg +zLPv7ANNK70H/rysscgyxhct90vzfGUCAwEAAaNLMEkwGgYDVR0RBBMwEYcEfwAA +AYIJbG9jYWxob3N0MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFK18uhz4KeDIsASC +2wxeJTR1U2SIMA0GCSqGSIb3DQEBCwUAA4IBAQB7W6ZOA3Uun+6THd01l3JlQqk7 +i/oTc9bUterTvc/tI1Quc349pZdVI1d0GkhyFRNMpTF1D5Cph5OkmfugFDsP9a62 +Gjg2CQ+H93WeEDZ5nLJpNQDPwVyMqM3dORPhVHx7S5IEj7vi8q5ko3MIl6L5wGdy +x7cyGxsgFj8INU651rtB8G27B1HL4yQW+Ix1wq/xlh/Lg+5/lIoC6MiywE5+UyMz +FOTETFuI4Zs+vg5Q36fm/hJ+WdLS4gifarY2blKQMZBcegkp0/DnUmAxXEz66zl3 +UPppc85X5/qPRO1GMRgadPxi6JNvha+m5CyCMTvaggVCvOiBpfn3f78fUMDc +-----END CERTIFICATE----- diff --git a/examples/http/server/https_simple/server.key b/examples/http/server/https_simple/server.key new file mode 100644 index 00000000..a9b48be0 --- /dev/null +++ b/examples/http/server/https_simple/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5b3Ge1QYadEt7 +QdYaNkG9Kgh0PBM3X919zjn74lwT4OCKIF2UlstETAgWz+z2saZLF/Qx1bbI8nbL +Nb297bPg1OLxbXOe8KvEn/jUO8bxiX3x9dgbmFI8p9XBNBDXc7+xbq7mdUP3NsLZ +ZdlsC+QaPoIKshewq/iWv63hxDdXjduqC7UyUHKYdGAmySZZiYyzudbA5sHmjXvY +vApO8uxB8qS9XnDDDMoZ7E2r2Zp7TWryD3V/QpwfXTD7EE/8huBVAb3t2TlS/PjG +sEP224gJUz2LhI4qKASYmQ9vuhpmzH7t7VTv8gPloMyz7+wDTSu9B/68rLHIMsYX +LfdL83xlAgMBAAECggEAI3Hg5vpTC1V1ZB8GfMYoNK9HJGijR69kV/rGbJYtAYO3 +h89987wLKIfb9/hQlCsK3Um73Ja8NJbcDCW+mgJIos4ufvVr51Kbkp79Yhv3AA5G +66wRXdz0wzFVk3OPUI+IcbL1bYm2rxdhkUp9j8CKHlYaZ075ZkTI5I/I/eGSroJU +nwSBgvZQnS27+qH0Cw/JC/80q3vcAKJb9eZNcnKtrHo1SIahu7yO+zGAOXF80vA2 +9fSEda0DZhjkP0tuwX/jhYRz33i8um++CQ0irD+Oe5voRpPsMUQbgqQkJTl5LL93 +HjaMVJTOnhMLkprARm0E0RkGJ+T8zo9BryQHm57I1QKBgQDLRaKQs9OqXDmimb+b +O0isGGG8Rw0RMaIXAYunF/K+8hQ1ZMIK01yE+bRpASAK9mAWOKfpqyEkz3hWoetp +ESXIMa32Phmg19EPakfxbd/bhhPd1ce08M9XAilPECXcr5xyDVuxVnWo9MisZJhE +49vMtZjxp8ZBUlJzoGmyli6lVwKBgQDpiVssjoM/ocSjbSiiDAH5PIl7t1S+vG65 +F6Np9rozvwBiQI9F5+BHlU5SwYmCrUREZHvUngHEvcWJxAz4eak2RchbzqKNXEiI +VfI/6QGqxbJuG3adfHOd+tMYMv7ttGU06uqQl3B6WmcAIW/Utiv3UI557LhnRgjc +pKFuLnm6owKBgG9QjeqyH3qOkJ1jltL6Txy3KWaCfjxpMrtohEKX0b4RMVHgAIcP +If5MBCjwjcyTCSGCGynSJg9TcjH278SUuFz+H6bWcRBsvzay2/zxT4KW1PBJbti+ +erzKGTcLv8AvhvvKJulhUIOasP3/BIfNRAPBeqTzXJVO8IoTUW6T4a13AoGAJG65 +OopBD3w9IQG2hRE6fZdkG1jOb7MV0upNJArJoaj6dll8AHvcEU7JmT94JFrDe6fx +aYn83KR+XK+pFlpke4MHbssdsM/kwOAnmrDPAcU1wNen+Ymgv9SRegT6oDq0Tz0W +utflRDE2QF73A0goM7ztfTfgzLuwRjuos3espeECgYEAqbm17B0UZolBVaPOBRuT +a+gYAOUxZpYCi+ajLFcDa67geLgCNTp8EJj/uwKsVCECGgDT5pJLtAnfeNQvYrfq +IN7I12L4L+PMn5Tv5E7HevWBknjd87mujS9YymGdXkBvQHWv9oEq1du0qHrL9tLx +pr+LuwJVR1QmxvWliRSCeew= +-----END PRIVATE KEY----- diff --git a/examples/network/tcp_acceptor/tcp_echo/tcp_echo.cpp b/examples/network/tcp_acceptor/tcp_echo/tcp_echo.cpp index 6dd2ea2d..b4ff0b13 100644 --- a/examples/network/tcp_acceptor/tcp_echo/tcp_echo.cpp +++ b/examples/network/tcp_acceptor/tcp_echo/tcp_echo.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include @@ -62,7 +62,7 @@ int main(int argc, char **argv) set conns; - TcpAcceptor acceptor(sp_loop); + TcpRawAcceptor acceptor(sp_loop); acceptor.initialize(bind_addr, 1); //! 指定有Client连接上了该做的事务 acceptor.setNewConnectionCallback( diff --git a/examples/network/tcp_acceptor/tcp_nc_server/tcp_nc_server.cpp b/examples/network/tcp_acceptor/tcp_nc_server/tcp_nc_server.cpp index fec1b762..7e4e8afe 100644 --- a/examples/network/tcp_acceptor/tcp_nc_server/tcp_nc_server.cpp +++ b/examples/network/tcp_acceptor/tcp_nc_server/tcp_nc_server.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include @@ -75,7 +75,7 @@ int main(int argc, char **argv) }, 0 ); - TcpAcceptor acceptor(sp_loop); + TcpRawAcceptor acceptor(sp_loop); acceptor.initialize(bind_addr, 1); //! 指定有Client连接上了该做的事务 acceptor.setNewConnectionCallback( diff --git a/examples/network/tcp_client/tls_echo_client/Makefile b/examples/network/tcp_client/tls_echo_client/Makefile new file mode 100644 index 00000000..5ac11e20 --- /dev/null +++ b/examples/network/tcp_client/tls_echo_client/Makefile @@ -0,0 +1,34 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. +# + +PROJECT := examples/network/tcp_client/tls_echo_client +EXE_NAME := ${PROJECT} + +CPP_SRC_FILES := tls_echo_client.cpp + +CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_network \ + -Wl,--whole-archive -ltbox_network_tls -Wl,--no-whole-archive \ + -ltbox_event \ + -ltbox_util \ + -ltbox_base \ + -lssl -lcrypto \ + -ldl + +include $(TOP_DIR)/mk/exe_common.mk diff --git a/examples/network/tcp_client/tls_echo_client/tls_echo_client.cpp b/examples/network/tcp_client/tls_echo_client/tls_echo_client.cpp new file mode 100644 index 00000000..a97ebc0f --- /dev/null +++ b/examples/network/tcp_client/tls_echo_client/tls_echo_client.cpp @@ -0,0 +1,141 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +/** + * TLS 版 echo client 示例 + * 用法:tls_echo_client [--ca ] [--hostname ] + * 连接成功后,stdin 输入的内容会发送到 server,server 返回的数据会显示在 stdout + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace tbox; +using namespace tbox::event; +using namespace tbox::network; + +void PrintUsage(const char *prog) +{ + cout << "Usage: " << prog << " [--ca ] [--hostname ]" << endl + << "Exp : " << prog << " 127.0.0.1:12345" << endl + << " " << prog << " 127.0.0.1:12345 --ca server.crt --hostname myserver" << endl; +} + +int main(int argc, char **argv) +{ + string server_addr_str; + string ca_file; + string hostname = "127.0.0.1"; + + //! 解析命令行参数 + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--ca") == 0 && i + 1 < argc) { + ca_file = argv[++i]; + } else if (strcmp(argv[i], "--hostname") == 0 && i + 1 < argc) { + hostname = argv[++i]; + } else if (argv[i][0] != '-') { + server_addr_str = argv[i]; + } else { + cerr << "Error: invalid option `" << argv[i] << "'" << endl; + PrintUsage(argv[0]); + return 0; + } + } + + if (server_addr_str.empty()) { + PrintUsage(argv[0]); + return 0; + } + + LogOutput_Enable(); + + SockAddr server_addr = SockAddr::FromString(server_addr_str); + + Loop *sp_loop = Loop::New(); + SetScopeExitAction([sp_loop] { delete sp_loop; }); + + StdioStream stdio(sp_loop); + stdio.initialize(); + + TcpClient client(sp_loop); + + //! 设置 TLS 配置(必须在 initialize 之前调用) + TlsConfig tls_config; + tls_config.hostname = hostname; //! SNI hostname + if (!ca_file.empty()) { + tls_config.ca_file = ca_file; + tls_config.verify_peer = true; + } else { + tls_config.verify_peer = false; //! 未指定 CA 证书时,不做对端验证 + } + if (!client.setTlsConfig(tls_config)) { + LogErr("set tls config fail, need network_tls module"); + return 0; + } + + client.initialize(server_addr); + + //! 连接成功后,绑定 stdio 和 client 的双向数据流 + client.setConnectedCallback( + [&client, &stdio] { + cout << "connected!" << endl; + stdio.enable(); //! 连接成功后才启用 stdin 读取 + client.bind(&stdio); //! server 的数据往终端输出 + stdio.bind(&client); //! 终端上的输入往 server 输出 + } + ); + + client.setDisconnectedCallback( + [&client, &stdio] { + cout << "disconnected!" << endl; + stdio.unbind(); + client.unbind(); + } + ); + + client.start(); + + //! 注册 ctrl+C 停止信号 + SignalEvent *sp_stop_ev = sp_loop->newSignalEvent(); + SetScopeExitAction([sp_stop_ev] { delete sp_stop_ev; }); + sp_stop_ev->initialize(SIGINT, Event::Mode::kOneshot); + sp_stop_ev->setCallback( + [sp_loop, &client] (int) { + client.stop(); + sp_loop->exitLoop(); + } + ); + sp_stop_ev->enable(); + + LogInfo("tls echo client running ..."); + sp_loop->runLoop(); + LogInfo("tls echo client stopped"); + + return 0; +} diff --git a/examples/network/tcp_connector/tcp_echo/tcp_echo.cpp b/examples/network/tcp_connector/tcp_echo/tcp_echo.cpp index 8573ccbe..3889ec9c 100644 --- a/examples/network/tcp_connector/tcp_echo/tcp_echo.cpp +++ b/examples/network/tcp_connector/tcp_echo/tcp_echo.cpp @@ -27,7 +27,7 @@ #include -#include +#include #include #include @@ -62,7 +62,7 @@ int main(int argc, char **argv) TcpConnection *sp_curr = nullptr; - TcpConnector connector(sp_loop); + TcpRawConnector connector(sp_loop); connector.initialize(bind_addr); //! 指定有Client连接上后该做的事务 connector.setConnectedCallback( diff --git a/examples/network/tcp_connector/tcp_nc_client/tcp_nc_client.cpp b/examples/network/tcp_connector/tcp_nc_client/tcp_nc_client.cpp index c5bddc90..33e20581 100644 --- a/examples/network/tcp_connector/tcp_nc_client/tcp_nc_client.cpp +++ b/examples/network/tcp_connector/tcp_nc_client/tcp_nc_client.cpp @@ -27,7 +27,7 @@ #include -#include +#include #include #include @@ -67,7 +67,7 @@ int main(int argc, char **argv) TcpConnection *sp_curr = nullptr; - TcpConnector connector(sp_loop); + TcpRawConnector connector(sp_loop); connector.initialize(bind_addr); //! 指定有Client连接上后该做的事务 connector.setConnectedCallback( diff --git a/examples/network/tcp_server/tls_echo_server/Makefile b/examples/network/tcp_server/tls_echo_server/Makefile new file mode 100644 index 00000000..76d61c99 --- /dev/null +++ b/examples/network/tcp_server/tls_echo_server/Makefile @@ -0,0 +1,34 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. +# + +PROJECT := examples/network/tcp_server/tls_echo_server +EXE_NAME := ${PROJECT} + +CPP_SRC_FILES := tls_echo_server.cpp + +CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_network \ + -Wl,--whole-archive -ltbox_network_tls -Wl,--no-whole-archive \ + -ltbox_event \ + -ltbox_util \ + -ltbox_base \ + -lssl -lcrypto \ + -ldl + +include $(TOP_DIR)/mk/exe_common.mk diff --git a/examples/network/tcp_server/tls_echo_server/server.crt b/examples/network/tcp_server/tls_echo_server/server.crt new file mode 100644 index 00000000..aafbc0be --- /dev/null +++ b/examples/network/tcp_server/tls_echo_server/server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDiTCCAnGgAwIBAgIUfLUbGuJjk5Wg1+v6hnOVyfPJrpEwDQYJKoZIhvcNAQEL +BQAwWDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxETAPBgNVBAoMCGNwcC10Ym94MRIwEAYDVQQDDAkxMjcuMC4wLjEwHhcN +MjYwNjI2MTMzMjQ5WhcNMzYwNjIzMTMzMjQ5WjBYMQswCQYDVQQGEwJDTjEQMA4G +A1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzERMA8GA1UECgwIY3BwLXRi +b3gxEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALlvcZ7VBhp0S3tB1ho2Qb0qCHQ8Ezdf3X3OOfviXBPg4IogXZSWy0RM +CBbP7PaxpksX9DHVtsjydss1vb3ts+DU4vFtc57wq8Sf+NQ7xvGJffH12BuYUjyn +1cE0ENdzv7FuruZ1Q/c2wtll2WwL5Bo+ggqyF7Cr+Ja/reHEN1eN26oLtTJQcph0 +YCbJJlmJjLO51sDmweaNe9i8Ck7y7EHypL1ecMMMyhnsTavZmntNavIPdX9CnB9d +MPsQT/yG4FUBve3ZOVL8+MawQ/bbiAlTPYuEjiooBJiZD2+6GmbMfu3tVO/yA+Wg +zLPv7ANNK70H/rysscgyxhct90vzfGUCAwEAAaNLMEkwGgYDVR0RBBMwEYcEfwAA +AYIJbG9jYWxob3N0MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFK18uhz4KeDIsASC +2wxeJTR1U2SIMA0GCSqGSIb3DQEBCwUAA4IBAQB7W6ZOA3Uun+6THd01l3JlQqk7 +i/oTc9bUterTvc/tI1Quc349pZdVI1d0GkhyFRNMpTF1D5Cph5OkmfugFDsP9a62 +Gjg2CQ+H93WeEDZ5nLJpNQDPwVyMqM3dORPhVHx7S5IEj7vi8q5ko3MIl6L5wGdy +x7cyGxsgFj8INU651rtB8G27B1HL4yQW+Ix1wq/xlh/Lg+5/lIoC6MiywE5+UyMz +FOTETFuI4Zs+vg5Q36fm/hJ+WdLS4gifarY2blKQMZBcegkp0/DnUmAxXEz66zl3 +UPppc85X5/qPRO1GMRgadPxi6JNvha+m5CyCMTvaggVCvOiBpfn3f78fUMDc +-----END CERTIFICATE----- diff --git a/examples/network/tcp_server/tls_echo_server/server.key b/examples/network/tcp_server/tls_echo_server/server.key new file mode 100644 index 00000000..a9b48be0 --- /dev/null +++ b/examples/network/tcp_server/tls_echo_server/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5b3Ge1QYadEt7 +QdYaNkG9Kgh0PBM3X919zjn74lwT4OCKIF2UlstETAgWz+z2saZLF/Qx1bbI8nbL +Nb297bPg1OLxbXOe8KvEn/jUO8bxiX3x9dgbmFI8p9XBNBDXc7+xbq7mdUP3NsLZ +ZdlsC+QaPoIKshewq/iWv63hxDdXjduqC7UyUHKYdGAmySZZiYyzudbA5sHmjXvY +vApO8uxB8qS9XnDDDMoZ7E2r2Zp7TWryD3V/QpwfXTD7EE/8huBVAb3t2TlS/PjG +sEP224gJUz2LhI4qKASYmQ9vuhpmzH7t7VTv8gPloMyz7+wDTSu9B/68rLHIMsYX +LfdL83xlAgMBAAECggEAI3Hg5vpTC1V1ZB8GfMYoNK9HJGijR69kV/rGbJYtAYO3 +h89987wLKIfb9/hQlCsK3Um73Ja8NJbcDCW+mgJIos4ufvVr51Kbkp79Yhv3AA5G +66wRXdz0wzFVk3OPUI+IcbL1bYm2rxdhkUp9j8CKHlYaZ075ZkTI5I/I/eGSroJU +nwSBgvZQnS27+qH0Cw/JC/80q3vcAKJb9eZNcnKtrHo1SIahu7yO+zGAOXF80vA2 +9fSEda0DZhjkP0tuwX/jhYRz33i8um++CQ0irD+Oe5voRpPsMUQbgqQkJTl5LL93 +HjaMVJTOnhMLkprARm0E0RkGJ+T8zo9BryQHm57I1QKBgQDLRaKQs9OqXDmimb+b +O0isGGG8Rw0RMaIXAYunF/K+8hQ1ZMIK01yE+bRpASAK9mAWOKfpqyEkz3hWoetp +ESXIMa32Phmg19EPakfxbd/bhhPd1ce08M9XAilPECXcr5xyDVuxVnWo9MisZJhE +49vMtZjxp8ZBUlJzoGmyli6lVwKBgQDpiVssjoM/ocSjbSiiDAH5PIl7t1S+vG65 +F6Np9rozvwBiQI9F5+BHlU5SwYmCrUREZHvUngHEvcWJxAz4eak2RchbzqKNXEiI +VfI/6QGqxbJuG3adfHOd+tMYMv7ttGU06uqQl3B6WmcAIW/Utiv3UI557LhnRgjc +pKFuLnm6owKBgG9QjeqyH3qOkJ1jltL6Txy3KWaCfjxpMrtohEKX0b4RMVHgAIcP +If5MBCjwjcyTCSGCGynSJg9TcjH278SUuFz+H6bWcRBsvzay2/zxT4KW1PBJbti+ +erzKGTcLv8AvhvvKJulhUIOasP3/BIfNRAPBeqTzXJVO8IoTUW6T4a13AoGAJG65 +OopBD3w9IQG2hRE6fZdkG1jOb7MV0upNJArJoaj6dll8AHvcEU7JmT94JFrDe6fx +aYn83KR+XK+pFlpke4MHbssdsM/kwOAnmrDPAcU1wNen+Ymgv9SRegT6oDq0Tz0W +utflRDE2QF73A0goM7ztfTfgzLuwRjuos3espeECgYEAqbm17B0UZolBVaPOBRuT +a+gYAOUxZpYCi+ajLFcDa67geLgCNTp8EJj/uwKsVCECGgDT5pJLtAnfeNQvYrfq +IN7I12L4L+PMn5Tv5E7HevWBknjd87mujS9YymGdXkBvQHWv9oEq1du0qHrL9tLx +pr+LuwJVR1QmxvWliRSCeew= +-----END PRIVATE KEY----- diff --git a/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp b/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp new file mode 100644 index 00000000..60bf3982 --- /dev/null +++ b/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp @@ -0,0 +1,128 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +/** + * TLS 版 echo server 示例 + * 用法:tls_echo_server --cert --key + * 必须指定证书文件和密钥文件 + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace tbox; +using namespace tbox::event; +using namespace tbox::network; + +void PrintUsage(const char *prog) +{ + cout << "Usage: " << prog << " --cert --key " << endl + << "Exp : " << prog << " 0.0.0.0:12345 --cert server.crt --key server.key" << endl; +} + +int main(int argc, char **argv) +{ + string bind_addr_str; + string cert_file; + string key_file; + + //! 解析命令行参数 + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--cert") == 0 && i + 1 < argc) { + cert_file = argv[++i]; + } else if (strcmp(argv[i], "--key") == 0 && i + 1 < argc) { + key_file = argv[++i]; + } else if (argv[i][0] != '-') { + bind_addr_str = argv[i]; + } else { + cerr << "Error: invalid option `" << argv[i] << "'" << endl; + PrintUsage(argv[0]); + return 0; + } + } + + if (bind_addr_str.empty() || cert_file.empty() || key_file.empty()) { + PrintUsage(argv[0]); + return 0; + } + + LogOutput_Enable(); + + SockAddr bind_addr = SockAddr::FromString(bind_addr_str); + + Loop *sp_loop = Loop::New(); + SetScopeExitAction([sp_loop] { delete sp_loop; }); + + TcpServer server(sp_loop); + + //! 设置 TLS 配置(必须在 initialize 之前调用) + TlsConfig tls_config; + tls_config.cert_file = cert_file; + tls_config.key_file = key_file; + tls_config.verify_peer = false; //! 测试环境不验证 client 证书 + if (!server.setTlsConfig(tls_config)) { + LogErr("set tls config fail, need network_tls module"); + return 0; + } + + server.initialize(bind_addr, 1); + //! 当收到数据时,直接往 client 指定对象发回去 + server.setReceiveCallback( + [&server] (const TcpServer::ConnToken &client, Buffer &buff) { + server.send(client, buff.readableBegin(), buff.readableSize()); + buff.hasReadAll(); + }, 0 + ); + server.start(); + + //! 注册 ctrl+C 停止信号 + SignalEvent *sp_stop_ev = sp_loop->newSignalEvent(); + SetScopeExitAction([sp_stop_ev] { delete sp_stop_ev; }); + sp_stop_ev->initialize(SIGINT, Event::Mode::kOneshot); + sp_stop_ev->setCallback( + [sp_loop, &server] (int) { + server.stop(); + sp_loop->exitLoop(); + } + ); + sp_stop_ev->enable(); + + LogInfo("tls echo server running ..."); + + if (bind_addr.type() == SockAddr::Type::kIPv4) { + IPAddress ip; + uint16_t port; + bind_addr.get(ip, port); + cout << "try command: nc " << ip.toString() << ' ' << port << endl; + } + + sp_loop->runLoop(); + LogInfo("tls echo server stopped"); + + return 0; +} diff --git a/modules/http/Makefile b/modules/http/Makefile index fd90a328..775eadba 100644 --- a/modules/http/Makefile +++ b/modules/http/Makefile @@ -66,7 +66,7 @@ TEST_CPP_SRC_FILES = \ url_test.cpp \ server/request_parser_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -ldl ENABLE_SHARED_LIB = no diff --git a/modules/http/client/client.cpp b/modules/http/client/client.cpp index 6a2aecf5..94f5956a 100644 --- a/modules/http/client/client.cpp +++ b/modules/http/client/client.cpp @@ -38,6 +38,11 @@ bool Client::initialize(const network::SockAddr &server_addr) return impl_->initialize(server_addr); } +void Client::setTlsConfig(const network::TlsConfig &config) +{ + impl_->setTlsConfig(config); +} + bool Client::start() { return impl_->start(); diff --git a/modules/http/client/client.h b/modules/http/client/client.h index 441dbb8a..34125fce 100644 --- a/modules/http/client/client.h +++ b/modules/http/client/client.h @@ -22,6 +22,7 @@ #include #include +#include #include #include "../common.h" @@ -53,6 +54,10 @@ class Client { //! 初始化,设置目标服务器地址 bool initialize(const network::SockAddr &server_addr); + //! 设置 TLS 配置(必须在 initialize() 之前调用) + //! 需要 network_tls 模块支持,未链接时调用无效 + void setTlsConfig(const network::TlsConfig &config); + bool start(); //!< 开始连接 void stop(); //!< 停止/断开连接 void cleanup(); //!< 清理,与 initialize() 是逆操作 diff --git a/modules/http/client/client_impl.cpp b/modules/http/client/client_impl.cpp index 8eda2a59..748331bb 100644 --- a/modules/http/client/client_impl.cpp +++ b/modules/http/client/client_impl.cpp @@ -64,6 +64,11 @@ bool Client::Impl::initialize(const SockAddr &server_addr) return true; } +void Client::Impl::setTlsConfig(const TlsConfig &config) +{ + tcp_client_.setTlsConfig(config); +} + bool Client::Impl::start() { if (state_ != State::kInited) { diff --git a/modules/http/client/client_impl.h b/modules/http/client/client_impl.h index 94c58890..8ddc31ae 100644 --- a/modules/http/client/client_impl.h +++ b/modules/http/client/client_impl.h @@ -25,6 +25,7 @@ #include #include +#include #include #include "client.h" @@ -48,6 +49,7 @@ class Client::Impl { public: bool initialize(const SockAddr &server_addr); + void setTlsConfig(const TlsConfig &config); bool start(); void stop(); void cleanup(); diff --git a/modules/http/server/server.cpp b/modules/http/server/server.cpp index 5f852fe4..d002695d 100644 --- a/modules/http/server/server.cpp +++ b/modules/http/server/server.cpp @@ -38,6 +38,11 @@ bool Server::initialize(const network::SockAddr &bind_addr, int listen_backlog) return impl_->initialize(bind_addr, listen_backlog); } +bool Server::setTlsConfig(const network::TlsConfig &config) +{ + return impl_->setTlsConfig(config); +} + bool Server::start() { return impl_->start(); diff --git a/modules/http/server/server.h b/modules/http/server/server.h index 8ebae2e0..d7a5a715 100644 --- a/modules/http/server/server.h +++ b/modules/http/server/server.h @@ -22,6 +22,7 @@ #include #include +#include #include "../common.h" #include "../request.h" @@ -45,6 +46,9 @@ class Server { public: bool initialize(const network::SockAddr &bind_addr, int listen_backlog); + //! 设置 TLS 配置(必须在 initialize() 之前调用) + //! 需要 network_tls 模块支持,未链接时调用无效 + bool setTlsConfig(const network::TlsConfig &config); bool start(); void stop(); void cleanup(); diff --git a/modules/http/server/server_imp.cpp b/modules/http/server/server_imp.cpp index 56ac9497..c86a9c80 100644 --- a/modules/http/server/server_imp.cpp +++ b/modules/http/server/server_imp.cpp @@ -63,6 +63,11 @@ bool Server::Impl::initialize(const network::SockAddr &bind_addr, int listen_bac return true; } +bool Server::Impl::setTlsConfig(const network::TlsConfig &config) +{ + return tcp_server_.setTlsConfig(config); +} + bool Server::Impl::start() { if (tcp_server_.start()) { diff --git a/modules/http/server/server_imp.h b/modules/http/server/server_imp.h index c3f981e1..3a7d34ca 100644 --- a/modules/http/server/server_imp.h +++ b/modules/http/server/server_imp.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "server.h" @@ -48,6 +49,7 @@ class Server::Impl { public: bool initialize(const SockAddr &bind_addr, int listen_backlog); + bool setTlsConfig(const network::TlsConfig &config); bool start(); void stop(); void cleanup(); diff --git a/modules/main/Makefile b/modules/main/Makefile index 8ed0a5a9..b99bd5da 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -53,7 +53,6 @@ TEST_LDFLAGS := $(LDFLAGS) \ -ltbox_log \ -ltbox_util \ -ltbox_base \ - -lssl -lcrypto \ -lpthread -ldl ENABLE_SHARED_LIB = no diff --git a/modules/network/Makefile b/modules/network/Makefile index 57498426..4d828401 100644 --- a/modules/network/Makefile +++ b/modules/network/Makefile @@ -26,7 +26,6 @@ LIB_VERSION_Z = 1 HEAD_FILES = \ buffered_fd.h \ - buffered_ssl_fd.h \ byte_stream.h \ stdio_stream.h \ uart.h \ @@ -36,26 +35,22 @@ HEAD_FILES = \ udp_socket.h \ tcp_connection.h \ tcp_raw_connection.h \ - tcp_tls_connection.h \ tcp_acceptor.h \ tcp_raw_acceptor.h \ - tcp_tls_acceptor.h \ tcp_connector.h \ tcp_raw_connector.h \ - tcp_tls_connector.h \ tcp_client.h \ tcp_server.h \ tcp_factory.h \ tcp_raw_factory.h \ - tcp_tls_factory.h \ tls_config.h \ + tls_factory_entry.h \ net_if.h \ domain_name.h \ dns_request.h \ CPP_SRC_FILES = \ buffered_fd.cpp \ - buffered_ssl_fd.cpp \ stdio_stream.cpp \ uart.cpp \ socket_fd.cpp \ @@ -64,18 +59,15 @@ CPP_SRC_FILES = \ udp_socket.cpp \ tcp_connection.cpp \ tcp_raw_connection.cpp \ - tcp_tls_connection.cpp \ tcp_acceptor.cpp \ tcp_raw_acceptor.cpp \ - tcp_tls_acceptor.cpp \ tcp_connector.cpp \ tcp_raw_connector.cpp \ - tcp_tls_connector.cpp \ tcp_client.cpp \ tcp_server.cpp \ tcp_raw_factory.cpp \ - tcp_tls_factory.cpp \ tls_config.cpp \ + tls_factory_entry.cpp \ net_if.cpp \ dns_request.cpp \ @@ -91,7 +83,7 @@ TEST_CPP_SRC_FILES = \ net_if_test.cpp \ dns_request_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -ldl ENABLE_SHARED_LIB = no diff --git a/modules/network/stdio_stream.cpp b/modules/network/stdio_stream.cpp index 681defaf..57b52201 100644 --- a/modules/network/stdio_stream.cpp +++ b/modules/network/stdio_stream.cpp @@ -24,8 +24,8 @@ namespace tbox { namespace network { -StdinStream::StdinStream(event::Loop *wp_loop) : - buff_fd_(wp_loop) +StdinStream::StdinStream(event::Loop *wp_loop) + : buff_fd_(wp_loop) { } bool StdinStream::initialize() diff --git a/modules/network/tcp_acceptor.cpp b/modules/network/tcp_acceptor.cpp index 403b9b11..31b36856 100644 --- a/modules/network/tcp_acceptor.cpp +++ b/modules/network/tcp_acceptor.cpp @@ -35,8 +35,8 @@ namespace tbox { namespace network { -TcpAcceptor::TcpAcceptor(event::Loop *wp_loop) : - wp_loop_(wp_loop) +TcpAcceptor::TcpAcceptor(event::Loop *wp_loop) + : wp_loop_(wp_loop) { } TcpAcceptor::~TcpAcceptor() diff --git a/modules/network/tcp_client.cpp b/modules/network/tcp_client.cpp index 1b5693ae..8da49a5f 100644 --- a/modules/network/tcp_client.cpp +++ b/modules/network/tcp_client.cpp @@ -28,7 +28,7 @@ #include "tcp_connection.h" #include "tcp_factory.h" #include "tcp_raw_factory.h" -#include "tcp_tls_factory.h" +#include "tls_factory_entry.h" #undef MODULE_ID #define MODULE_ID "tbox.tcp" @@ -55,8 +55,8 @@ struct TcpClient::Data { int cb_level = 0; }; -TcpClient::TcpClient(event::Loop *wp_loop) : - d_(new Data) +TcpClient::TcpClient(event::Loop *wp_loop) + : d_(new Data) { TBOX_ASSERT(d_ != nullptr); @@ -78,23 +78,37 @@ TcpClient::~TcpClient() delete d_; } -void TcpClient::setTlsConfig(const TlsConfig &config) +bool TcpClient::setTlsConfig(const TlsConfig &config) { if (d_->state != State::kNone) { LogWarn("cannot set TLS config after initialization"); - return; + return false; } if (!config.isValid()) { LogWarn("invalid TLS config"); - return; + return false; } //! 替换 factory 和 connector + TcpFactory *tls_factory = CreateTlsFactory(TlsRole::kClient, config); + if (tls_factory == nullptr) { + LogWarn("failed to create TLS factory, TLS module may not be linked"); + return false; + } + + if (!tls_factory->initialize()) { + LogWarn("failed init TLS factory, config may invalid"); + delete tls_factory; + return false; + } + CHECK_DELETE_RESET_OBJ(d_->sp_connector); CHECK_DELETE_RESET_OBJ(d_->sp_factory); - d_->sp_factory = new TcpTlsFactory(config); + d_->sp_factory = tls_factory; d_->sp_connector = d_->sp_factory->createConnector(d_->wp_loop); + + return true; } bool TcpClient::initialize(const SockAddr &server_addr) diff --git a/modules/network/tcp_client.h b/modules/network/tcp_client.h index 44d352de..7b55d7aa 100644 --- a/modules/network/tcp_client.h +++ b/modules/network/tcp_client.h @@ -67,7 +67,7 @@ class TcpClient : public ByteStream { void setReconnectDelayCalcFunc(const ReconnectDelayCalc &func); //! 设置 TLS 配置(必须在 initialize() 之前调用) - void setTlsConfig(const TlsConfig &config); + bool setTlsConfig(const TlsConfig &config); bool start(); //!< 开始连接服务端 void stop(); //!< 如果没有连接则成,则停止连接;否则断开连接 diff --git a/modules/network/tcp_connection.cpp b/modules/network/tcp_connection.cpp index d481872b..94a30bd9 100644 --- a/modules/network/tcp_connection.cpp +++ b/modules/network/tcp_connection.cpp @@ -30,10 +30,10 @@ namespace network { using namespace std::placeholders; -TcpConnection::TcpConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) : - wp_loop_(wp_loop), - sp_buffered_fd_(nullptr), - peer_addr_(peer_addr) +TcpConnection::TcpConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) + : wp_loop_(wp_loop) + , sp_buffered_fd_(nullptr) + , peer_addr_(peer_addr) { //! sp_buffered_fd_ 由子类在构造函数中创建,然后调用 setupBufferedFd() } diff --git a/modules/network/tcp_connector.cpp b/modules/network/tcp_connector.cpp index 6cb25186..c5d4e9da 100644 --- a/modules/network/tcp_connector.cpp +++ b/modules/network/tcp_connector.cpp @@ -31,9 +31,9 @@ namespace tbox { namespace network { -TcpConnector::TcpConnector(event::Loop *wp_loop) : - wp_loop_(wp_loop), - reconn_delay_calc_func_([](int) {return 1;}) +TcpConnector::TcpConnector(event::Loop *wp_loop) + : wp_loop_(wp_loop) + , reconn_delay_calc_func_([](int) {return 1;}) { } TcpConnector::~TcpConnector() @@ -168,8 +168,9 @@ void TcpConnector::enterConnectingState() int conn_errno = conn_ret == 0 ? 0 : errno; //! 检查错误码 - if ((conn_errno == 0) || (conn_errno == EINPROGRESS) - || (conn_errno == EINTR) || (conn_errno == EISCONN)) { + if ((conn_errno == 0) || (conn_errno == EINPROGRESS) || + (conn_errno == EINTR) || (conn_errno == EISCONN)) + { //! 正常情况 sock_fd_ = std::move(new_sock_fd); diff --git a/modules/network/tcp_factory.h b/modules/network/tcp_factory.h index 8c2ed65c..b642efd0 100644 --- a/modules/network/tcp_factory.h +++ b/modules/network/tcp_factory.h @@ -33,6 +33,7 @@ class TcpAcceptor; class TcpFactory { public: virtual ~TcpFactory() {} + virtual bool initialize() = 0; virtual TcpConnector* createConnector(event::Loop *wp_loop) = 0; virtual TcpAcceptor* createAcceptor(event::Loop *wp_loop) = 0; diff --git a/modules/network/tcp_raw_acceptor.cpp b/modules/network/tcp_raw_acceptor.cpp index 54e275f8..4a034564 100644 --- a/modules/network/tcp_raw_acceptor.cpp +++ b/modules/network/tcp_raw_acceptor.cpp @@ -28,8 +28,8 @@ namespace tbox { namespace network { -TcpRawAcceptor::TcpRawAcceptor(event::Loop *wp_loop) : - TcpAcceptor(wp_loop) +TcpRawAcceptor::TcpRawAcceptor(event::Loop *wp_loop) + : TcpAcceptor(wp_loop) { } TcpConnection* TcpRawAcceptor::createConnection(event::Loop *wp_loop, SocketFd fd, diff --git a/modules/network/tcp_raw_connection.cpp b/modules/network/tcp_raw_connection.cpp index 53a8c174..7529efd8 100644 --- a/modules/network/tcp_raw_connection.cpp +++ b/modules/network/tcp_raw_connection.cpp @@ -27,8 +27,8 @@ namespace tbox { namespace network { -TcpRawConnection::TcpRawConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) : - TcpConnection(wp_loop, fd, peer_addr) +TcpRawConnection::TcpRawConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) + : TcpConnection(wp_loop, fd, peer_addr) { sp_buffered_fd_ = new BufferedFd(wp_loop_); sp_buffered_fd_->initialize(fd); diff --git a/modules/network/tcp_raw_connector.cpp b/modules/network/tcp_raw_connector.cpp index 201059ca..22048f3a 100644 --- a/modules/network/tcp_raw_connector.cpp +++ b/modules/network/tcp_raw_connector.cpp @@ -28,12 +28,11 @@ namespace tbox { namespace network { -TcpRawConnector::TcpRawConnector(event::Loop *wp_loop) : - TcpConnector(wp_loop) +TcpRawConnector::TcpRawConnector(event::Loop *wp_loop) + : TcpConnector(wp_loop) { } -TcpConnection* TcpRawConnector::createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) +TcpConnection* TcpRawConnector::createConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) { return new TcpRawConnection(wp_loop, fd, peer_addr); } diff --git a/modules/network/tcp_raw_connector.h b/modules/network/tcp_raw_connector.h index d5cb9d8a..6193c25f 100644 --- a/modules/network/tcp_raw_connector.h +++ b/modules/network/tcp_raw_connector.h @@ -32,8 +32,7 @@ class TcpRawConnector : public TcpConnector { explicit TcpRawConnector(event::Loop *wp_loop); protected: - virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) override; + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) override; virtual void onTcpConnected(SocketFd fd, const SockAddr &peer_addr) override; }; diff --git a/modules/network/tcp_raw_factory.h b/modules/network/tcp_raw_factory.h index 5d11d32a..1780c326 100644 --- a/modules/network/tcp_raw_factory.h +++ b/modules/network/tcp_raw_factory.h @@ -29,6 +29,7 @@ namespace network { //! 创建 TcpRawConnector 和 TcpRawAcceptor class TcpRawFactory : public TcpFactory { public: + virtual bool initialize() override { return true; } virtual TcpConnector* createConnector(event::Loop *wp_loop) override; virtual TcpAcceptor* createAcceptor(event::Loop *wp_loop) override; }; diff --git a/modules/network/tcp_server.cpp b/modules/network/tcp_server.cpp index b44675fd..bb7fbe16 100644 --- a/modules/network/tcp_server.cpp +++ b/modules/network/tcp_server.cpp @@ -30,7 +30,7 @@ #include "tcp_connection.h" #include "tcp_factory.h" #include "tcp_raw_factory.h" -#include "tcp_tls_factory.h" +#include "tls_factory_entry.h" #undef MODULE_ID #define MODULE_ID "tbox.tcp" @@ -81,23 +81,37 @@ TcpServer::~TcpServer() delete d_; } -void TcpServer::setTlsConfig(const TlsConfig &config) +bool TcpServer::setTlsConfig(const TlsConfig &config) { if (d_->state != State::kNone) { LogWarn("cannot set TLS config after initialization"); - return; + return false; } if (!config.isValid()) { LogWarn("invalid TLS config"); - return; + return false; } //! 替换 factory 和 acceptor + TcpFactory *tls_factory = CreateTlsFactory(TlsRole::kServer, config); + if (tls_factory == nullptr) { + LogWarn("failed to create TLS factory, TLS module may not be linked"); + return false; + } + + if (!tls_factory->initialize()) { + LogWarn("failed init TLS factory, config may invalid"); + delete tls_factory; + return false; + } + CHECK_DELETE_RESET_OBJ(d_->sp_acceptor); CHECK_DELETE_RESET_OBJ(d_->sp_factory); - d_->sp_factory = new TcpTlsFactory(config); + d_->sp_factory = tls_factory; d_->sp_acceptor = d_->sp_factory->createAcceptor(d_->wp_loop); + + return true; } bool TcpServer::initialize(const SockAddr &bind_addr, int listen_backlog) diff --git a/modules/network/tcp_server.h b/modules/network/tcp_server.h index de732863..55f53223 100644 --- a/modules/network/tcp_server.h +++ b/modules/network/tcp_server.h @@ -58,7 +58,7 @@ class TcpServer { bool initialize(const SockAddr &bind_addr, int listen_backlog); //! 设置 TLS 配置(必须在 initialize() 之前调用) - void setTlsConfig(const TlsConfig &config); + bool setTlsConfig(const TlsConfig &config); using ConnectedCallback = std::function; using DisconnectedCallback = std::function; diff --git a/modules/network/tls_config.cpp b/modules/network/tls_config.cpp index 9745683a..d559a4d8 100644 --- a/modules/network/tls_config.cpp +++ b/modules/network/tls_config.cpp @@ -2,7 +2,7 @@ * .============. * // M A K E / \ * // C++ DEV / \ - * // E A S Y / \/ \ + * // E A S Y / \/ \ * ++ ----------. \/\ . * \\ \ \ /\ / * \\ \ \ / @@ -26,13 +26,19 @@ namespace network { bool TlsConfig::isValid() const { - //! 至少需要有证书和密钥文件(server 场景)或 CA 文件(client 场景) + //! cert_file 和 key_file 必须同时设置或同时为空 if (!cert_file.empty() && key_file.empty()) { LogErr("cert_file is set but key_file is not"); return false; } - if (key_file.empty() && cert_file.empty() && ca_file.empty() && ca_path.empty()) { - LogErr("no TLS configuration provided"); + if (!key_file.empty() && cert_file.empty()) { + LogErr("key_file is set but cert_file is not"); + return false; + } + //! 当 verify_peer=false 时,允许不提供任何证书(client 场景,类似 curl -k) + //! 但当 verify_peer=true 时,必须有 CA 证书来验证对端 + if (verify_peer && ca_file.empty() && ca_path.empty()) { + LogErr("verify_peer requires ca_file or ca_path"); return false; } return true; diff --git a/modules/network/tls_config.h b/modules/network/tls_config.h index 3f0f8389..9a4ec20d 100644 --- a/modules/network/tls_config.h +++ b/modules/network/tls_config.h @@ -30,16 +30,15 @@ struct TlsConfig { //! 通用配置 std::string ca_file; //!< CA 证书文件路径 std::string ca_path; //!< CA 证书目录路径 + bool verify_peer = true; //!< 是否验证对端证书 int verify_depth = 1; //!< 证书链验证深度 - //! Server 专用配置 - std::string cert_file; //!< 服务器/客户端证书文件 - std::string key_file; //!< 服务器/客户端私钥文件 - - //! Client 专用配置(用于双向 TLS) - std::string client_cert_file; //!< 客户端证书文件(可选) - std::string client_key_file; //!< 客户端私钥文件(可选) + //! 本端证书和私钥 + //! Server 场景:必须设置,用于向 client 出示证书 + //! Client 场景:可选设置,用于双向 TLS(mTLS)向 server 出示证书 + std::string cert_file; //!< 本端证书文件 + std::string key_file; //!< 本端私钥文件 //! Client SNI 配置 std::string hostname; //!< 用于 SNI (Server Name Indication) 的主机名 diff --git a/modules/network/tls_factory_entry.cpp b/modules/network/tls_factory_entry.cpp new file mode 100644 index 00000000..17a897fe --- /dev/null +++ b/modules/network/tls_factory_entry.cpp @@ -0,0 +1,34 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S E / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "tls_factory_entry.h" + +#include + +namespace tbox { +namespace network { + +__attribute__((weak)) TcpFactory* CreateTlsFactory(TlsRole role, const TlsConfig &config) +{ + LogWarn("TLS module not linked, cannot create TLS factory"); + return nullptr; +} + +} +} diff --git a/modules/network/tls_factory_entry.h b/modules/network/tls_factory_entry.h new file mode 100644 index 00000000..a7466356 --- /dev/null +++ b/modules/network/tls_factory_entry.h @@ -0,0 +1,43 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S E / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_NETWORK_TLS_FACTORY_ENTRY_H_20260626 +#define TBOX_NETWORK_TLS_FACTORY_ENTRY_H_20260626 + +#include "tls_config.h" +#include "tcp_factory.h" + +namespace tbox { +namespace network { + +//! TLS 角色 +enum class TlsRole { + kClient, //!< 作为 TLS Client(用于 TcpClient) + kServer, //!< 作为 TLS Server(用于 TcpServer) +}; + +//! TLS 工厂创建入口函数 +//! 默认为弱实现(返回 nullptr),由 network_tls 模块提供强实现 +//! 当链接了 libtbox_network_tls 时,强符号覆盖弱符号,TLS 功能可用 +//! 当未链接 network_tls 时,弱符号生效,调用将返回 nullptr 并打印警告 +extern TcpFactory* CreateTlsFactory(TlsRole role, const TlsConfig &config); + +} +} +#endif //TBOX_NETWORK_TLS_FACTORY_ENTRY_H_20260626 diff --git a/modules/network_tls/Makefile b/modules/network_tls/Makefile new file mode 100644 index 00000000..65203d38 --- /dev/null +++ b/modules/network_tls/Makefile @@ -0,0 +1,54 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S E / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +PROJECT = network_tls +LIB_NAME = network_tls +LIB_VERSION_X = 0 +LIB_VERSION_Y = 0 +LIB_VERSION_Z = 1 + +HEAD_FILES = \ + buffered_ssl_fd.h \ + tcp_tls_factory.h \ + tcp_tls_connection.h \ + tcp_tls_connector.h \ + tcp_tls_acceptor.h \ + +CPP_SRC_FILES = \ + tls_factory_entry.cpp \ + buffered_ssl_fd.cpp \ + tcp_tls_factory.cpp \ + tcp_tls_connection.cpp \ + tcp_tls_connector.cpp \ + tcp_tls_acceptor.cpp \ + +CXXFLAGS := -DMODULE_ID='"tbox.network_tls"' $(CXXFLAGS) + +TEST_CPP_SRC_FILES = \ + $(CPP_SRC_FILES) \ + +TEST_LDFLAGS := $(LDFLAGS) \ + -ltbox_network -ltbox_network_tls \ + -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base \ + -lssl -lcrypto -ldl + +ENABLE_SHARED_LIB = no + +include $(TOP_DIR)/mk/lib_tbox_common.mk diff --git a/modules/network/buffered_ssl_fd.cpp b/modules/network_tls/buffered_ssl_fd.cpp similarity index 89% rename from modules/network/buffered_ssl_fd.cpp rename to modules/network_tls/buffered_ssl_fd.cpp index 70247b7c..54ccaf8c 100644 --- a/modules/network/buffered_ssl_fd.cpp +++ b/modules/network_tls/buffered_ssl_fd.cpp @@ -2,7 +2,7 @@ * .============. * // M A K E / \ * // C++ DEV / \ - * // E A S Y / \/ \ + * // E A S E / \/ \ * ++ ----------. \/\ . * \\ \ \ /\ / * \\ \ \ / @@ -106,14 +106,27 @@ ssize_t BufferedSslFd::doRead(void *buffer, size_t size) if (ssl_error == SSL_ERROR_SYSCALL) { //! 系统调用错误 if (errno == 0) { - //! EOF:对端关闭了连接 + //! EOF:对端关闭了连接(未发送 close_notify) return 0; } //! 其他系统错误,errno 已由系统设置 return -1; } - //! 其他 SSL 错误 + if (ssl_error == SSL_ERROR_SSL) { + //! SSL 协议错误 + //! 当 errno == 0 时,通常是对端非正常关闭连接(未发送 close_notify) + //! 这在实践中很常见,应该视为正常断开,而不是错误 + if (errno == 0) { + return 0; + } + //! 其他 SSL 协议错误 + LogWarn("SSL_read error: SSL_ERROR_SSL, errno:%d", errno); + errno = ECONNRESET; + return -1; + } + + //! 其他未知 SSL 错误 LogWarn("SSL_read error: %d, errno:%d", ssl_error, errno); errno = ECONNRESET; //! 将 SSL 错误映射为连接错误 return -1; diff --git a/modules/network/buffered_ssl_fd.h b/modules/network_tls/buffered_ssl_fd.h similarity index 98% rename from modules/network/buffered_ssl_fd.h rename to modules/network_tls/buffered_ssl_fd.h index 006178fa..02c65c9c 100644 --- a/modules/network/buffered_ssl_fd.h +++ b/modules/network_tls/buffered_ssl_fd.h @@ -22,7 +22,7 @@ #include -#include "buffered_fd.h" +#include namespace tbox { namespace network { diff --git a/modules/network/tcp_tls_acceptor.cpp b/modules/network_tls/tcp_tls_acceptor.cpp similarity index 81% rename from modules/network/tcp_tls_acceptor.cpp rename to modules/network_tls/tcp_tls_acceptor.cpp index cfb5c5e0..992a5ee3 100644 --- a/modules/network/tcp_tls_acceptor.cpp +++ b/modules/network_tls/tcp_tls_acceptor.cpp @@ -27,15 +27,15 @@ #include #undef MODULE_ID -#define MODULE_ID "tbox.tcp" +#define MODULE_ID "tbox.tcp_tls" namespace tbox { namespace network { -TcpTlsAcceptor::TcpTlsAcceptor(event::Loop *wp_loop, SSL_CTX *ssl_ctx, const TlsConfig &tls_config) : - TcpAcceptor(wp_loop), - ssl_ctx_(ssl_ctx), - tls_config_(tls_config) +TcpTlsAcceptor::TcpTlsAcceptor(event::Loop *wp_loop, SSL_CTX *ssl_ctx, const TlsConfig &tls_config) + : TcpAcceptor(wp_loop) + , ssl_ctx_(ssl_ctx) + , tls_config_(tls_config) { } TcpTlsAcceptor::~TcpTlsAcceptor() @@ -131,13 +131,13 @@ void TcpTlsAcceptor::onSslHandshakeEvent(short events) int ssl_error = SSL_get_error(handshake_ssl_, ret); if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) { //! 需要继续等待 + //! kOneshot 事件触发后已自动 disable,可以直接 reinitialize,无需重新创建 FdEvent + //! 这避免了在回调中删除 FdEvent 导致的 assert 失败 short next_events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; - - CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); - sp_handshake_ev_ = wp_loop_->newFdEvent("TcpTlsAcceptor::sp_handshake_ev_"); sp_handshake_ev_->initialize(handshake_fd_.get(), next_events, event::Event::Mode::kOneshot); - sp_handshake_ev_->setCallback(std::bind(&TcpTlsAcceptor::onSslHandshakeEvent, this, std::placeholders::_1)); sp_handshake_ev_->enable(); + + LogDbg("SSL accept continue, waiting for %s", (ssl_error == SSL_ERROR_WANT_READ) ? "READ" : "WRITE"); } else { //! 握手失败 LogErr("SSL accept fail, error:%d", ssl_error); @@ -150,7 +150,16 @@ void TcpTlsAcceptor::onSslHandshakeSuccess() RECORD_SCOPE(); //! 清理握手相关的 FdEvent - CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + //! 不能在回调中直接删除 FdEvent(cb_level_ > 0),需要延后删除 + if (sp_handshake_ev_ != nullptr) { + sp_handshake_ev_->disable(); + event::FdEvent *tmp = nullptr; + std::swap(tmp, sp_handshake_ev_); + wp_loop_->runNext( + [tmp] { delete tmp; }, + "TcpTlsAcceptor::onSslHandshakeSuccess, delete ev" + ); + } //! 将 SSL 和 fd 从握手状态转移到 TcpTlsConnection SSL *ssl = handshake_ssl_; @@ -178,7 +187,16 @@ void TcpTlsAcceptor::onSslHandshakeSuccess() void TcpTlsAcceptor::onSslHandshakeFail() { //! 清理握手相关的资源 - CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + //! 不能在回调中直接删除 FdEvent(cb_level_ > 0),需要延后删除 + if (sp_handshake_ev_ != nullptr) { + sp_handshake_ev_->disable(); + event::FdEvent *tmp = nullptr; + std::swap(tmp, sp_handshake_ev_); + wp_loop_->runNext( + [tmp] { delete tmp; }, + "TcpTlsAcceptor::onSslHandshakeFail, delete ev" + ); + } SSL_free(handshake_ssl_); handshake_ssl_ = nullptr; diff --git a/modules/network/tcp_tls_acceptor.h b/modules/network_tls/tcp_tls_acceptor.h similarity index 94% rename from modules/network/tcp_tls_acceptor.h rename to modules/network_tls/tcp_tls_acceptor.h index cdb25748..1b2f782d 100644 --- a/modules/network/tcp_tls_acceptor.h +++ b/modules/network_tls/tcp_tls_acceptor.h @@ -22,9 +22,9 @@ #include -#include "tcp_acceptor.h" -#include "tls_config.h" -#include "socket_fd.h" +#include +#include +#include namespace tbox { namespace network { diff --git a/modules/network/tcp_tls_connection.cpp b/modules/network_tls/tcp_tls_connection.cpp similarity index 94% rename from modules/network/tcp_tls_connection.cpp rename to modules/network_tls/tcp_tls_connection.cpp index 7712200f..bb7c866c 100644 --- a/modules/network/tcp_tls_connection.cpp +++ b/modules/network_tls/tcp_tls_connection.cpp @@ -22,14 +22,14 @@ #include #undef MODULE_ID -#define MODULE_ID "tbox.tcp" +#define MODULE_ID "tbox.tcp_tls" namespace tbox { namespace network { -TcpTlsConnection::TcpTlsConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr, SSL *ssl) : - TcpConnection(wp_loop, fd, peer_addr), - ssl_(ssl) +TcpTlsConnection::TcpTlsConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr, SSL *ssl) + : TcpConnection(wp_loop, fd, peer_addr) + , ssl_(ssl) { //! 创建 BufferedSslFd 并初始化 auto *ssl_fd = new BufferedSslFd(wp_loop_); diff --git a/modules/network/tcp_tls_connection.h b/modules/network_tls/tcp_tls_connection.h similarity index 97% rename from modules/network/tcp_tls_connection.h rename to modules/network_tls/tcp_tls_connection.h index 07699931..106f2340 100644 --- a/modules/network/tcp_tls_connection.h +++ b/modules/network_tls/tcp_tls_connection.h @@ -22,7 +22,7 @@ #include -#include "tcp_connection.h" +#include #include "buffered_ssl_fd.h" namespace tbox { diff --git a/modules/network/tcp_tls_connector.cpp b/modules/network_tls/tcp_tls_connector.cpp similarity index 84% rename from modules/network/tcp_tls_connector.cpp rename to modules/network_tls/tcp_tls_connector.cpp index 560c41af..f7ae0472 100644 --- a/modules/network/tcp_tls_connector.cpp +++ b/modules/network_tls/tcp_tls_connector.cpp @@ -2,7 +2,7 @@ * .============. * // M A K E / \ * // C++ DEV / \ - * // E A S Y / \/ \ + * // E A S E / \/ \ * ++ ----------. \/\ . * \\ \ \ /\ / * \\ \ \ / @@ -27,7 +27,7 @@ #include #undef MODULE_ID -#define MODULE_ID "tbox.tcp" +#define MODULE_ID "tbox.tcp_tls" namespace tbox { namespace network { @@ -140,13 +140,13 @@ void TcpTlsConnector::onSslHandshakeEvent(short events) int ssl_error = SSL_get_error(handshake_ssl_, ret); if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) { //! 需要继续等待 + //! kOneshot 事件触发后已自动 disable,可以直接 reinitialize,无需重新创建 FdEvent + //! 这避免了在回调中删除 FdEvent 导致的 assert 失败 short next_events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; - - CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); - sp_handshake_ev_ = wp_loop_->newFdEvent("TcpTlsConnector::sp_handshake_ev_"); sp_handshake_ev_->initialize(handshake_fd_.get(), next_events, event::Event::Mode::kOneshot); - sp_handshake_ev_->setCallback(std::bind(&TcpTlsConnector::onSslHandshakeEvent, this, std::placeholders::_1)); sp_handshake_ev_->enable(); + + LogDbg("SSL handshake continue, waiting for %s", (ssl_error == SSL_ERROR_WANT_READ) ? "READ" : "WRITE"); } else { //! 握手失败 LogErr("SSL handshake fail, error:%d", ssl_error); @@ -159,7 +159,16 @@ void TcpTlsConnector::onSslHandshakeSuccess() RECORD_SCOPE(); //! 清理握手相关的 FdEvent - CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + //! 不能在回调中直接删除 FdEvent(cb_level_ > 0),需要延后删除 + if (sp_handshake_ev_ != nullptr) { + sp_handshake_ev_->disable(); + event::FdEvent *tmp = nullptr; + std::swap(tmp, sp_handshake_ev_); + wp_loop_->runNext( + [tmp] { delete tmp; }, + "TcpTlsConnector::onSslHandshakeSuccess, delete ev" + ); + } //! 将 SSL 和 fd 从握手状态转移到 TcpTlsConnection SSL *ssl = handshake_ssl_; @@ -188,7 +197,16 @@ void TcpTlsConnector::onSslHandshakeSuccess() void TcpTlsConnector::onSslHandshakeFail() { //! 清理握手相关的资源 - CHECK_DELETE_RESET_OBJ(sp_handshake_ev_); + //! 不能在回调中直接删除 FdEvent(cb_level_ > 0),需要延后删除 + if (sp_handshake_ev_ != nullptr) { + sp_handshake_ev_->disable(); + event::FdEvent *tmp = nullptr; + std::swap(tmp, sp_handshake_ev_); + wp_loop_->runNext( + [tmp] { delete tmp; }, + "TcpTlsConnector::onSslHandshakeFail, delete ev" + ); + } SSL_free(handshake_ssl_); handshake_ssl_ = nullptr; diff --git a/modules/network/tcp_tls_connector.h b/modules/network_tls/tcp_tls_connector.h similarity index 93% rename from modules/network/tcp_tls_connector.h rename to modules/network_tls/tcp_tls_connector.h index 480cf191..1612dba1 100644 --- a/modules/network/tcp_tls_connector.h +++ b/modules/network_tls/tcp_tls_connector.h @@ -2,7 +2,7 @@ * .============. * // M A K E / \ * // C++ DEV / \ - * // E A S Y / \/ \ + * // E A S E / \/ \ * ++ ----------. \/\ . * \\ \ \ /\ / * \\ \ \ / @@ -22,9 +22,9 @@ #include -#include "tcp_connector.h" -#include "tls_config.h" -#include "socket_fd.h" +#include +#include +#include namespace tbox { namespace network { diff --git a/modules/network/tcp_tls_factory.cpp b/modules/network_tls/tcp_tls_factory.cpp similarity index 38% rename from modules/network/tcp_tls_factory.cpp rename to modules/network_tls/tcp_tls_factory.cpp index f499b194..9cc37096 100644 --- a/modules/network/tcp_tls_factory.cpp +++ b/modules/network_tls/tcp_tls_factory.cpp @@ -2,7 +2,7 @@ * .============. * // M A K E / \ * // C++ DEV / \ - * // E A S Y / \/ \ + * // E A S Y / \/ \ * ++ ----------. \/\ . * \\ \ \ /\ / * \\ \ \ / @@ -25,41 +25,54 @@ #include #include +#include +#include #undef MODULE_ID -#define MODULE_ID "tbox.tcp" +#define MODULE_ID "tbox.tcp_tls" namespace tbox { namespace network { -TcpTlsFactory::TcpTlsFactory(const TlsConfig &config) : - tls_config_(config) +namespace { +//! 加载证书与私钥到 SSL_CTX +//! 成功返回 true,失败返回 false +bool LoadCertAndKey(SSL_CTX *ctx, const std::string &cert_file, const std::string &key_file) { - //! 创建 client 端 SSL_CTX - client_ssl_ctx_ = createClientSslCtx(); - //! 创建 server 端 SSL_CTX - server_ssl_ctx_ = createServerSslCtx(); -} - -TcpTlsFactory::~TcpTlsFactory() -{ - if (client_ssl_ctx_ != nullptr) - SSL_CTX_free(client_ssl_ctx_); - if (server_ssl_ctx_ != nullptr) - SSL_CTX_free(server_ssl_ctx_); -} - -TcpConnector* TcpTlsFactory::createConnector(event::Loop *wp_loop) -{ - return new TcpTlsConnector(wp_loop, client_ssl_ctx_, tls_config_); + if (SSL_CTX_use_certificate_file(ctx, cert_file.c_str(), SSL_FILETYPE_PEM) != 1) { + LogErr("SSL_CTX_use_certificate_file fail, file:%s", cert_file.c_str()); + ERR_print_errors_fp(stderr); + return false; + } + if (SSL_CTX_use_PrivateKey_file(ctx, key_file.c_str(), SSL_FILETYPE_PEM) != 1) { + LogErr("SSL_CTX_use_PrivateKey_file fail, file:%s", key_file.c_str()); + ERR_print_errors_fp(stderr); + return false; + } + if (SSL_CTX_check_private_key(ctx) != 1) { + LogErr("SSL_CTX_check_private_key fail"); + return false; + } + return true; } -TcpAcceptor* TcpTlsFactory::createAcceptor(event::Loop *wp_loop) +//! 加载 CA 证书到 SSL_CTX +//! 成功返回 true,失败返回 false +bool LoadCaCert(SSL_CTX *ctx, const TlsConfig &tls_config) { - return new TcpTlsAcceptor(wp_loop, server_ssl_ctx_, tls_config_); + const char *ca_file = tls_config.ca_file.empty() ? nullptr : tls_config.ca_file.c_str(); + const char *ca_path = tls_config.ca_path.empty() ? nullptr : tls_config.ca_path.c_str(); + + if (SSL_CTX_load_verify_locations(ctx, ca_file, ca_path) != 1) { + LogErr("SSL_CTX_load_verify_locations fail, ca_file:%s, ca_path:%s", + tls_config.ca_file.c_str(), tls_config.ca_path.c_str()); + ERR_print_errors_fp(stderr); + return false; + } + return true; } -SSL_CTX* TcpTlsFactory::createClientSslCtx() const +SSL_CTX* CreateClientSslCtx(const TlsConfig &tls_config) { SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); if (ctx == nullptr) { @@ -67,30 +80,22 @@ SSL_CTX* TcpTlsFactory::createClientSslCtx() const return nullptr; } + ScopeExitActionGuard guard([ctx] { SSL_CTX_free(ctx); }); + //! 设置最低 TLS 版本为 1.2 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); //! 加载 CA 证书(用于验证 server) - if (!tls_config_.ca_file.empty() || !tls_config_.ca_path.empty()) { - const char *ca_file = tls_config_.ca_file.empty() ? nullptr : tls_config_.ca_file.c_str(); - const char *ca_path = tls_config_.ca_path.empty() ? nullptr : tls_config_.ca_path.c_str(); - - if (SSL_CTX_load_verify_locations(ctx, ca_file, ca_path) != 1) { - LogErr("SSL_CTX_load_verify_locations fail, ca_file:%s, ca_path:%s", - tls_config_.ca_file.c_str(), tls_config_.ca_path.c_str()); - ERR_print_errors_fp(stderr); - SSL_CTX_free(ctx); + if (!tls_config.ca_file.empty() || !tls_config.ca_path.empty()) { + if (!LoadCaCert(ctx, tls_config)) return nullptr; - } - if (tls_config_.verify_peer) { + if (tls_config.verify_peer) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); - } - } else if (tls_config_.verify_peer) { + } else if (tls_config.verify_peer) { //! 使用系统默认 CA 证书 if (SSL_CTX_set_default_verify_paths(ctx) != 1) { LogErr("SSL_CTX_set_default_verify_paths fail"); - SSL_CTX_free(ctx); return nullptr; } SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); @@ -98,31 +103,18 @@ SSL_CTX* TcpTlsFactory::createClientSslCtx() const SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); } - //! 加载 client 证书和密钥(可选,用于双向 TLS) - if (!tls_config_.client_cert_file.empty() && !tls_config_.client_key_file.empty()) { - if (SSL_CTX_use_certificate_file(ctx, tls_config_.client_cert_file.c_str(), SSL_FILETYPE_PEM) != 1) { - LogErr("SSL_CTX_use_certificate_file fail, file:%s", tls_config_.client_cert_file.c_str()); - ERR_print_errors_fp(stderr); - SSL_CTX_free(ctx); - return nullptr; - } - if (SSL_CTX_use_PrivateKey_file(ctx, tls_config_.client_key_file.c_str(), SSL_FILETYPE_PEM) != 1) { - LogErr("SSL_CTX_use_PrivateKey_file fail, file:%s", tls_config_.client_key_file.c_str()); - ERR_print_errors_fp(stderr); - SSL_CTX_free(ctx); - return nullptr; - } - if (SSL_CTX_check_private_key(ctx) != 1) { - LogErr("SSL_CTX_check_private_key fail"); - SSL_CTX_free(ctx); + //! 加载本端证书和密钥(可选,用于双向 TLS) + if (!tls_config.cert_file.empty() && !tls_config.key_file.empty()) { + if (!LoadCertAndKey(ctx, tls_config.cert_file, tls_config.key_file)) { return nullptr; } } + guard.cancel(); return ctx; } -SSL_CTX* TcpTlsFactory::createServerSslCtx() const +SSL_CTX* CreateServerSslCtx(const TlsConfig &tls_config) { SSL_CTX *ctx = SSL_CTX_new(TLS_server_method()); if (ctx == nullptr) { @@ -130,54 +122,73 @@ SSL_CTX* TcpTlsFactory::createServerSslCtx() const return nullptr; } + ScopeExitActionGuard guard([ctx] { SSL_CTX_free(ctx); }); + //! 设置最低 TLS 版本为 1.2 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); - //! 加载 server 证书和密钥 - if (!tls_config_.cert_file.empty() && !tls_config_.key_file.empty()) { - if (SSL_CTX_use_certificate_file(ctx, tls_config_.cert_file.c_str(), SSL_FILETYPE_PEM) != 1) { - LogErr("SSL_CTX_use_certificate_file fail, file:%s", tls_config_.cert_file.c_str()); - ERR_print_errors_fp(stderr); - SSL_CTX_free(ctx); + //! 加载本端证书和密钥(Server 必须设置) + if (!tls_config.cert_file.empty() && !tls_config.key_file.empty()) { + if (!LoadCertAndKey(ctx, tls_config.cert_file, tls_config.key_file)) return nullptr; - } - if (SSL_CTX_use_PrivateKey_file(ctx, tls_config_.key_file.c_str(), SSL_FILETYPE_PEM) != 1) { - LogErr("SSL_CTX_use_PrivateKey_file fail, file:%s", tls_config_.key_file.c_str()); - ERR_print_errors_fp(stderr); - SSL_CTX_free(ctx); - return nullptr; - } - if (SSL_CTX_check_private_key(ctx) != 1) { - LogErr("SSL_CTX_check_private_key fail"); - SSL_CTX_free(ctx); - return nullptr; - } + } else { LogErr("server cert_file and key_file must be set"); - SSL_CTX_free(ctx); return nullptr; } //! 加载 CA 证书(可选,用于验证 client - 双向 TLS) - if (!tls_config_.ca_file.empty() || !tls_config_.ca_path.empty()) { - const char *ca_file = tls_config_.ca_file.empty() ? nullptr : tls_config_.ca_file.c_str(); - const char *ca_path = tls_config_.ca_path.empty() ? nullptr : tls_config_.ca_path.c_str(); - - if (SSL_CTX_load_verify_locations(ctx, ca_file, ca_path) != 1) { - LogErr("SSL_CTX_load_verify_locations fail"); - ERR_print_errors_fp(stderr); - SSL_CTX_free(ctx); + if (!tls_config.ca_file.empty() || !tls_config.ca_path.empty()) { + if (!LoadCaCert(ctx, tls_config)) return nullptr; - } - if (tls_config_.verify_peer) { + if (tls_config.verify_peer) { SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); - SSL_CTX_set_verify_depth(ctx, tls_config_.verify_depth); + SSL_CTX_set_verify_depth(ctx, tls_config.verify_depth); } } + guard.cancel(); return ctx; } +} + +/////////////////////////////////////////////////////// + +TcpTlsFactory::TcpTlsFactory(TlsRole role, const TlsConfig &config) + : role_(role) + , tls_config_(config) +{ } + +TcpTlsFactory::~TcpTlsFactory() +{ + if (ssl_ctx_ != nullptr) + SSL_CTX_free(ssl_ctx_); +} + +bool TcpTlsFactory::initialize() +{ + if (role_ == TlsRole::kClient) { + ssl_ctx_ = CreateClientSslCtx(tls_config_); + } else if (role_ == TlsRole::kServer) { + ssl_ctx_ = CreateServerSslCtx(tls_config_); + } + return ssl_ctx_ != nullptr; +} + +TcpConnector* TcpTlsFactory::createConnector(event::Loop *wp_loop) +{ + TBOX_ASSERT(role_ == TlsRole::kClient); + TBOX_ASSERT(ssl_ctx_ != nullptr); + return new TcpTlsConnector(wp_loop, ssl_ctx_, tls_config_); +} + +TcpAcceptor* TcpTlsFactory::createAcceptor(event::Loop *wp_loop) +{ + TBOX_ASSERT(role_ == TlsRole::kServer); + TBOX_ASSERT(ssl_ctx_ != nullptr); + return new TcpTlsAcceptor(wp_loop, ssl_ctx_, tls_config_); +} } } diff --git a/modules/network/tcp_tls_factory.h b/modules/network_tls/tcp_tls_factory.h similarity index 67% rename from modules/network/tcp_tls_factory.h rename to modules/network_tls/tcp_tls_factory.h index 6cd6df31..e842b36a 100644 --- a/modules/network/tcp_tls_factory.h +++ b/modules/network_tls/tcp_tls_factory.h @@ -2,7 +2,7 @@ * .============. * // M A K E / \ * // C++ DEV / \ - * // E A S Y / \/ \ + * // E A S Y / \/ \ * ++ ----------. \/\ . * \\ \ \ /\ / * \\ \ \ / @@ -22,30 +22,30 @@ #include -#include "tcp_factory.h" -#include "tls_config.h" +#include +#include +#include namespace tbox { namespace network { //! TLS 工厂 -//! 创建 TcpTlsConnector 和 TcpTlsAcceptor -//! 管理 SSL_CTX 的生命周期(分别创建 client 和 server 的 SSL_CTX) +//! 根据 TlsRole 创建对应的 SSL_CTX,仅持有本端所需的那一个 +//! kClient 角色:创建 client SSL_CTX,仅支持 createConnector +//! kServer 角色:创建 server SSL_CTX,仅支持 createAcceptor class TcpTlsFactory : public TcpFactory { public: - explicit TcpTlsFactory(const TlsConfig &config); + TcpTlsFactory(TlsRole role, const TlsConfig &config); ~TcpTlsFactory(); + virtual bool initialize() override; virtual TcpConnector* createConnector(event::Loop *wp_loop) override; virtual TcpAcceptor* createAcceptor(event::Loop *wp_loop) override; private: - SSL_CTX* createClientSslCtx() const; - SSL_CTX* createServerSslCtx() const; - + TlsRole role_; TlsConfig tls_config_; - SSL_CTX *client_ssl_ctx_ = nullptr; //!< client 端 SSL_CTX - SSL_CTX *server_ssl_ctx_ = nullptr; //!< server 端 SSL_CTX + SSL_CTX *ssl_ctx_ = nullptr; }; } diff --git a/modules/network_tls/tls_factory_entry.cpp b/modules/network_tls/tls_factory_entry.cpp new file mode 100644 index 00000000..1ad97bfe --- /dev/null +++ b/modules/network_tls/tls_factory_entry.cpp @@ -0,0 +1,32 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S E / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2018 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include +#include "tcp_tls_factory.h" + +namespace tbox { +namespace network { + +TcpFactory* CreateTlsFactory(TlsRole role, const TlsConfig &config) +{ + return new TcpTlsFactory(role, config); +} + +} +} diff --git a/modules/run/Makefile b/modules/run/Makefile index 92758a95..a92f34fb 100644 --- a/modules/run/Makefile +++ b/modules/run/Makefile @@ -35,9 +35,12 @@ LDFLAGS += \ -ltbox_log \ -ltbox_util \ -ltbox_base \ - -lssl -lcrypto \ -lpthread \ -ldl \ -rdynamic +ifeq ($(findstring network_tls,$(MODULES)),network_tls) +LDFLAGS += -Wl,--whole-archive -ltbox_network_tls -Wl,--no-whole-archive -lssl -lcrypto +endif + include $(TOP_DIR)/mk/exe_common.mk diff --git a/modules/terminal/Makefile b/modules/terminal/Makefile index bfcf83f2..89f1dc39 100644 --- a/modules/terminal/Makefile +++ b/modules/terminal/Makefile @@ -60,7 +60,7 @@ TEST_CPP_SRC_FILES = \ impl/key_event_scanner.cpp \ impl/key_event_scanner_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_network -ltbox_event -ltbox_util -ltbox_base -ldl ENABLE_SHARED_LIB = no include $(TOP_DIR)/mk/lib_tbox_common.mk diff --git a/modules/websocket/Makefile b/modules/websocket/Makefile index 813f4d39..07e04a64 100644 --- a/modules/websocket/Makefile +++ b/modules/websocket/Makefile @@ -46,7 +46,7 @@ TEST_CPP_SRC_FILES = \ ws_frame_parser_test.cpp \ ws_frame_builder_test.cpp \ -TEST_LDFLAGS := $(LDFLAGS) -ltbox_crypto -ltbox_http -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -lssl -lcrypto -ldl +TEST_LDFLAGS := $(LDFLAGS) -ltbox_crypto -ltbox_http -ltbox_network -ltbox_log -ltbox_eventx -ltbox_event -ltbox_util -ltbox_base -ldl ENABLE_SHARED_LIB = no diff --git a/modules/websocket/client/ws_client.cpp b/modules/websocket/client/ws_client.cpp index 3f5bb63a..b22c4bf3 100644 --- a/modules/websocket/client/ws_client.cpp +++ b/modules/websocket/client/ws_client.cpp @@ -91,6 +91,11 @@ void WsClient::setReconnectDelayCalcFunc(const ReconnectDelayCalc &func) impl_->setReconnectDelayCalcFunc(func); } +void WsClient::setTlsConfig(const network::TlsConfig &config) +{ + impl_->setTlsConfig(config); +} + bool WsClient::send(const std::string &text) { return impl_->send(text); diff --git a/modules/websocket/client/ws_client.h b/modules/websocket/client/ws_client.h index a56b69d2..69cb89ef 100644 --- a/modules/websocket/client/ws_client.h +++ b/modules/websocket/client/ws_client.h @@ -22,6 +22,7 @@ #include #include +#include #include #include "../ws_frame.h" @@ -75,6 +76,10 @@ class WsClient { //! 设置自定义重连延迟策略(委托给底层 TcpConnector) void setReconnectDelayCalcFunc(const ReconnectDelayCalc &func); + //! 设置 TLS 配置(必须在 initialize() 之前调用) + //! 需要 network_tls 模块支持,未链接时调用无效 + void setTlsConfig(const network::TlsConfig &config); + public: //! 发送文本帧 bool send(const std::string &text); diff --git a/modules/websocket/client/ws_client_impl.cpp b/modules/websocket/client/ws_client_impl.cpp index b937e49d..ef473fd8 100644 --- a/modules/websocket/client/ws_client_impl.cpp +++ b/modules/websocket/client/ws_client_impl.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -87,8 +88,9 @@ bool WsClient::Impl::initialize(const network::SockAddr &server_addr, const std: server_addr_ = server_addr; url_path_ = url_path; - //! 创建 TcpRawConnector(保持存活,供重连使用) - sp_connector_ = new network::TcpRawConnector(wp_loop_); + //! 创建 TcpFactory(默认使用 Raw)和 Connector + sp_factory_ = new network::TcpRawFactory; + sp_connector_ = sp_factory_->createConnector(wp_loop_); sp_connector_->initialize(server_addr_); sp_connector_->setConnectedCallback(std::bind(&WsClient::Impl::onTcpConnected, this, _1)); @@ -96,6 +98,36 @@ bool WsClient::Impl::initialize(const network::SockAddr &server_addr, const std: return true; } +void WsClient::Impl::setTlsConfig(const network::TlsConfig &config) +{ + if (state_ != WsClient::State::kNone) { + LogWarn("cannot set TLS config after initialization"); + return; + } + + if (!config.isValid()) { + LogWarn("invalid TLS config"); + return; + } + + //! 替换 factory 和 connector + CHECK_DELETE_RESET_OBJ(sp_connector_); + CHECK_DELETE_RESET_OBJ(sp_factory_); + + network::TcpFactory *tls_factory = network::createTlsFactory(config); + if (tls_factory == nullptr) { + LogWarn("failed to create TLS factory, TLS module may not be linked"); + //! 回退到 Raw Factory + sp_factory_ = new network::TcpRawFactory; + } else { + sp_factory_ = tls_factory; + } + + sp_connector_ = sp_factory_->createConnector(wp_loop_); + sp_connector_->initialize(server_addr_); + sp_connector_->setConnectedCallback(std::bind(&WsClient::Impl::onTcpConnected, this, _1)); +} + void WsClient::Impl::setReconnectDelayCalcFunc(const WsClient::ReconnectDelayCalc &func) { if (sp_connector_ != nullptr) @@ -151,6 +183,7 @@ void WsClient::Impl::cleanup() stop(); CHECK_DELETE_RESET_OBJ(sp_connector_); + CHECK_DELETE_RESET_OBJ(sp_factory_); connected_cb_ = nullptr; disconnected_cb_ = nullptr; diff --git a/modules/websocket/client/ws_client_impl.h b/modules/websocket/client/ws_client_impl.h index 2491a894..2b4ece47 100644 --- a/modules/websocket/client/ws_client_impl.h +++ b/modules/websocket/client/ws_client_impl.h @@ -24,7 +24,8 @@ #include #include #include -#include +#include +#include #include #include "ws_client.h" @@ -57,6 +58,7 @@ class WsClient::Impl { void setErrorCallback(const WsClient::ErrorCallback &cb) { error_cb_ = cb; } void setAutoReconnect(bool enable) { reconnect_enabled_ = enable; } void setReconnectDelayCalcFunc(const WsClient::ReconnectDelayCalc &func); + void setTlsConfig(const network::TlsConfig &config); public: bool send(const std::string &text); @@ -111,6 +113,7 @@ class WsClient::Impl { WsClient *wp_parent_; event::Loop *wp_loop_; + network::TcpFactory *sp_factory_ = nullptr; network::TcpConnector *sp_connector_ = nullptr; network::TcpConnection *sp_tcp_conn_ = nullptr; -- Gitee From cdd1b2b5317baaeba96f7e262796f4876173be08 Mon Sep 17 00:00:00 2001 From: Hevake Date: Sat, 27 Jun 2026 21:35:22 +0800 Subject: [PATCH 4/9] =?UTF-8?q?opt(tcp=5Ftls):=20=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E4=BA=86=E4=BC=98=E5=8C=96=E3=80=82=E4=B8=8D=E8=BF=87=E8=BF=98?= =?UTF-8?q?=E5=AD=98=E5=9C=A8bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 6 ++ .../tls_echo_server/tls_echo_server.cpp | 4 +- modules/network/CMakeLists.txt | 18 +--- modules/network/tcp_connection.cpp | 3 +- modules/network/tcp_connection.h | 4 +- modules/network/tcp_raw_connection.cpp | 4 +- modules/network/tls_factory_entry.cpp | 2 +- modules/network_tls/CMakeLists.txt | 82 +++++++++++++++++++ modules/network_tls/tcp_tls_acceptor.cpp | 7 +- modules/network_tls/tcp_tls_acceptor.h | 2 +- modules/network_tls/tcp_tls_connection.cpp | 10 +-- modules/network_tls/tcp_tls_connection.h | 3 - modules/network_tls/tcp_tls_connector.cpp | 7 +- modules/websocket/client/ws_client_impl.cpp | 2 +- version.mk | 2 +- 15 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 modules/network_tls/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ec3a963..cc19805a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ option(TBOX_ENABLE_EVENT "build event" ON) option(TBOX_ENABLE_EVENTX "build eventx" ON) option(TBOX_ENABLE_LOG "build log" ON) option(TBOX_ENABLE_NETWORK "build network" ON) +option(TBOX_ENABLE_NETWORK_TLS "build network_tls" ON) option(TBOX_ENABLE_TERMINAL "build terminal" ON) option(TBOX_ENABLE_TRACE "build trace" ON) option(TBOX_ENABLE_MAIN "build main" ON) @@ -154,6 +155,11 @@ if(TBOX_ENABLE_NETWORK) list(APPEND TBOX_COMPONENTS network) endif() +if(TBOX_ENABLE_NETWORK_TLS) + message(STATUS "network_tls module enabled") + list(APPEND TBOX_COMPONENTS network_tls) +endif() + if(TBOX_ENABLE_TERMINAL) message(STATUS "terminal module enabled") list(APPEND TBOX_COMPONENTS terminal) diff --git a/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp b/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp index 60bf3982..6745f932 100644 --- a/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp +++ b/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp @@ -94,6 +94,8 @@ int main(int argc, char **argv) //! 当收到数据时,直接往 client 指定对象发回去 server.setReceiveCallback( [&server] (const TcpServer::ConnToken &client, Buffer &buff) { + std::string text((const char*)buff.readableBegin(), buff.readableSize()); + LogInfo("len:%u, text:%s", text.size(), text.c_str()); server.send(client, buff.readableBegin(), buff.readableSize()); buff.hasReadAll(); }, 0 @@ -118,7 +120,7 @@ int main(int argc, char **argv) IPAddress ip; uint16_t port; bind_addr.get(ip, port); - cout << "try command: nc " << ip.toString() << ' ' << port << endl; + cout << "try command: .install/bin/examples/network/tcp_client/tls_echo_client :" << port << endl; } sp_loop->runLoop(); diff --git a/modules/network/CMakeLists.txt b/modules/network/CMakeLists.txt index c0aec3ca..bc765e65 100644 --- a/modules/network/CMakeLists.txt +++ b/modules/network/CMakeLists.txt @@ -27,13 +27,10 @@ set(TBOX_NETWORK_VERSION ${TBOX_NETWORK_VERSION_MAJOR}.${TBOX_NETWORK_VERSION_MI add_definitions(-DMODULE_ID="tbox.network") -find_package(OpenSSL REQUIRED) - set(TBOX_LIBRARY_NAME tbox_network) set(TBOX_NETWORK_HEADERS buffered_fd.h - buffered_ssl_fd.h byte_stream.h stdio_stream.h uart.h @@ -43,26 +40,22 @@ set(TBOX_NETWORK_HEADERS udp_socket.h tcp_connection.h tcp_raw_connection.h - tcp_tls_connection.h tcp_acceptor.h tcp_raw_acceptor.h - tcp_tls_acceptor.h tcp_connector.h tcp_raw_connector.h - tcp_tls_connector.h tcp_client.h tcp_server.h tcp_factory.h tcp_raw_factory.h - tcp_tls_factory.h tls_config.h + tls_factory_entry.h net_if.h domain_name.h dns_request.h) set(TBOX_NETWORK_SOURCES buffered_fd.cpp - buffered_ssl_fd.cpp stdio_stream.cpp uart.cpp socket_fd.cpp @@ -71,18 +64,15 @@ set(TBOX_NETWORK_SOURCES udp_socket.cpp tcp_connection.cpp tcp_raw_connection.cpp - tcp_tls_connection.cpp tcp_acceptor.cpp tcp_raw_acceptor.cpp - tcp_tls_acceptor.cpp tcp_connector.cpp tcp_raw_connector.cpp - tcp_tls_connector.cpp tcp_client.cpp tcp_server.cpp tcp_raw_factory.cpp - tcp_tls_factory.cpp tls_config.cpp + tls_factory_entry.cpp net_if.cpp dns_request.cpp) @@ -104,11 +94,9 @@ set_target_properties( SOVERSION ${TBOX_NETWORK_VERSION_MAJOR} ) -target_link_libraries(${TBOX_LIBRARY_NAME} OpenSSL::SSL OpenSSL::Crypto) - if(${TBOX_ENABLE_TEST}) add_executable(${TBOX_LIBRARY_NAME}_test ${TBOX_NETWORK_TEST_SOURCES}) - target_link_libraries(${TBOX_LIBRARY_NAME}_test gmock_main gmock gtest pthread ${TBOX_LIBRARY_NAME} tbox_base tbox_util tbox_event tbox_eventx OpenSSL::SSL OpenSSL::Crypto rt dl) + target_link_libraries(${TBOX_LIBRARY_NAME}_test gmock_main gmock gtest pthread ${TBOX_LIBRARY_NAME} tbox_base tbox_util tbox_event tbox_eventx rt dl) add_test(NAME ${TBOX_LIBRARY_NAME}_test COMMAND ${TBOX_LIBRARY_NAME}_test) endif() diff --git a/modules/network/tcp_connection.cpp b/modules/network/tcp_connection.cpp index 94a30bd9..63c3d321 100644 --- a/modules/network/tcp_connection.cpp +++ b/modules/network/tcp_connection.cpp @@ -30,9 +30,8 @@ namespace network { using namespace std::placeholders; -TcpConnection::TcpConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) +TcpConnection::TcpConnection(event::Loop *wp_loop, const SockAddr &peer_addr) : wp_loop_(wp_loop) - , sp_buffered_fd_(nullptr) , peer_addr_(peer_addr) { //! sp_buffered_fd_ 由子类在构造函数中创建,然后调用 setupBufferedFd() diff --git a/modules/network/tcp_connection.h b/modules/network/tcp_connection.h index 6e5d7db9..179da3cf 100644 --- a/modules/network/tcp_connection.h +++ b/modules/network/tcp_connection.h @@ -72,7 +72,7 @@ class TcpConnection : public ByteStream { protected: //! 基类构造函数,子类需在构造后自行创建 sp_buffered_fd_ 并调用 setupBufferedFd() - explicit TcpConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr); + explicit TcpConnection(event::Loop *wp_loop, const SockAddr &peer_addr); //! 初始化 BufferedFd 的回调(在子类创建 sp_buffered_fd_ 后调用) void setupBufferedFd(); @@ -86,7 +86,7 @@ class TcpConnection : public ByteStream { protected: event::Loop *wp_loop_; - BufferedFd *sp_buffered_fd_; + BufferedFd *sp_buffered_fd_ = nullptr; SockAddr peer_addr_; DisconnectedCallback disconnected_cb_; diff --git a/modules/network/tcp_raw_connection.cpp b/modules/network/tcp_raw_connection.cpp index 7529efd8..e7b2d57e 100644 --- a/modules/network/tcp_raw_connection.cpp +++ b/modules/network/tcp_raw_connection.cpp @@ -28,9 +28,9 @@ namespace tbox { namespace network { TcpRawConnection::TcpRawConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) - : TcpConnection(wp_loop, fd, peer_addr) + : TcpConnection(wp_loop, peer_addr) { - sp_buffered_fd_ = new BufferedFd(wp_loop_); + sp_buffered_fd_ = new BufferedFd(wp_loop); sp_buffered_fd_->initialize(fd); setupBufferedFd(); } diff --git a/modules/network/tls_factory_entry.cpp b/modules/network/tls_factory_entry.cpp index 17a897fe..bfeba18e 100644 --- a/modules/network/tls_factory_entry.cpp +++ b/modules/network/tls_factory_entry.cpp @@ -24,7 +24,7 @@ namespace tbox { namespace network { -__attribute__((weak)) TcpFactory* CreateTlsFactory(TlsRole role, const TlsConfig &config) +__attribute__((weak)) TcpFactory* CreateTlsFactory(TlsRole, const TlsConfig &) { LogWarn("TLS module not linked, cannot create TLS factory"); return nullptr; diff --git a/modules/network_tls/CMakeLists.txt b/modules/network_tls/CMakeLists.txt new file mode 100644 index 00000000..1385e0f2 --- /dev/null +++ b/modules/network_tls/CMakeLists.txt @@ -0,0 +1,82 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S E / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +cmake_minimum_required(VERSION 3.15) + +set(TBOX_NETWORK_TLS_VERSION_MAJOR 0) +set(TBOX_NETWORK_TLS_VERSION_MINOR 0) +set(TBOX_NETWORK_TLS_VERSION_PATCH 1) +set(TBOX_NETWORK_TLS_VERSION ${TBOX_NETWORK_TLS_VERSION_MAJOR}.${TBOX_NETWORK_TLS_VERSION_MINOR}.${TBOX_NETWORK_TLS_VERSION_PATCH}) + +add_definitions(-DMODULE_ID="tbox.network_tls") + +find_package(OpenSSL REQUIRED) + +set(TBOX_LIBRARY_NAME tbox_network_tls) + +set(TBOX_NETWORK_TLS_HEADERS + buffered_ssl_fd.h + tcp_tls_connection.h + tcp_tls_acceptor.h + tcp_tls_connector.h + tcp_tls_factory.h) + +set(TBOX_NETWORK_TLS_SOURCES + tls_factory_entry.cpp + buffered_ssl_fd.cpp + tcp_tls_connection.cpp + tcp_tls_acceptor.cpp + tcp_tls_connector.cpp + tcp_tls_factory.cpp) + +add_library(${TBOX_LIBRARY_NAME} ${TBOX_BUILD_LIB_TYPE} ${TBOX_NETWORK_TLS_SOURCES}) +add_library(tbox::${TBOX_LIBRARY_NAME} ALIAS ${TBOX_LIBRARY_NAME}) + +set_target_properties( + ${TBOX_LIBRARY_NAME} PROPERTIES + VERSION ${TBOX_NETWORK_TLS_VERSION} + SOVERSION ${TBOX_NETWORK_TLS_VERSION_MAJOR} +) + +target_link_libraries(${TBOX_LIBRARY_NAME} tbox_network OpenSSL::SSL OpenSSL::Crypto) + +# install the target and create export-set +install( + TARGETS ${TBOX_LIBRARY_NAME} + EXPORT ${TBOX_LIBRARY_NAME}_targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +# install header file +install( + FILES ${TBOX_NETWORK_TLS_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tbox/network +) + +# generate and install export file +install( + EXPORT ${TBOX_LIBRARY_NAME}_targets + FILE ${TBOX_LIBRARY_NAME}_targets.cmake + NAMESPACE tbox:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/tbox +) diff --git a/modules/network_tls/tcp_tls_acceptor.cpp b/modules/network_tls/tcp_tls_acceptor.cpp index 992a5ee3..0ed46c18 100644 --- a/modules/network_tls/tcp_tls_acceptor.cpp +++ b/modules/network_tls/tcp_tls_acceptor.cpp @@ -54,8 +54,7 @@ TcpTlsAcceptor::~TcpTlsAcceptor() handshake_fd_.close(); } -TcpConnection* TcpTlsAcceptor::createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) +TcpConnection* TcpTlsAcceptor::createConnection(event::Loop *, SocketFd, const SockAddr &) { //! 注意:此方法不直接使用,由 onSslHandshakeSuccess 创建 TcpTlsConnection return nullptr; @@ -84,7 +83,7 @@ void TcpTlsAcceptor::startSslHandshake(SocketFd fd, const SockAddr &peer_addr) SSL_set_accept_state(handshake_ssl_); //! 保存握手需要的参数 - handshake_fd_ = fd; + handshake_fd_ = fd; //! 不要使用 handshake_fd_.swap(fd) 否则会有问题 handshake_peer_addr_ = peer_addr; //! 开始 SSL_accept @@ -116,7 +115,7 @@ void TcpTlsAcceptor::startSslHandshake(SocketFd fd, const SockAddr &peer_addr) } } -void TcpTlsAcceptor::onSslHandshakeEvent(short events) +void TcpTlsAcceptor::onSslHandshakeEvent(short) { //! 继续 SSL_accept ERR_clear_error(); diff --git a/modules/network_tls/tcp_tls_acceptor.h b/modules/network_tls/tcp_tls_acceptor.h index 1b2f782d..bde74b5c 100644 --- a/modules/network_tls/tcp_tls_acceptor.h +++ b/modules/network_tls/tcp_tls_acceptor.h @@ -39,7 +39,7 @@ class TcpTlsAcceptor : public TcpAcceptor { protected: virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) override; + const SockAddr &peer_addr) override; virtual void onClientAccepted(SocketFd fd, const SockAddr &peer_addr) override; private: diff --git a/modules/network_tls/tcp_tls_connection.cpp b/modules/network_tls/tcp_tls_connection.cpp index bb7c866c..d6197531 100644 --- a/modules/network_tls/tcp_tls_connection.cpp +++ b/modules/network_tls/tcp_tls_connection.cpp @@ -28,17 +28,13 @@ namespace tbox { namespace network { TcpTlsConnection::TcpTlsConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr, SSL *ssl) - : TcpConnection(wp_loop, fd, peer_addr) - , ssl_(ssl) + : TcpConnection(wp_loop, peer_addr) { //! 创建 BufferedSslFd 并初始化 - auto *ssl_fd = new BufferedSslFd(wp_loop_); - ssl_fd->initialize(fd, ssl_); + auto *ssl_fd = new BufferedSslFd(wp_loop); + ssl_fd->initialize(fd, ssl); sp_buffered_fd_ = ssl_fd; setupBufferedFd(); - - //! SSL 对象已由 BufferedSslFd 接管,本类不再单独管理 - ssl_ = nullptr; } bool TcpTlsConnection::doDisconnect() diff --git a/modules/network_tls/tcp_tls_connection.h b/modules/network_tls/tcp_tls_connection.h index 106f2340..92e5084e 100644 --- a/modules/network_tls/tcp_tls_connection.h +++ b/modules/network_tls/tcp_tls_connection.h @@ -40,9 +40,6 @@ class TcpTlsConnection : public TcpConnection { protected: virtual bool doDisconnect() override; virtual bool doShutdown(int howto) override; - - private: - SSL *ssl_ = nullptr; }; } diff --git a/modules/network_tls/tcp_tls_connector.cpp b/modules/network_tls/tcp_tls_connector.cpp index f7ae0472..52ac1377 100644 --- a/modules/network_tls/tcp_tls_connector.cpp +++ b/modules/network_tls/tcp_tls_connector.cpp @@ -54,8 +54,7 @@ TcpTlsConnector::~TcpTlsConnector() handshake_fd_.close(); } -TcpConnection* TcpTlsConnector::createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) +TcpConnection* TcpTlsConnector::createConnection(event::Loop *, SocketFd, const SockAddr &) { //! 注意:此方法只在 SSL 握手成功后由 onSslHandshakeSuccess 调用 //! 此时 handshake_ssl_ 已经是完全建立的 SSL 连接 @@ -90,7 +89,7 @@ void TcpTlsConnector::startSslHandshake(SocketFd fd, const SockAddr &peer_addr) SSL_set_fd(handshake_ssl_, fd.get()); //! 保存握手需要的参数 - handshake_fd_ = fd; + handshake_fd_ = fd; //! 不要使用 handshake_fd_.swap(fd) 否则会有问题 handshake_peer_addr_ = peer_addr; //! 开始 SSL_connect @@ -125,7 +124,7 @@ void TcpTlsConnector::startSslHandshake(SocketFd fd, const SockAddr &peer_addr) } } -void TcpTlsConnector::onSslHandshakeEvent(short events) +void TcpTlsConnector::onSslHandshakeEvent(short) { //! 继续 SSL_connect ERR_clear_error(); diff --git a/modules/websocket/client/ws_client_impl.cpp b/modules/websocket/client/ws_client_impl.cpp index ef473fd8..85589d21 100644 --- a/modules/websocket/client/ws_client_impl.cpp +++ b/modules/websocket/client/ws_client_impl.cpp @@ -114,7 +114,7 @@ void WsClient::Impl::setTlsConfig(const network::TlsConfig &config) CHECK_DELETE_RESET_OBJ(sp_connector_); CHECK_DELETE_RESET_OBJ(sp_factory_); - network::TcpFactory *tls_factory = network::createTlsFactory(config); + network::TcpFactory *tls_factory = network::CreateTlsFactory(network::TlsRole::kClient, config); if (tls_factory == nullptr) { LogWarn("failed to create TLS factory, TLS module may not be linked"); //! 回退到 Raw Factory diff --git a/version.mk b/version.mk index bedb645e..40ec7c2e 100644 --- a/version.mk +++ b/version.mk @@ -21,4 +21,4 @@ # TBOX版本号 TBOX_VERSION_MAJOR := 1 TBOX_VERSION_MINOR := 15 -TBOX_VERSION_REVISION := 2 +TBOX_VERSION_REVISION := 3 -- Gitee From f07b4ae6bf98283b07b0df9059a44c41bbd8fecd Mon Sep 17 00:00:00 2001 From: Hevake Date: Sat, 27 Jun 2026 22:25:06 +0800 Subject: [PATCH 5/9] =?UTF-8?q?fix(tcp=5Ftls):=E4=BF=AE=E5=A4=8D=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E6=95=B0=E6=8D=AE=E5=BB=B6=E8=BF=9F=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/network/buffered_fd.cpp | 8 ++++++++ modules/network/buffered_fd.h | 10 +++++++++- modules/network_tls/buffered_ssl_fd.cpp | 13 +++++++++++++ modules/network_tls/buffered_ssl_fd.h | 4 ++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/modules/network/buffered_fd.cpp b/modules/network/buffered_fd.cpp index 096d966d..a40be478 100644 --- a/modules/network/buffered_fd.cpp +++ b/modules/network/buffered_fd.cpp @@ -258,6 +258,14 @@ void BufferedFd::onReadCallback(short) LogWarn("read error, rsize:%d, errno:%d, %s", rsize, errno, strerror(errno)); } } + + //! 读取周期完成后,通知子类检查是否有内部缓冲的残留数据 + onReadCycleCompleted(); +} + +void BufferedFd::triggerReadCycle() +{ + onReadCallback(0); } void BufferedFd::onWriteCallback(short) diff --git a/modules/network/buffered_fd.h b/modules/network/buffered_fd.h index 618583f6..d0d0aaf5 100644 --- a/modules/network/buffered_fd.h +++ b/modules/network/buffered_fd.h @@ -92,12 +92,20 @@ class BufferedFd : public ByteStream { virtual ssize_t doRead(void *buffer, size_t size); virtual ssize_t doWrite(const void *data, size_t size); + //! 读取周期完成后的钩子,子类可覆写以处理内部缓冲的残留数据 + //! 如 BufferedSslFd 通过此钩子检测 SSL_pending() 并触发重读 + virtual void onReadCycleCompleted() {} + + //! 手动触发一次读取周期(用于提取子类内部缓冲的残留数据) + void triggerReadCycle(); + + event::Loop *wp_loop_ = nullptr; //! 事件驱动(子类可能需要访问) + private: void onReadCallback(short); void onWriteCallback(short); private: - event::Loop *wp_loop_ = nullptr; //! 事件驱动 Fd fd_; State state_ = State::kEmpty; diff --git a/modules/network_tls/buffered_ssl_fd.cpp b/modules/network_tls/buffered_ssl_fd.cpp index 54ccaf8c..168c56d5 100644 --- a/modules/network_tls/buffered_ssl_fd.cpp +++ b/modules/network_tls/buffered_ssl_fd.cpp @@ -202,5 +202,18 @@ void BufferedSslFd::handleSslRenegotiation(int ssl_error, bool is_read_op) } } +void BufferedSslFd::onReadCycleCompleted() +{ + //! SSL_read 可能解密了完整 SSL 记录但仅返回部分数据(受调用方 buffer size 限制) + //! 剩余数据留在 OpenSSL 内部缓冲中,fd 的内核缓冲已空,不会触发新的读事件 + //! 通过 SSL_pending() 检测残留数据,并用 runNext 触发下一轮读取来提取 + if (ssl_ != nullptr && SSL_pending(ssl_) > 0) { + wp_loop_->runNext( + [this] { triggerReadCycle(); }, + "BufferedSslFd::readPendingData" + ); + } +} + } } diff --git a/modules/network_tls/buffered_ssl_fd.h b/modules/network_tls/buffered_ssl_fd.h index 02c65c9c..0fd5f03b 100644 --- a/modules/network_tls/buffered_ssl_fd.h +++ b/modules/network_tls/buffered_ssl_fd.h @@ -47,6 +47,10 @@ class BufferedSslFd : public BufferedFd { virtual ssize_t doRead(void *buffer, size_t size) override; virtual ssize_t doWrite(const void *data, size_t size) override; + //! 读取周期完成后检查 SSL 内部缓冲是否有残留数据 + //! 如果有,通过 runNext 触发下一轮读取以提取残留数据 + virtual void onReadCycleCompleted() override; + private: //! 处理 SSL 读写过程中的 WANT_READ/WANT_WRITE(renegotiation) void handleSslRenegotiation(int ssl_error, bool is_read_op); -- Gitee From eb9d4be67f18623eff9ae86bbde3bf5c0097d9a0 Mon Sep 17 00:00:00 2001 From: Hevake Date: Mon, 29 Jun 2026 13:00:32 +0800 Subject: [PATCH 6/9] =?UTF-8?q?fix:1.15.4,=E8=A7=A3=E5=86=B3TLS=E6=AF=8F?= =?UTF-8?q?=E6=AC=A1=E5=9B=9E=E8=B0=83=E5=8F=AA=E8=83=BD=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E5=87=A0=E4=B8=AA=E5=AD=97=E8=8A=82=EF=BC=8C=E4=B8=8D=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD=EF=BC=8C=E4=BC=9A=E8=A7=A6=E5=8F=91=E5=A4=9A=E6=AC=A1?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/tcp_server/tcp_echo/tcp_echo.cpp | 2 + modules/network/buffered_fd.cpp | 66 ++++++++----------- modules/network/buffered_fd.h | 12 +--- modules/network/socket_fd.h | 4 ++ modules/network_tls/buffered_ssl_fd.cpp | 52 ++++++++++----- modules/network_tls/buffered_ssl_fd.h | 7 +- version.mk | 2 +- 7 files changed, 74 insertions(+), 71 deletions(-) diff --git a/examples/network/tcp_server/tcp_echo/tcp_echo.cpp b/examples/network/tcp_server/tcp_echo/tcp_echo.cpp index 74092b1e..2295e067 100644 --- a/examples/network/tcp_server/tcp_echo/tcp_echo.cpp +++ b/examples/network/tcp_server/tcp_echo/tcp_echo.cpp @@ -61,6 +61,8 @@ int main(int argc, char **argv) //! 当收到数据时,直接往client指定对象发回去 server.setReceiveCallback( [&server] (const TcpServer::ConnToken &client, Buffer &buff) { + std::string text((const char*)buff.readableBegin(), buff.readableSize()); + LogInfo("len:%u, text:%s", text.size(), text.c_str()); server.send(client, buff.readableBegin(), buff.readableSize()); buff.hasReadAll(); }, 0 diff --git a/modules/network/buffered_fd.cpp b/modules/network/buffered_fd.cpp index a40be478..aa15aad8 100644 --- a/modules/network/buffered_fd.cpp +++ b/modules/network/buffered_fd.cpp @@ -179,9 +179,9 @@ void BufferedFd::shrinkSendBuffer() send_buff_.shrink(); } -ssize_t BufferedFd::doRead(void *buffer, size_t size) +ssize_t BufferedFd::doReadv(const struct iovec *iov, int iovcnt) { - return fd_.read(buffer, size); + return fd_.readv(iov, iovcnt); } ssize_t BufferedFd::doWrite(const void *data, size_t size) @@ -192,40 +192,35 @@ ssize_t BufferedFd::doWrite(const void *data, size_t size) void BufferedFd::onReadCallback(short) { RECORD_SCOPE(); - char extbuf[1024]; //! 扩展存储空间,当 recv_buff_ 写满时使用 + struct iovec rbuf[2]; + char extbuf[1024]; //! 扩展存储空间 - //! 循环读取数据,直到读不到为止 - ssize_t rsize; size_t writable_size = recv_buff_.writableSize(); - if (writable_size > 0) { - //! 优先读入 recv_buff_ 可写区域 - while ((rsize = doRead(recv_buff_.writableBegin(), writable_size)) > 0) { - recv_buff_.hasWritten(rsize); - writable_size = recv_buff_.writableSize(); - } - } else { - //! recv_buff_ 可写空间不足时,先读入 extbuf 再 append - while ((rsize = doRead(extbuf, sizeof(extbuf))) > 0) { - recv_buff_.append(extbuf, rsize); - writable_size = recv_buff_.writableSize(); - //! 如果 recv_buff_ 又有了可写空间,切换到直接读入模式 - if (writable_size > 0) { - ssize_t r2; - while ((r2 = doRead(recv_buff_.writableBegin(), writable_size)) > 0) { - recv_buff_.hasWritten(r2); - writable_size = recv_buff_.writableSize(); - } - //! 用 r2 更新 rsize 用于后续状态判断 - if (r2 != 0) - rsize = r2; - break; + //! 优先将数据读入到 recv_buff_ 中去,如果它装不下就再存到 extbuff 中 + rbuf[0].iov_base = recv_buff_.writableBegin(); + rbuf[0].iov_len = writable_size; + rbuf[1].iov_base = extbuf; + rbuf[1].iov_len = sizeof(extbuf); + + ssize_t rsize = doReadv(rbuf, 2); + if (rsize > 0) { //! 读到了数据 + do { + if (static_cast(rsize) > writable_size) { + //! 如果实际读出的数据比 recv_buff_ 的可写区还大,说明有部分数据是写到了 extbuf 中去了 + recv_buff_.hasWritten(writable_size); + size_t remain_size = rsize - writable_size; //! 计算 extbuf 中的数据大小 + recv_buff_.append(extbuf, remain_size); + } else { + recv_buff_.hasWritten(rsize); } - } - } - //! 检查读取结果 - if (recv_buff_.readableSize() > 0) { //! 读到了数据 + //! 继续读,直到 rsize == 0,表示读完为止 + writable_size = recv_buff_.writableSize(); + rbuf[0].iov_base = recv_buff_.writableBegin(); + rbuf[0].iov_len = writable_size; + } while ((rsize = doReadv(rbuf, 2)) > 0); + //! 如果有绑定接收者,则应将数据直接转发给接收者 if (wp_receiver_ != nullptr) { wp_receiver_->send(recv_buff_.readableBegin(), recv_buff_.readableSize()); @@ -258,14 +253,6 @@ void BufferedFd::onReadCallback(short) LogWarn("read error, rsize:%d, errno:%d, %s", rsize, errno, strerror(errno)); } } - - //! 读取周期完成后,通知子类检查是否有内部缓冲的残留数据 - onReadCycleCompleted(); -} - -void BufferedFd::triggerReadCycle() -{ - onReadCallback(0); } void BufferedFd::onWriteCallback(short) @@ -299,3 +286,4 @@ void BufferedFd::onWriteCallback(short) } } + diff --git a/modules/network/buffered_fd.h b/modules/network/buffered_fd.h index d0d0aaf5..10fe5dad 100644 --- a/modules/network/buffered_fd.h +++ b/modules/network/buffered_fd.h @@ -89,23 +89,15 @@ class BufferedFd : public ByteStream { protected: //! 可被子类覆写的底层I/O方法(如 BufferedSslFd 使用 SSL_read/SSL_write) - virtual ssize_t doRead(void *buffer, size_t size); + virtual ssize_t doReadv(const struct iovec *iov, int iovcnt); virtual ssize_t doWrite(const void *data, size_t size); - //! 读取周期完成后的钩子,子类可覆写以处理内部缓冲的残留数据 - //! 如 BufferedSslFd 通过此钩子检测 SSL_pending() 并触发重读 - virtual void onReadCycleCompleted() {} - - //! 手动触发一次读取周期(用于提取子类内部缓冲的残留数据) - void triggerReadCycle(); - - event::Loop *wp_loop_ = nullptr; //! 事件驱动(子类可能需要访问) - private: void onReadCallback(short); void onWriteCallback(short); private: + event::Loop *wp_loop_ = nullptr; Fd fd_; State state_ = State::kEmpty; diff --git a/modules/network/socket_fd.h b/modules/network/socket_fd.h index 4f2c5af4..db729dcd 100644 --- a/modules/network/socket_fd.h +++ b/modules/network/socket_fd.h @@ -37,6 +37,10 @@ class SocketFd : public util::Fd { using Fd::operator=; using Fd::swap; + //! 显式声明拷贝构造与拷贝赋值,消除 GCC -Wdeprecated-copy 警告 + SocketFd(const SocketFd &other) : Fd(other) { } + SocketFd& operator=(const SocketFd &other) { return static_cast(Fd::operator=(other)); } + public: static SocketFd CreateSocket(int domain, int type, int protocal); static SocketFd CreateUdpSocket(); diff --git a/modules/network_tls/buffered_ssl_fd.cpp b/modules/network_tls/buffered_ssl_fd.cpp index 168c56d5..ccd6d8a1 100644 --- a/modules/network_tls/buffered_ssl_fd.cpp +++ b/modules/network_tls/buffered_ssl_fd.cpp @@ -57,10 +57,43 @@ bool BufferedSslFd::initialize(Fd fd, SSL *ssl, short events) SSL_set_mode(ssl_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); //! 调用父类 initialize - if (!BufferedFd::initialize(fd, events)) - return false; + return BufferedFd::initialize(fd, events); +} + +ssize_t BufferedSslFd::doReadv(const struct iovec *iov, int iovcnt) +{ + //! SSL_read 只能向单一连续 buffer 写入数据,无法像 readv() 那样 scatter-gather + //! 实现:逐个 iov 调用 doRead(),将 SSL 解密数据依次填入各 iov 缓冲区 + //! 已填满的 iov 不再参与后续读取,确保数据不会溢出 + + ssize_t total_read = 0; + for (int i = 0; i < iovcnt; ++i) { + if (iov[i].iov_len == 0) + continue; + + ssize_t rsize = doRead(iov[i].iov_base, iov[i].iov_len); + if (rsize > 0) { + total_read += rsize; + //! 如果本次读取未填满当前 iov,说明 SSL 内部缓冲已暂无更多数据 + //! 后续 iov 也无法再填充,直接返回已读总量 + if (static_cast(rsize) < iov[i].iov_len) + return total_read; + } else if (rsize == 0) { + //! 对端关闭连接(close_notify),立即返回 + //! 若已有部分数据读入前面 iov,total_read > 0,由上层判断 + //! 若无数据读入,total_read == 0,上层会走 read_zero_cb_ 逻辑 + return (total_read > 0) ? total_read : 0; + } else { + //! 读取出错(EAGAIN 或其他错误) + //! 若已有部分数据读入前面 iov,total_read > 0,优先返回已读数据 + //! errno 已由 doRead 设置(EAGAIN 等),上层据此判断 + return (total_read > 0) ? total_read : -1; + } + } - return true; + //! 所有 iov 都被填满,但 SSL 内部缓冲可能还有 pending 数据 + //! 上层 onReadCallback 的 while 循环会继续调用 doReadv 来提取 + return total_read; } ssize_t BufferedSslFd::doRead(void *buffer, size_t size) @@ -202,18 +235,5 @@ void BufferedSslFd::handleSslRenegotiation(int ssl_error, bool is_read_op) } } -void BufferedSslFd::onReadCycleCompleted() -{ - //! SSL_read 可能解密了完整 SSL 记录但仅返回部分数据(受调用方 buffer size 限制) - //! 剩余数据留在 OpenSSL 内部缓冲中,fd 的内核缓冲已空,不会触发新的读事件 - //! 通过 SSL_pending() 检测残留数据,并用 runNext 触发下一轮读取来提取 - if (ssl_ != nullptr && SSL_pending(ssl_) > 0) { - wp_loop_->runNext( - [this] { triggerReadCycle(); }, - "BufferedSslFd::readPendingData" - ); - } -} - } } diff --git a/modules/network_tls/buffered_ssl_fd.h b/modules/network_tls/buffered_ssl_fd.h index 0fd5f03b..b71b528e 100644 --- a/modules/network_tls/buffered_ssl_fd.h +++ b/modules/network_tls/buffered_ssl_fd.h @@ -44,14 +44,11 @@ class BufferedSslFd : public BufferedFd { protected: //! 覆写底层 I/O 方法 - virtual ssize_t doRead(void *buffer, size_t size) override; + virtual ssize_t doReadv(const struct iovec *iov, int iovcnt); virtual ssize_t doWrite(const void *data, size_t size) override; - //! 读取周期完成后检查 SSL 内部缓冲是否有残留数据 - //! 如果有,通过 runNext 触发下一轮读取以提取残留数据 - virtual void onReadCycleCompleted() override; - private: + ssize_t doRead(void *buffer, size_t size); //! 处理 SSL 读写过程中的 WANT_READ/WANT_WRITE(renegotiation) void handleSslRenegotiation(int ssl_error, bool is_read_op); diff --git a/version.mk b/version.mk index 40ec7c2e..9c364666 100644 --- a/version.mk +++ b/version.mk @@ -21,4 +21,4 @@ # TBOX版本号 TBOX_VERSION_MAJOR := 1 TBOX_VERSION_MINOR := 15 -TBOX_VERSION_REVISION := 3 +TBOX_VERSION_REVISION := 4 -- Gitee From ad43a5cc26ce6fe0cbea1c0aeaf713debe81305f Mon Sep 17 00:00:00 2001 From: Hevake Date: Mon, 29 Jun 2026 13:53:43 +0800 Subject: [PATCH 7/9] tidy --- .../network/tcp_server/{tcp_echo => tcp_echo_server}/Makefile | 4 ++-- .../tcp_echo.cpp => tcp_echo_server/tcp_echo_server.cpp} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename examples/network/tcp_server/{tcp_echo => tcp_echo_server}/Makefile (89%) rename examples/network/tcp_server/{tcp_echo/tcp_echo.cpp => tcp_echo_server/tcp_echo_server.cpp} (100%) diff --git a/examples/network/tcp_server/tcp_echo/Makefile b/examples/network/tcp_server/tcp_echo_server/Makefile similarity index 89% rename from examples/network/tcp_server/tcp_echo/Makefile rename to examples/network/tcp_server/tcp_echo_server/Makefile index 34dd0473..e35b9bae 100644 --- a/examples/network/tcp_server/tcp_echo/Makefile +++ b/examples/network/tcp_server/tcp_echo_server/Makefile @@ -18,10 +18,10 @@ # of the source tree. # -PROJECT := examples/network/tcp_server/tcp_echo +PROJECT := examples/network/tcp_server/tcp_echo_server EXE_NAME := ${PROJECT} -CPP_SRC_FILES := tcp_echo.cpp +CPP_SRC_FILES := tcp_echo_server.cpp CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) LDFLAGS += \ diff --git a/examples/network/tcp_server/tcp_echo/tcp_echo.cpp b/examples/network/tcp_server/tcp_echo_server/tcp_echo_server.cpp similarity index 100% rename from examples/network/tcp_server/tcp_echo/tcp_echo.cpp rename to examples/network/tcp_server/tcp_echo_server/tcp_echo_server.cpp -- Gitee From 7aaeab7f8c86cbfb7b9413e7d45cc7d1d48fe5f2 Mon Sep 17 00:00:00 2001 From: Hevake Date: Mon, 29 Jun 2026 14:15:17 +0800 Subject: [PATCH 8/9] =?UTF-8?q?tiny:=20=E4=BF=AE=E6=94=B9=20https=5Fsimple?= =?UTF-8?q?=20=E4=B8=8E=20tls=5Fecho=5Fserver=20=E7=9A=84=20Makefile=20?= =?UTF-8?q?=E4=BD=BF=E4=B9=8B=E5=9C=A8=E5=AE=89=E8=A3=85=E6=97=B6=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E5=AF=B9=E5=BA=94=E7=9A=84=E8=AF=81=E4=B9=A6=E4=B8=8E?= =?UTF-8?q?=E5=AF=86=E9=92=A5=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/http/server/https_simple/Makefile | 2 ++ examples/network/tcp_server/tls_echo_server/Makefile | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/http/server/https_simple/Makefile b/examples/http/server/https_simple/Makefile index bc10b125..0e345fc3 100644 --- a/examples/http/server/https_simple/Makefile +++ b/examples/http/server/https_simple/Makefile @@ -21,6 +21,8 @@ EXE_NAME := ${PROJECT} CPP_SRC_FILES := https_simple.cpp +CONF_FILES := server.crt server.key + CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) LDFLAGS += \ -ltbox_http \ diff --git a/examples/network/tcp_server/tls_echo_server/Makefile b/examples/network/tcp_server/tls_echo_server/Makefile index 76d61c99..ff551dfb 100644 --- a/examples/network/tcp_server/tls_echo_server/Makefile +++ b/examples/network/tcp_server/tls_echo_server/Makefile @@ -21,6 +21,8 @@ EXE_NAME := ${PROJECT} CPP_SRC_FILES := tls_echo_server.cpp +CONF_FILES := server.crt server.key + CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) LDFLAGS += \ -ltbox_network \ -- Gitee From 2d14bd084bc490ecfebd919912a6d85cb5244534 Mon Sep 17 00:00:00 2001 From: Hevake Date: Mon, 29 Jun 2026 14:49:43 +0800 Subject: [PATCH 9/9] =?UTF-8?q?tidy:=20=E6=95=B4=E7=90=86=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=9B=E4=BF=AE=E6=94=B9isValid()=EF=BC=8C=E6=94=BE?= =?UTF-8?q?=E5=AE=BD=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/network/tcp_connector.h | 3 +-- modules/network/tcp_raw_acceptor.cpp | 3 +-- modules/network/tcp_raw_acceptor.h | 3 +-- modules/network/tls_config.cpp | 12 ++++++------ modules/network_tls/tcp_tls_connector.h | 3 +-- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/modules/network/tcp_connector.h b/modules/network/tcp_connector.h index 43f7bc8e..3db2c860 100644 --- a/modules/network/tcp_connector.h +++ b/modules/network/tcp_connector.h @@ -75,8 +75,7 @@ class TcpConnector { virtual int connect(SocketFd sock_fd, const SockAddr &addr) const; //! 子类覆写:创建对应类型的 TcpConnection - virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) = 0; + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) = 0; //! 子类覆写:TCP 连接成功后的处理 //! TcpRawConnector: 立即 enable + 触发 connected_cb_ diff --git a/modules/network/tcp_raw_acceptor.cpp b/modules/network/tcp_raw_acceptor.cpp index 4a034564..79d9aefa 100644 --- a/modules/network/tcp_raw_acceptor.cpp +++ b/modules/network/tcp_raw_acceptor.cpp @@ -32,8 +32,7 @@ TcpRawAcceptor::TcpRawAcceptor(event::Loop *wp_loop) : TcpAcceptor(wp_loop) { } -TcpConnection* TcpRawAcceptor::createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) +TcpConnection* TcpRawAcceptor::createConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) { return new TcpRawConnection(wp_loop, fd, peer_addr); } diff --git a/modules/network/tcp_raw_acceptor.h b/modules/network/tcp_raw_acceptor.h index 29e00da0..58e0553c 100644 --- a/modules/network/tcp_raw_acceptor.h +++ b/modules/network/tcp_raw_acceptor.h @@ -32,8 +32,7 @@ class TcpRawAcceptor : public TcpAcceptor { explicit TcpRawAcceptor(event::Loop *wp_loop); protected: - virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) override; + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) override; virtual void onClientAccepted(SocketFd fd, const SockAddr &peer_addr) override; }; diff --git a/modules/network/tls_config.cpp b/modules/network/tls_config.cpp index d559a4d8..2d5ead78 100644 --- a/modules/network/tls_config.cpp +++ b/modules/network/tls_config.cpp @@ -35,12 +35,12 @@ bool TlsConfig::isValid() const LogErr("key_file is set but cert_file is not"); return false; } - //! 当 verify_peer=false 时,允许不提供任何证书(client 场景,类似 curl -k) - //! 但当 verify_peer=true 时,必须有 CA 证书来验证对端 - if (verify_peer && ca_file.empty() && ca_path.empty()) { - LogErr("verify_peer requires ca_file or ca_path"); - return false; - } + //! ca_file 与 ca_path 可选,不要求必须设置: + //! - verify_peer=true 但未指定 ca_file/ca_path 时,Client 使用系统默认 CA + //! (SSL_CTX_set_default_verify_paths),如 /etc/ssl/certs + //! - Server 不验证客户端证书时不需要 CA + //! - 指定了 ca_file 或 ca_path 时,两者至少有一个非空即可,OpenSSL 会正常加载 + return true; } diff --git a/modules/network_tls/tcp_tls_connector.h b/modules/network_tls/tcp_tls_connector.h index 1612dba1..ce568acb 100644 --- a/modules/network_tls/tcp_tls_connector.h +++ b/modules/network_tls/tcp_tls_connector.h @@ -38,8 +38,7 @@ class TcpTlsConnector : public TcpConnector { ~TcpTlsConnector(); protected: - virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, - const SockAddr &peer_addr) override; + virtual TcpConnection* createConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr) override; virtual void onTcpConnected(SocketFd fd, const SockAddr &peer_addr) override; private: -- Gitee