1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "http_connection.hpp" 6 #include "logging.hpp" 7 #include "ssl_key_handler.hpp" 8 9 #include <boost/asio/ip/address.hpp> 10 #include <boost/asio/ip/tcp.hpp> 11 #include <boost/asio/signal_set.hpp> 12 #include <boost/asio/ssl/context.hpp> 13 #include <boost/asio/ssl/stream.hpp> 14 #include <boost/asio/steady_timer.hpp> 15 #include <boost/beast/core/stream_traits.hpp> 16 17 #include <atomic> 18 #include <chrono> 19 #include <cstdint> 20 #include <filesystem> 21 #include <future> 22 #include <memory> 23 #include <string> 24 #include <utility> 25 #include <vector> 26 27 namespace crow 28 { 29 30 template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket> 31 class Server 32 { 33 using self_t = Server<Handler, Adaptor>; 34 35 public: Server(Handler * handlerIn,boost::asio::ip::tcp::acceptor && acceptorIn,std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn,std::shared_ptr<boost::asio::io_context> io)36 Server(Handler* handlerIn, boost::asio::ip::tcp::acceptor&& acceptorIn, 37 std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn, 38 std::shared_ptr<boost::asio::io_context> io) : 39 ioService(std::move(io)), acceptor(std::move(acceptorIn)), 40 signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn), 41 adaptorCtx(std::move(adaptorCtxIn)) 42 {} 43 updateDateStr()44 void updateDateStr() 45 { 46 time_t lastTimeT = time(nullptr); 47 tm myTm{}; 48 49 gmtime_r(&lastTimeT, &myTm); 50 51 dateStr.resize(100); 52 size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1, 53 "%a, %d %b %Y %H:%M:%S GMT", &myTm); 54 dateStr.resize(dateStrSz); 55 } 56 run()57 void run() 58 { 59 loadCertificate(); 60 updateDateStr(); 61 62 getCachedDateStr = [this]() -> std::string { 63 static std::chrono::time_point<std::chrono::steady_clock> 64 lastDateUpdate = std::chrono::steady_clock::now(); 65 if (std::chrono::steady_clock::now() - lastDateUpdate >= 66 std::chrono::seconds(10)) 67 { 68 lastDateUpdate = std::chrono::steady_clock::now(); 69 updateDateStr(); 70 } 71 return dateStr; 72 }; 73 74 BMCWEB_LOG_INFO("bmcweb server is running, local endpoint {}", 75 acceptor.local_endpoint().address().to_string()); 76 startAsyncWaitForSignal(); 77 doAccept(); 78 } 79 loadCertificate()80 void loadCertificate() 81 { 82 if constexpr (BMCWEB_INSECURE_DISABLE_SSL) 83 { 84 return; 85 } 86 87 auto sslContext = ensuressl::getSslServerContext(); 88 89 adaptorCtx = sslContext; 90 handler->ssl(std::move(sslContext)); 91 } 92 startAsyncWaitForSignal()93 void startAsyncWaitForSignal() 94 { 95 signals.async_wait( 96 [this](const boost::system::error_code& ec, int signalNo) { 97 if (ec) 98 { 99 BMCWEB_LOG_INFO("Error in signal handler{}", ec.message()); 100 } 101 else 102 { 103 if (signalNo == SIGHUP) 104 { 105 BMCWEB_LOG_INFO("Receivied reload signal"); 106 loadCertificate(); 107 startAsyncWaitForSignal(); 108 } 109 else 110 { 111 stop(); 112 } 113 } 114 }); 115 } 116 stop()117 void stop() 118 { 119 ioService->stop(); 120 } 121 using Socket = boost::beast::lowest_layer_type<Adaptor>; 122 using SocketPtr = std::unique_ptr<Socket>; 123 afterAccept(SocketPtr socket,const boost::system::error_code & ec)124 void afterAccept(SocketPtr socket, const boost::system::error_code& ec) 125 { 126 if (ec) 127 { 128 BMCWEB_LOG_ERROR("Failed to accept socket {}", ec); 129 return; 130 } 131 132 boost::asio::steady_timer timer(*ioService); 133 std::shared_ptr<Connection<Adaptor, Handler>> connection; 134 135 if constexpr (std::is_same<Adaptor, 136 boost::asio::ssl::stream< 137 boost::asio::ip::tcp::socket>>::value) 138 { 139 if (adaptorCtx == nullptr) 140 { 141 BMCWEB_LOG_CRITICAL( 142 "Asked to launch TLS socket but no context available"); 143 return; 144 } 145 connection = std::make_shared<Connection<Adaptor, Handler>>( 146 handler, std::move(timer), getCachedDateStr, 147 Adaptor(std::move(*socket), *adaptorCtx)); 148 } 149 else 150 { 151 connection = std::make_shared<Connection<Adaptor, Handler>>( 152 handler, std::move(timer), getCachedDateStr, 153 Adaptor(std::move(*socket))); 154 } 155 156 boost::asio::post(*ioService, [connection] { connection->start(); }); 157 158 doAccept(); 159 } 160 doAccept()161 void doAccept() 162 { 163 if (ioService == nullptr) 164 { 165 BMCWEB_LOG_CRITICAL("IoService was null"); 166 return; 167 } 168 169 SocketPtr socket = std::make_unique<Socket>(*ioService); 170 // Keep a raw pointer so when the socket is moved, the pointer is still 171 // valid 172 Socket* socketPtr = socket.get(); 173 174 acceptor.async_accept( 175 *socketPtr, 176 std::bind_front(&self_t::afterAccept, this, std::move(socket))); 177 } 178 179 private: 180 std::shared_ptr<boost::asio::io_context> ioService; 181 std::function<std::string()> getCachedDateStr; 182 boost::asio::ip::tcp::acceptor acceptor; 183 boost::asio::signal_set signals; 184 185 std::string dateStr; 186 187 Handler* handler; 188 189 std::shared_ptr<boost::asio::ssl::context> adaptorCtx; 190 }; 191 } // namespace crow 192