1 #pragma once 2 3 #include "http_connection.hpp" 4 #include "logging.hpp" 5 #include "ssl_key_handler.hpp" 6 7 #include <boost/asio/ip/address.hpp> 8 #include <boost/asio/ip/tcp.hpp> 9 #include <boost/asio/signal_set.hpp> 10 #include <boost/asio/ssl/context.hpp> 11 #include <boost/asio/steady_timer.hpp> 12 #include <boost/beast/ssl/ssl_stream.hpp> 13 14 #include <atomic> 15 #include <chrono> 16 #include <cstdint> 17 #include <filesystem> 18 #include <future> 19 #include <memory> 20 #include <utility> 21 #include <vector> 22 23 namespace crow 24 { 25 26 template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket> 27 class Server 28 { 29 public: 30 Server(Handler* handlerIn, 31 std::unique_ptr<boost::asio::ip::tcp::acceptor>&& acceptorIn, 32 std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn, 33 std::shared_ptr<boost::asio::io_context> io = 34 std::make_shared<boost::asio::io_context>()) : 35 ioService(std::move(io)), 36 acceptor(std::move(acceptorIn)), 37 signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn), 38 adaptorCtx(std::move(adaptorCtxIn)) 39 {} 40 41 Server(Handler* handlerIn, const std::string& bindaddr, uint16_t port, 42 const std::shared_ptr<boost::asio::ssl::context>& adaptorCtxIn, 43 const std::shared_ptr<boost::asio::io_context>& io = 44 std::make_shared<boost::asio::io_context>()) : 45 Server(handlerIn, 46 std::make_unique<boost::asio::ip::tcp::acceptor>( 47 *io, boost::asio::ip::tcp::endpoint( 48 boost::asio::ip::make_address(bindaddr), port)), 49 adaptorCtxIn, io) 50 {} 51 52 Server(Handler* handlerIn, int existingSocket, 53 const std::shared_ptr<boost::asio::ssl::context>& adaptorCtxIn, 54 const std::shared_ptr<boost::asio::io_context>& io = 55 std::make_shared<boost::asio::io_context>()) : 56 Server(handlerIn, 57 std::make_unique<boost::asio::ip::tcp::acceptor>( 58 *io, boost::asio::ip::tcp::v6(), existingSocket), 59 adaptorCtxIn, io) 60 {} 61 62 void updateDateStr() 63 { 64 time_t lastTimeT = time(nullptr); 65 tm myTm{}; 66 67 gmtime_r(&lastTimeT, &myTm); 68 69 dateStr.resize(100); 70 size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1, 71 "%a, %d %b %Y %H:%M:%S GMT", &myTm); 72 dateStr.resize(dateStrSz); 73 } 74 75 void run() 76 { 77 loadCertificate(); 78 updateDateStr(); 79 80 getCachedDateStr = [this]() -> std::string { 81 static std::chrono::time_point<std::chrono::steady_clock> 82 lastDateUpdate = std::chrono::steady_clock::now(); 83 if (std::chrono::steady_clock::now() - lastDateUpdate >= 84 std::chrono::seconds(10)) 85 { 86 lastDateUpdate = std::chrono::steady_clock::now(); 87 updateDateStr(); 88 } 89 return dateStr; 90 }; 91 92 BMCWEB_LOG_INFO("bmcweb server is running, local endpoint {}", 93 acceptor->local_endpoint().address().to_string()); 94 startAsyncWaitForSignal(); 95 doAccept(); 96 } 97 98 void loadCertificate() 99 { 100 #ifdef BMCWEB_ENABLE_SSL 101 namespace fs = std::filesystem; 102 // Cleanup older certificate file existing in the system 103 fs::path oldCert = "/home/root/server.pem"; 104 if (fs::exists(oldCert)) 105 { 106 fs::remove("/home/root/server.pem"); 107 } 108 fs::path certPath = "/etc/ssl/certs/https/"; 109 // if path does not exist create the path so that 110 // self signed certificate can be created in the 111 // path 112 if (!fs::exists(certPath)) 113 { 114 fs::create_directories(certPath); 115 } 116 fs::path certFile = certPath / "server.pem"; 117 BMCWEB_LOG_INFO("Building SSL Context file={}", certFile.string()); 118 std::string sslPemFile(certFile); 119 ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile); 120 std::shared_ptr<boost::asio::ssl::context> sslContext = 121 ensuressl::getSslContext(sslPemFile); 122 adaptorCtx = sslContext; 123 handler->ssl(std::move(sslContext)); 124 #endif 125 } 126 127 void startAsyncWaitForSignal() 128 { 129 signals.async_wait( 130 [this](const boost::system::error_code& ec, int signalNo) { 131 if (ec) 132 { 133 BMCWEB_LOG_INFO("Error in signal handler{}", ec.message()); 134 } 135 else 136 { 137 if (signalNo == SIGHUP) 138 { 139 BMCWEB_LOG_INFO("Receivied reload signal"); 140 loadCertificate(); 141 boost::system::error_code ec2; 142 acceptor->cancel(ec2); 143 if (ec2) 144 { 145 BMCWEB_LOG_ERROR( 146 "Error while canceling async operations:{}", 147 ec2.message()); 148 } 149 startAsyncWaitForSignal(); 150 } 151 else 152 { 153 stop(); 154 } 155 } 156 }); 157 } 158 159 void stop() 160 { 161 ioService->stop(); 162 } 163 164 void doAccept() 165 { 166 boost::asio::steady_timer timer(*ioService); 167 std::shared_ptr<Connection<Adaptor, Handler>> connection; 168 if constexpr (std::is_same<Adaptor, 169 boost::beast::ssl_stream< 170 boost::asio::ip::tcp::socket>>::value) 171 { 172 connection = std::make_shared<Connection<Adaptor, Handler>>( 173 handler, std::move(timer), getCachedDateStr, 174 Adaptor(*ioService, *adaptorCtx)); 175 } 176 else 177 { 178 connection = std::make_shared<Connection<Adaptor, Handler>>( 179 handler, std::move(timer), getCachedDateStr, 180 Adaptor(*ioService)); 181 } 182 acceptor->async_accept( 183 boost::beast::get_lowest_layer(connection->socket()), 184 [this, connection](const boost::system::error_code& ec) { 185 if (!ec) 186 { 187 boost::asio::post(*ioService, 188 [connection] { connection->start(); }); 189 } 190 doAccept(); 191 }); 192 } 193 194 private: 195 std::shared_ptr<boost::asio::io_context> ioService; 196 std::function<std::string()> getCachedDateStr; 197 std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor; 198 boost::asio::signal_set signals; 199 200 std::string dateStr; 201 202 Handler* handler; 203 204 std::shared_ptr<boost::asio::ssl::context> adaptorCtx; 205 }; 206 } // namespace crow 207