From 6081819802972c716745a44e3521ccb3b3cf7b1a Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 11 Apr 2020 13:17:32 +0200 Subject: Support IPv6 --- TODO | 1 + config.cpp | 50 ++++++++++++++++++++++++++++++++------------------ https.cpp | 27 ++++++++++++++++++++------- https.h | 1 - plugin.cpp | 1 - response.cpp | 15 +++++++++++++++ server.cpp | 12 +++++++++++- webserver.conf | 17 +++++++++++++++++ 8 files changed, 96 insertions(+), 28 deletions(-) diff --git a/TODO b/TODO index a0de147..98e1990 100644 --- a/TODO +++ b/TODO @@ -2,6 +2,7 @@ Certbot: https://certbot.eff.org/lets-encrypt/debianbuster-other Webbox Debian 10 +Request properties: Remote Address, e.g. [::1]:8081 -> ipv6 / ipv4 Speed up DocRoot, use string_view read: The socket was closed due to a timeout statistics diff --git a/config.cpp b/config.cpp index 78f33e9..8d6704c 100644 --- a/config.cpp +++ b/config.cpp @@ -4,11 +4,27 @@ #include #include +#include +#include #include namespace pt = boost::property_tree; using namespace std::string_literals; +namespace { + + void RemovePortFromHostname(std::string& hostname) + { + // besides hostnames and IPv4 addresses, consider IPv6 addresses: [xx::yy]:8080 + // so only remove ":N" if after "]" + size_t pos = hostname.find_last_of(":]"); + if (pos != hostname.npos && hostname[pos] == ':') { + hostname = hostname.substr(0, pos); + } + } + +} // anonymous namespace + void Config::readConfigfile(std::string filename) { if (filename == "") { @@ -185,10 +201,7 @@ std::string Config::DocRoot(const Socket& socket, const std::string& requested_h std::string result; size_t path_len{0}; // find longest matching prefix - auto pos {host.find(':')}; - if (pos != host.npos) { - host = host.substr(0, pos); - } + RemovePortFromHostname(host); for (const auto& site: m_sites) { if (std::find(socket.serve_sites.begin(), socket.serve_sites.end(), site.name) != socket.serve_sites.end()) { @@ -197,7 +210,12 @@ std::string Config::DocRoot(const Socket& socket, const std::string& requested_h for (const auto& path: site.paths) { if (boost::starts_with(requested_path, path.requested) && path.requested.size() > path_len) { path_len = path.requested.size(); - result = path.params.at("target"); + try { + result = path.params.at("target"); + } catch (const std::out_of_range& ex) { + std::cout << "Out of range at Config::DocRoot(): target" << std::endl; + std::rethrow_exception(std::current_exception()); + } } } } @@ -215,10 +233,7 @@ std::string Config::GetPlugin(const Socket& socket, const std::string& requested std::string result; size_t path_len{0}; - auto pos {host.find(':')}; - if (pos != host.npos) { - host = host.substr(0, pos); - } + RemovePortFromHostname(host); for (const auto& site: m_sites) { if (std::find(socket.serve_sites.begin(), socket.serve_sites.end(), site.name) != socket.serve_sites.end()) { @@ -227,7 +242,12 @@ std::string Config::GetPlugin(const Socket& socket, const std::string& requested for (const auto& path: site.paths) { if (boost::starts_with(requested_path, path.requested) && path.requested.size() > path_len) { path_len = path.requested.size(); - result = path.params.at("plugin"); + try { + result = path.params.at("plugin"); + } catch (const std::out_of_range& ex) { + std::cout << "Out of range at Config::DocRoot(): target" << std::endl; + std::rethrow_exception(std::current_exception()); + } } } } @@ -245,10 +265,7 @@ std::string Config::GetPluginPath(const Socket& socket, const std::string& reque std::string result; size_t path_len{0}; - auto pos {host.find(':')}; - if (pos != host.npos) { - host = host.substr(0, pos); - } + RemovePortFromHostname(host); for (const auto& site: m_sites) { if (std::find(socket.serve_sites.begin(), socket.serve_sites.end(), site.name) != socket.serve_sites.end()) { @@ -275,10 +292,7 @@ std::string Config::GetRelativePath(const Socket& socket, const std::string& req std::string result; size_t path_len{0}; - auto pos {host.find(':')}; - if (pos != host.npos) { - host = host.substr(0, pos); - } + RemovePortFromHostname(host); for (const auto& site: m_sites) { if (std::find(socket.serve_sites.begin(), socket.serve_sites.end(), site.name) != socket.serve_sites.end()) { diff --git a/https.cpp b/https.cpp index facc533..a263a54 100644 --- a/https.cpp +++ b/https.cpp @@ -573,9 +573,16 @@ int servername_callback(SSL *s, int *al, void *arg) HTTPS::Server::ctx_type* ctx_map = (HTTPS::Server::ctx_type*)arg; - ssl::context& ctx = *(ctx_map->at(server_name)); + auto it {ctx_map->find(server_name)}; + std::shared_ptr ctx{}; + if (it != ctx_map->end()) { + ctx = it->second; + } else { + std::cout << "Warning: server_name " << server_name << " not found in list of prepared contexts. Using dummy ctx." << std::endl; + ctx = ctx_map->at(""); + } - SSL_set_SSL_CTX(s, ctx.native_handle()); + SSL_set_SSL_CTX(s, ctx->native_handle()); return SSL_TLSEXT_ERR_OK; } @@ -588,6 +595,15 @@ namespace HTTPS { Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins) : ::Server(config, ioc, socket, plugins) { + // initial dummy, before we can add specific ctx w/ certificate + std::shared_ptr ctx_dummy{std::make_shared(tls_method)}; + load_server_certificate(*ctx_dummy, "", ""); + //SSL_CTX_set_client_hello_cb(ctx_dummy->native_handle(), servername_callback, &m_ctx); + SSL_CTX_set_tlsext_servername_callback(ctx_dummy->native_handle(), servername_callback); + SSL_CTX_set_tlsext_servername_arg(ctx_dummy->native_handle(), &m_ctx); + m_ctx.emplace("", ctx_dummy); + + // import the real certificates for (const auto& serve_site: socket.serve_sites) { for (const auto& site: config.Sites()) { if (site.name == serve_site) { @@ -596,6 +612,7 @@ Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socke std::cout << "Creating SSL context/cert for site " << serve_site << " on port " << socket.port << std::endl; load_server_certificate(*ctx, site.cert_path, site.key_path); + //SSL_CTX_set_client_hello_cb(ctx->native_handle(), servername_callback, &m_ctx); SSL_CTX_set_tlsext_servername_callback(ctx->native_handle(), servername_callback); SSL_CTX_set_tlsext_servername_arg(ctx->native_handle(), &m_ctx); @@ -617,14 +634,10 @@ int Server::start() auto const address = net::ip::make_address(m_socket.address); auto const port = static_cast(std::atoi(m_socket.port.data())); - load_server_certificate(m_ctx_dummy, "", ""); // initial dummy, before we can add specific ctx w/ certificate - SSL_CTX_set_tlsext_servername_callback(m_ctx_dummy.native_handle(), servername_callback); - SSL_CTX_set_tlsext_servername_arg(m_ctx_dummy.native_handle(), &m_ctx); - // Create and launch a listening port std::make_shared( m_ioc, - m_ctx_dummy, + *m_ctx[""], tcp::endpoint{address, port}, *this)->run(); diff --git a/https.h b/https.h index e2aabbb..864f379 100644 --- a/https.h +++ b/https.h @@ -38,7 +38,6 @@ public: private: ctx_type m_ctx; - ssl::context m_ctx_dummy{tls_method}; // Initial use, will be replaced by host specific context (with specific certificate) public: Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins); diff --git a/plugin.cpp b/plugin.cpp index a29e2f0..28fdb62 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -62,7 +62,6 @@ void PluginLoader::load_plugins() } } } - } bool PluginLoader::validate_config() diff --git a/response.cpp b/response.cpp index 70c93b1..4d6aaaf 100644 --- a/response.cpp +++ b/response.cpp @@ -109,6 +109,17 @@ mime_type(beast::string_view path) return "application/text"; } +// Used to return errors by generating response page and HTTP status code +response_type HttpStatus(std::string status, std::string message, response_type& res) +{ + res.result(unsigned(stoul(status))); + res.set(http::field::content_type, "text/html"); + res.body() = "

