diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ec3a963de22a5af7509cfb4f2e21660b8c40332..cc19805a9b15ad2381a6f638566ab65fd14711fe 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/config.mk b/config.mk index 94768fc33083f612f9006a9399a37adbec54e42b..b5865ef3d8ac4c447400ac00161690b58b454624 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 0000000000000000000000000000000000000000..0e345fc31a36dccd82683aa0bd0c311f91302af5 --- /dev/null +++ b/examples/http/server/https_simple/Makefile @@ -0,0 +1,39 @@ +# +# .============. +# // 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 + +CONF_FILES := server.crt server.key + +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 0000000000000000000000000000000000000000..bc7487b15b9b01cec621db670e05b82f714c93f8 --- /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 0000000000000000000000000000000000000000..aafbc0be60b172114ec6f6477d0c274635b6b0c9 --- /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 0000000000000000000000000000000000000000..a9b48be039b7b166b2caeeee5cc0af49bf75a9bc --- /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 6dd2ea2de36ec9ace1805e0d2db29ecf5503bf4b..b4ff0b1362dbbd5b0314d4600899837876e3c056 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 fec1b76281d4b52cb5cef6f14acbb8e3ea1610ae..7e4e8afedbf6732a82c056b2a48be5d891c217aa 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 0000000000000000000000000000000000000000..5ac11e20152769bf1a5ea756938ef0dbda4d8a72 --- /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 0000000000000000000000000000000000000000..a97ebc0f4c91166b175fb396a257f43b9f1deb1f --- /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 8573ccbe44e37a84359fb965811ce9afa5e16aa9..3889ec9c5d3fe1e8a7c374a5d29a7b61f35808de 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 c5bddc904164c7420cb59e4b451232afeaf364ad..33e205815a1c7cd76aa638f8f89d21d5b88d280c 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/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 34dd04730721d19f687ac17d9239ecb1732e5590..e35b9baefb78bd7cd9268d9bd9fe11622cdc22e1 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 94% rename from examples/network/tcp_server/tcp_echo/tcp_echo.cpp rename to examples/network/tcp_server/tcp_echo_server/tcp_echo_server.cpp index 74092b1e04dd325e9e963b21eab3232e5e019c6e..2295e06768241cc3007cd21bca61545721fa7071 100644 --- a/examples/network/tcp_server/tcp_echo/tcp_echo.cpp +++ b/examples/network/tcp_server/tcp_echo_server/tcp_echo_server.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/examples/network/tcp_server/tls_echo_server/Makefile b/examples/network/tcp_server/tls_echo_server/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..ff551dfb92fe5b292b023669ba8269e80c9253a2 --- /dev/null +++ b/examples/network/tcp_server/tls_echo_server/Makefile @@ -0,0 +1,36 @@ +# +# .============. +# // 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 + +CONF_FILES := server.crt server.key + +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 0000000000000000000000000000000000000000..aafbc0be60b172114ec6f6477d0c274635b6b0c9 --- /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 0000000000000000000000000000000000000000..a9b48be039b7b166b2caeeee5cc0af49bf75a9bc --- /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 0000000000000000000000000000000000000000..6745f93230dc2a06bc5bd3f3851d4193e4555aa3 --- /dev/null +++ b/examples/network/tcp_server/tls_echo_server/tls_echo_server.cpp @@ -0,0 +1,130 @@ +/* + * .============. + * // 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) { + 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 + ); + 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: .install/bin/examples/network/tcp_client/tls_echo_client :" << port << endl; + } + + sp_loop->runLoop(); + LogInfo("tls echo server stopped"); + + return 0; +} diff --git a/modules/http/client/client.cpp b/modules/http/client/client.cpp index 6a2aecf5fc7dd20984e9d008fa19f71c5baaee1f..94f5956a195d30339dda91806955b605cd8a293d 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 441dbb8a2a82d61a473eda35e41e413bdfe005f8..34125fced7961b15d7080121a1f59cfc0049f339 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 8eda2a59361ade5ad23f6716c08d170d4fd2cc08..748331bbe5f00e5ffd4ab15cd8b0de698686ab2c 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 94c58890f1967fbebb9399226894b49b4d369031..8ddc31ae0b5b1e789a046617b558c76c8b020187 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 5f852fe46b4e3540bd0870ec88529c00f3e71721..d002695d54efe8b1f87adf9b704c12d07c6bbf03 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 8ebae2e034c9e84101b7b95527a8fba5b5777b03..d7a5a715109e88604153d975808d0f98b69afa65 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 56ac94975cae8ff2e78efa9e16083e623e5c8ab4..c86a9c80a170c6d3536fc3a0049d68b624819a29 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 c3f981e118b8b44cabcd1caaa88fe73d2481aa7b..3a7d34ca577e5db4e95a8227f669fd572d8e3b9b 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/network/CMakeLists.txt b/modules/network/CMakeLists.txt index a2fe0b8580ec2d145096afd27f5ad11bcb4badd4..bc765e6549fb8d40d3f13de1d180c650c56a823b 100644 --- a/modules/network/CMakeLists.txt +++ b/modules/network/CMakeLists.txt @@ -39,10 +39,17 @@ set(TBOX_NETWORK_HEADERS sockaddr.h udp_socket.h tcp_connection.h + tcp_raw_connection.h tcp_acceptor.h + tcp_raw_acceptor.h tcp_connector.h + tcp_raw_connector.h tcp_client.h tcp_server.h + tcp_factory.h + tcp_raw_factory.h + tls_config.h + tls_factory_entry.h net_if.h domain_name.h dns_request.h) @@ -56,10 +63,16 @@ set(TBOX_NETWORK_SOURCES sockaddr.cpp udp_socket.cpp tcp_connection.cpp + tcp_raw_connection.cpp tcp_acceptor.cpp + tcp_raw_acceptor.cpp tcp_connector.cpp + tcp_raw_connector.cpp tcp_client.cpp tcp_server.cpp + tcp_raw_factory.cpp + tls_config.cpp + tls_factory_entry.cpp net_if.cpp dns_request.cpp) diff --git a/modules/network/Makefile b/modules/network/Makefile index e5823adf682292d7ce1d300096446e4b07ca2e69..4d828401dfd5d86d60b1559bd93419ec976f4051 100644 --- a/modules/network/Makefile +++ b/modules/network/Makefile @@ -34,10 +34,17 @@ HEAD_FILES = \ sockaddr.h \ udp_socket.h \ tcp_connection.h \ + tcp_raw_connection.h \ tcp_acceptor.h \ + tcp_raw_acceptor.h \ tcp_connector.h \ + tcp_raw_connector.h \ tcp_client.h \ tcp_server.h \ + tcp_factory.h \ + tcp_raw_factory.h \ + tls_config.h \ + tls_factory_entry.h \ net_if.h \ domain_name.h \ dns_request.h \ @@ -51,10 +58,16 @@ CPP_SRC_FILES = \ sockaddr.cpp \ udp_socket.cpp \ tcp_connection.cpp \ + tcp_raw_connection.cpp \ tcp_acceptor.cpp \ + tcp_raw_acceptor.cpp \ tcp_connector.cpp \ + tcp_raw_connector.cpp \ tcp_client.cpp \ tcp_server.cpp \ + tcp_raw_factory.cpp \ + tls_config.cpp \ + tls_factory_entry.cpp \ net_if.cpp \ dns_request.cpp \ diff --git a/modules/network/buffered_fd.cpp b/modules/network/buffered_fd.cpp index e9d1bedd2def78c23b26ecc4f7a0fc37442c1e19..aa15aad8b5c283d610226f27c8a430455f7d2a32 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,6 +179,16 @@ void BufferedFd::shrinkSendBuffer() send_buff_.shrink(); } +ssize_t BufferedFd::doReadv(const struct iovec *iov, int iovcnt) +{ + return fd_.readv(iov, iovcnt); +} + +ssize_t BufferedFd::doWrite(const void *data, size_t size) +{ + return fd_.write(data, size); +} + void BufferedFd::onReadCallback(short) { RECORD_SCOPE(); @@ -193,7 +203,7 @@ void BufferedFd::onReadCallback(short) rbuf[1].iov_base = extbuf; rbuf[1].iov_len = sizeof(extbuf); - ssize_t rsize = fd_.readv(rbuf, 2); + ssize_t rsize = doReadv(rbuf, 2); if (rsize > 0) { //! 读到了数据 do { if (static_cast(rsize) > writable_size) { @@ -209,7 +219,7 @@ void BufferedFd::onReadCallback(short) 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); + } while ((rsize = doReadv(rbuf, 2)) > 0); //! 如果有绑定接收者,则应将数据直接转发给接收者 if (wp_receiver_ != nullptr) { @@ -261,7 +271,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 { @@ -276,3 +286,4 @@ void BufferedFd::onWriteCallback(short) } } + diff --git a/modules/network/buffered_fd.h b/modules/network/buffered_fd.h index fa2b13f1020226757edec41780d23614c9f48e3d..10fe5dadac02f662e47dc24de3c50f8887e7448f 100644 --- a/modules/network/buffered_fd.h +++ b/modules/network/buffered_fd.h @@ -87,12 +87,17 @@ 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 doReadv(const struct iovec *iov, int iovcnt); + virtual ssize_t doWrite(const void *data, size_t size); + private: void onReadCallback(short); void onWriteCallback(short); private: - event::Loop *wp_loop_ = nullptr; //! 事件驱动 + 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 4f2c5af4f0c8f86a33c82987e976c897ed9b7f99..db729dcdc5434f13fa5c644675c527263c1b59ee 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/stdio_stream.cpp b/modules/network/stdio_stream.cpp index 681defaf6ae112e5cafbc24fb830a4e7f46f2769..57b5220148b937f26d480aa6b4e0deee90c82553 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 f41c1db5b05fd885803b73686e98752c7119cf71..31b36856fb75d66509278cd12c8ee32f0ce0ae25 100644 --- a/modules/network/tcp_acceptor.cpp +++ b/modules/network/tcp_acceptor.cpp @@ -29,16 +29,14 @@ #include #include -#include "tcp_connection.h" - #undef MODULE_ID #define MODULE_ID "tbox.tcp" 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() @@ -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 7a398de3555b0c2c068439201816aa17397e57ce..05b3083db1b766dc2d9303e2eb21cc6ccceb1664 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 07d0a1904b68e80cc901140055406d48befc9372..8da49a5f729d4ebec5d0be58976a09f093b99721 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 "tls_factory_entry.h" #undef MODULE_ID #define MODULE_ID "tbox.tcp" @@ -45,19 +48,21 @@ 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; }; -TcpClient::TcpClient(event::Loop *wp_loop) : - d_(new Data) +TcpClient::TcpClient(event::Loop *wp_loop) + : d_(new Data) { 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,44 @@ 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_; } +bool TcpClient::setTlsConfig(const TlsConfig &config) +{ + if (d_->state != State::kNone) { + LogWarn("cannot set TLS config after initialization"); + return false; + } + + if (!config.isValid()) { + LogWarn("invalid TLS config"); + 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 = tls_factory; + d_->sp_connector = d_->sp_factory->createConnector(d_->wp_loop); + + return true; +} + 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 9cbcca1c2b02bb50ac8380c75367da5d0b9e0e73..7b55d7aa4b4c46ebe98ebad7596850a91d6d43be 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() 之前调用) + bool setTlsConfig(const TlsConfig &config); + bool start(); //!< 开始连接服务端 void stop(); //!< 如果没有连接则成,则停止连接;否则断开连接 diff --git a/modules/network/tcp_connection.cpp b/modules/network/tcp_connection.cpp index 8cdb1e03045e345c227a103faa8f81497f2a09e4..63c3d321e4c0e6e14f91ce4ab6621805d2e31204 100644 --- a/modules/network/tcp_connection.cpp +++ b/modules/network/tcp_connection.cpp @@ -30,16 +30,17 @@ 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_(new BufferedFd(wp_loop)), - peer_addr_(peer_addr) +TcpConnection::TcpConnection(event::Loop *wp_loop, const SockAddr &peer_addr) + : wp_loop_(wp_loop) + , peer_addr_(peer_addr) +{ + //! sp_buffered_fd_ 由子类在构造函数中创建,然后调用 setupBufferedFd() +} + +void TcpConnection::setupBufferedFd() { - sp_buffered_fd_->initialize(fd); 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 +63,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 +72,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 5c290a53309db71939643475aa4102c90afae0e7..179da3cfddd6914af5c9aeddeb8b6d5bb2a16b58 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,16 +71,22 @@ class TcpConnection : public ByteStream { virtual Buffer* getReceiveBuffer() override; protected: - void onSocketClosed(); - void onReadError(int errnum); + //! 基类构造函数,子类需在构造后自行创建 sp_buffered_fd_ 并调用 setupBufferedFd() + explicit TcpConnection(event::Loop *wp_loop, const SockAddr &peer_addr); - private: - explicit TcpConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr); - void enable(); + //! 初始化 BufferedFd 的回调(在子类创建 sp_buffered_fd_ 后调用) + void setupBufferedFd(); - private: + //! 断开连接的具体操作 + //! 子类可覆写此方法以在断开前执行额外操作(如 SSL_shutdown) + virtual bool doDisconnect() = 0; + + //! 子类可覆写此方法以在 shutdown 时执行额外操作 + virtual bool doShutdown(int howto) = 0; + + protected: event::Loop *wp_loop_; - BufferedFd *sp_buffered_fd_; + BufferedFd *sp_buffered_fd_ = nullptr; SockAddr peer_addr_; DisconnectedCallback disconnected_cb_; @@ -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 47baf61facaf906485d65bf98daab117e85978e0..c5d4e9dae3746701119c640556eaa664a29ada18 100644 --- a/modules/network/tcp_connector.cpp +++ b/modules/network/tcp_connector.cpp @@ -25,17 +25,15 @@ #include #include -#include "tcp_connection.h" - #undef MODULE_ID #define MODULE_ID "tbox.tcp" 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() @@ -170,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); @@ -286,14 +285,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 c303bf2c95ba50eaa78ff10cc5af48a5ef2f7869..3db2c86069655800a860b354241f5897753b56bc 100644 --- a/modules/network/tcp_connector.h +++ b/modules/network/tcp_connector.h @@ -74,6 +74,14 @@ 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 +93,7 @@ class TcpConnector { void onSocketWritable(); //!< 当连接成功时的处理 void onDelayTimeout(); //!< 当等待延时到期后的处理 - private: + protected: event::Loop *wp_loop_ = nullptr; State state_ = State::kNone; //! 当前状态 @@ -108,5 +116,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 0000000000000000000000000000000000000000..b642efd0a4791eb15ba102e83a7df90758bf50a2 --- /dev/null +++ b/modules/network/tcp_factory.h @@ -0,0 +1,44 @@ +/* + * .============. + * // 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 bool initialize() = 0; + + 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 0000000000000000000000000000000000000000..79d9aefaeb62e962934412cceb90103f49a3d188 --- /dev/null +++ b/modules/network/tcp_raw_acceptor.cpp @@ -0,0 +1,57 @@ +/* + * .============. + * // 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 0000000000000000000000000000000000000000..58e0553c3faabcdd71649b568d07688d34c555b1 --- /dev/null +++ b/modules/network/tcp_raw_acceptor.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_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 0000000000000000000000000000000000000000..e7b2d57ef4a55926b833752e8491cffc244c638b --- /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, 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 0000000000000000000000000000000000000000..b5b221d8e951bc8a0f4b003b2176c0adee46cb72 --- /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 0000000000000000000000000000000000000000..22048f3a98c9ad4bd82a979da70391c8b8aa2fb7 --- /dev/null +++ b/modules/network/tcp_raw_connector.cpp @@ -0,0 +1,57 @@ +/* + * .============. + * // 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 0000000000000000000000000000000000000000..6193c25fe9c3ead4790dfe2db5b7bec3fac174a3 --- /dev/null +++ b/modules/network/tcp_raw_connector.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_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 0000000000000000000000000000000000000000..d2f74f81bbe0c994a7be68b16b94ded7a7e2a77f --- /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 0000000000000000000000000000000000000000..1780c32607fdb4b787dcf0fc34af0a253bfaa026 --- /dev/null +++ b/modules/network/tcp_raw_factory.h @@ -0,0 +1,39 @@ +/* + * .============. + * // 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 bool initialize() override { return true; } + 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 b870441916e199ba0e0bc8a06087dd6e8f932082..bb7fbe16f284f1398060234f3b3679f7f5345b88 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 "tls_factory_entry.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,44 @@ TcpServer::~TcpServer() cleanup(); CHECK_DELETE_RESET_OBJ(d_->sp_acceptor); + CHECK_DELETE_RESET_OBJ(d_->sp_factory); delete d_; } +bool TcpServer::setTlsConfig(const TlsConfig &config) +{ + if (d_->state != State::kNone) { + LogWarn("cannot set TLS config after initialization"); + return false; + } + + if (!config.isValid()) { + LogWarn("invalid TLS config"); + 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 = tls_factory; + d_->sp_acceptor = d_->sp_factory->createAcceptor(d_->wp_loop); + + return true; +} + 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 8a7c121646a58087ad6658cd5fe2ebe58e85ee54..55f53223099e28044f43d175c163d9722f172894 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() 之前调用) + bool setTlsConfig(const TlsConfig &config); + using ConnectedCallback = std::function; using DisconnectedCallback = std::function; using ReceiveCallback = std::function; diff --git a/modules/network/tls_config.cpp b/modules/network/tls_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d5ead781007da9d54975ff0ac133e172cd3975a --- /dev/null +++ b/modules/network/tls_config.cpp @@ -0,0 +1,48 @@ +/* + * .============. + * // 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 +{ + //! 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()) { + LogErr("key_file is set but cert_file is not"); + 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_config.h b/modules/network/tls_config.h new file mode 100644 index 0000000000000000000000000000000000000000..9a4ec20d30c8aa2b4c0abd90a561d48ddf3d153f --- /dev/null +++ b/modules/network/tls_config.h @@ -0,0 +1,52 @@ +/* + * .============. + * // 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 场景:必须设置,用于向 client 出示证书 + //! Client 场景:可选设置,用于双向 TLS(mTLS)向 server 出示证书 + std::string cert_file; //!< 本端证书文件 + std::string 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/network/tls_factory_entry.cpp b/modules/network/tls_factory_entry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bfeba18e7bae108e57d2ba13e1e001cb37c5c518 --- /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, const TlsConfig &) +{ + 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 0000000000000000000000000000000000000000..a7466356743cef8e37fb53850c46c9d36be4b713 --- /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/CMakeLists.txt b/modules/network_tls/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1385e0f2bc0d621a86b3833d3619340dc24ca14e --- /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/Makefile b/modules/network_tls/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..65203d38d69b99e525f62a7f897bb90bb55dc293 --- /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_tls/buffered_ssl_fd.cpp b/modules/network_tls/buffered_ssl_fd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccd6d8a1ec750129feabcb4113f02cb8f281b8a2 --- /dev/null +++ b/modules/network_tls/buffered_ssl_fd.cpp @@ -0,0 +1,239 @@ +/* + * .============. + * // 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 "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 + 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; + } + } + + //! 所有 iov 都被填满,但 SSL 内部缓冲可能还有 pending 数据 + //! 上层 onReadCallback 的 while 循环会继续调用 doReadv 来提取 + return total_read; +} + +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:对端关闭了连接(未发送 close_notify) + return 0; + } + //! 其他系统错误,errno 已由系统设置 + return -1; + } + + 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; +} + +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_tls/buffered_ssl_fd.h b/modules/network_tls/buffered_ssl_fd.h new file mode 100644 index 0000000000000000000000000000000000000000..b71b528eced9014a4bd82a1497cd035a0c37c259 --- /dev/null +++ b/modules/network_tls/buffered_ssl_fd.h @@ -0,0 +1,64 @@ +/* + * .============. + * // 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 + +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 doReadv(const struct iovec *iov, int iovcnt); + virtual ssize_t doWrite(const void *data, size_t size) 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); + + 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_tls/tcp_tls_acceptor.cpp b/modules/network_tls/tcp_tls_acceptor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ed46c180753a00d202551b3bac35ee3c3e3d0be --- /dev/null +++ b/modules/network_tls/tcp_tls_acceptor.cpp @@ -0,0 +1,209 @@ +/* + * .============. + * // 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_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() +{ + //! 如果握手还在进行中,需要清理 + 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 *, SocketFd, const SockAddr &) +{ + //! 注意:此方法不直接使用,由 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_fd_.swap(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) +{ + //! 继续 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) { + //! 需要继续等待 + //! kOneshot 事件触发后已自动 disable,可以直接 reinitialize,无需重新创建 FdEvent + //! 这避免了在回调中删除 FdEvent 导致的 assert 失败 + short next_events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; + sp_handshake_ev_->initialize(handshake_fd_.get(), next_events, event::Event::Mode::kOneshot); + 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); + onSslHandshakeFail(); + } +} + +void TcpTlsAcceptor::onSslHandshakeSuccess() +{ + RECORD_SCOPE(); + + //! 清理握手相关的 FdEvent + //! 不能在回调中直接删除 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_; + 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() +{ + //! 清理握手相关的资源 + //! 不能在回调中直接删除 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; + handshake_fd_.close(); + + //! 握手失败不影响其他连接,仅关闭当前 fd + LogNotice("TLS handshake fail, close connection"); +} + +} +} diff --git a/modules/network_tls/tcp_tls_acceptor.h b/modules/network_tls/tcp_tls_acceptor.h new file mode 100644 index 0000000000000000000000000000000000000000..bde74b5c8fd288db0d187ff5c3715473976979ab --- /dev/null +++ b/modules/network_tls/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 +#include +#include + +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_tls/tcp_tls_connection.cpp b/modules/network_tls/tcp_tls_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d6197531c0b3fb212cd63be04acec52b6630b233 --- /dev/null +++ b/modules/network_tls/tcp_tls_connection.cpp @@ -0,0 +1,79 @@ +/* + * .============. + * // 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_tls" + +namespace tbox { +namespace network { + +TcpTlsConnection::TcpTlsConnection(event::Loop *wp_loop, SocketFd fd, const SockAddr &peer_addr, SSL *ssl) + : TcpConnection(wp_loop, peer_addr) +{ + //! 创建 BufferedSslFd 并初始化 + auto *ssl_fd = new BufferedSslFd(wp_loop); + ssl_fd->initialize(fd, ssl); + sp_buffered_fd_ = ssl_fd; + setupBufferedFd(); +} + +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_tls/tcp_tls_connection.h b/modules/network_tls/tcp_tls_connection.h new file mode 100644 index 0000000000000000000000000000000000000000..92e5084eaca86be29283939cfa67173ec09af29e --- /dev/null +++ b/modules/network_tls/tcp_tls_connection.h @@ -0,0 +1,47 @@ +/* + * .============. + * // 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 +#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; +}; + +} +} +#endif //TBOX_NETWORK_TCP_TLS_CONNECTION_H_20260616 diff --git a/modules/network_tls/tcp_tls_connector.cpp b/modules/network_tls/tcp_tls_connector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52ac1377501917057c9cce4cf985297eecba68b2 --- /dev/null +++ b/modules/network_tls/tcp_tls_connector.cpp @@ -0,0 +1,220 @@ +/* + * .============. + * // 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_connector.h" +#include "tcp_tls_connection.h" + +#include +#include +#include +#include +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp_tls" + +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 *, SocketFd, const SockAddr &) +{ + //! 注意:此方法只在 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_fd_.swap(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) +{ + //! 继续 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) { + //! 需要继续等待 + //! kOneshot 事件触发后已自动 disable,可以直接 reinitialize,无需重新创建 FdEvent + //! 这避免了在回调中删除 FdEvent 导致的 assert 失败 + short next_events = (ssl_error == SSL_ERROR_WANT_READ) ? event::FdEvent::kReadEvent : event::FdEvent::kWriteEvent; + sp_handshake_ev_->initialize(handshake_fd_.get(), next_events, event::Event::Mode::kOneshot); + 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); + onSslHandshakeFail(); + } +} + +void TcpTlsConnector::onSslHandshakeSuccess() +{ + RECORD_SCOPE(); + + //! 清理握手相关的 FdEvent + //! 不能在回调中直接删除 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_; + 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() +{ + //! 清理握手相关的资源 + //! 不能在回调中直接删除 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; + handshake_fd_.close(); + + //! SSL 握手失败视为连接失败,触发重连逻辑 + LogNotice("TLS handshake fail, treat as connection fail"); + onConnectFail(); +} + +} +} diff --git a/modules/network_tls/tcp_tls_connector.h b/modules/network_tls/tcp_tls_connector.h new file mode 100644 index 0000000000000000000000000000000000000000..ce568acb6c17080913290a359546ae2702c8dcfa --- /dev/null +++ b/modules/network_tls/tcp_tls_connector.h @@ -0,0 +1,67 @@ +/* + * .============. + * // 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_CONNECTOR_H_20260616 +#define TBOX_NETWORK_TCP_TLS_CONNECTOR_H_20260616 + +#include + +#include +#include +#include + +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_tls/tcp_tls_factory.cpp b/modules/network_tls/tcp_tls_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9cc37096d1066e8ac31e4df153f441b9be315dff --- /dev/null +++ b/modules/network_tls/tcp_tls_factory.cpp @@ -0,0 +1,194 @@ +/* + * .============. + * // 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 +#include +#include + +#undef MODULE_ID +#define MODULE_ID "tbox.tcp_tls" + +namespace tbox { +namespace network { + +namespace { +//! 加载证书与私钥到 SSL_CTX +//! 成功返回 true,失败返回 false +bool LoadCertAndKey(SSL_CTX *ctx, const std::string &cert_file, const std::string &key_file) +{ + 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; +} + +//! 加载 CA 证书到 SSL_CTX +//! 成功返回 true,失败返回 false +bool LoadCaCert(SSL_CTX *ctx, const TlsConfig &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* CreateClientSslCtx(const TlsConfig &tls_config) +{ + SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); + if (ctx == nullptr) { + LogErr("SSL_CTX_new(TLS_client_method) fail"); + 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()) { + if (!LoadCaCert(ctx, tls_config)) + 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"); + return nullptr; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr); + } else { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); + } + + //! 加载本端证书和密钥(可选,用于双向 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* CreateServerSslCtx(const TlsConfig &tls_config) +{ + SSL_CTX *ctx = SSL_CTX_new(TLS_server_method()); + if (ctx == nullptr) { + LogErr("SSL_CTX_new(TLS_server_method) fail"); + 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 (!LoadCertAndKey(ctx, tls_config.cert_file, tls_config.key_file)) + return nullptr; + + } else { + LogErr("server cert_file and key_file must be set"); + return nullptr; + } + + //! 加载 CA 证书(可选,用于验证 client - 双向 TLS) + if (!tls_config.ca_file.empty() || !tls_config.ca_path.empty()) { + if (!LoadCaCert(ctx, tls_config)) + 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); + } + } + + 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_tls/tcp_tls_factory.h b/modules/network_tls/tcp_tls_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..e842b36a3094bd27ab2dc8989d765f793d19a9f6 --- /dev/null +++ b/modules/network_tls/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 +#include +#include + +namespace tbox { +namespace network { + +//! TLS 工厂 +//! 根据 TlsRole 创建对应的 SSL_CTX,仅持有本端所需的那一个 +//! kClient 角色:创建 client SSL_CTX,仅支持 createConnector +//! kServer 角色:创建 server SSL_CTX,仅支持 createAcceptor +class TcpTlsFactory : public TcpFactory { + public: + 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: + TlsRole role_; + TlsConfig tls_config_; + SSL_CTX *ssl_ctx_ = nullptr; +}; + +} +} +#endif //TBOX_NETWORK_TCP_TLS_FACTORY_H_20260616 diff --git a/modules/network_tls/tls_factory_entry.cpp b/modules/network_tls/tls_factory_entry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ad97bfe7ab9e2f7bc4e0cc5e185f3f203485089 --- /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 a9e994dc6df5d016b4f4f504cd560dafd0806714..a92f34fbe5df0edac18dea463a1e9ab69e2e1bf4 100644 --- a/modules/run/Makefile +++ b/modules/run/Makefile @@ -39,4 +39,8 @@ LDFLAGS += \ -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/websocket/client/ws_client.cpp b/modules/websocket/client/ws_client.cpp index 3f5bb63a5d597e5824d270ef2a8b980dd8978bba..b22c4bf36b03ec382323c98f955a1790a8c51019 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 a56b69d2f6ca5e046b041319b375c641a71a5024..69cb89ef059585f9639b5e54b2c0d69884cc8a8b 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 9d489a80c99f206d75ff447ba7478eb85f20546f..85589d21fe4dee1728d7d21a33eee2e807f3c641 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; - //! 创建 TcpConnector(保持存活,供重连使用) - sp_connector_ = new network::TcpConnector(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(network::TlsRole::kClient, 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 e0f9e98884a1f37e2d7ad7669bbeda43d2fb7404..2b4ece478efb139af6db50bc634c6af4b4ee14bf 100644 --- a/modules/websocket/client/ws_client_impl.h +++ b/modules/websocket/client/ws_client_impl.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include "ws_client.h" @@ -56,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); @@ -110,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; diff --git a/version.mk b/version.mk index 28229c818040dc576883ae38f27aee9fe59ff8bd..9c364666e36081b5b893c6a00017d6865359e98d 100644 --- a/version.mk +++ b/version.mk @@ -20,5 +20,5 @@ # TBOX版本号 TBOX_VERSION_MAJOR := 1 -TBOX_VERSION_MINOR := 14 +TBOX_VERSION_MINOR := 15 TBOX_VERSION_REVISION := 4