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