"s + VersionString + " Error

"s + status + " "s + message + "

"s; + res.prepare_payload(); + + return res; +} + } // anonymous namespace response_type generate_response(request_type& req, Server& server) @@ -121,6 +132,10 @@ response_type generate_response(request_type& req, Server& server) std::string host{req["host"]}; std::string target{req.target()}; std::string plugin_name { server.GetConfig().GetPlugin(server.GetSocket(), host, target)}; + if (plugin_name == "") { + return HttpStatus("400", "Bad request: Host "s + host + ":"s + target + " unknown"s, res); + } + plugin_type plugin{server.GetPlugin(plugin_name)}; auto GetServerParamFunction {std::function(std::bind(GetServerParam, _1, std::ref(server)))}; diff --git a/server.cpp b/server.cpp index 26feabc..395be7d 100644 --- a/server.cpp +++ b/server.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include @@ -92,6 +94,14 @@ const Socket& Server::GetSocket() plugin_type Server::GetPlugin(const std::string& name) { - return m_plugins.at(name); // Config validation made sure that we will find it here. For safety, a thrown exception will be caught in webserver.cpp + try { + return m_plugins.at(name); + } catch (const std::out_of_range& ex) { + std::cout << "Out of range at Server::GetPlugin(): " << name << std::endl; + std::rethrow_exception(std::current_exception()); + } catch (...) { + std::cout << "Unknown exception at Server::GetPlugin(): " << name << std::endl; + std::rethrow_exception(std::current_exception()); + } } diff --git a/webserver.conf b/webserver.conf index a50dc6d..44dba49 100644 --- a/webserver.conf +++ b/webserver.conf @@ -13,6 +13,9 @@ lists.antcom.de antcom.de www.antcom.de + ip6-localhost + 127.0.0.1 + [::1] static-files /home/ernie/homepage/test @@ -53,11 +56,25 @@ reichwein.it --> + +
::1
+ 8080 + http + +
127.0.0.1
8081 https
+ +
::1
+ 8081 + https +
127.0.0.1
443 -- cgit v1.2.3