diff options
author | Roland Reichwein <mail@reichwein.it> | 2020-04-11 13:17:32 +0200 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2020-04-11 13:17:32 +0200 |
commit | 6081819802972c716745a44e3521ccb3b3cf7b1a (patch) | |
tree | d1dafa4b821acff243f1613d2dd483e49782ff0b | |
parent | 07f01d1ab5e68fc042356fd90fa07c199791b29c (diff) |
Support IPv6
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | config.cpp | 50 | ||||
-rw-r--r-- | https.cpp | 27 | ||||
-rw-r--r-- | https.h | 1 | ||||
-rw-r--r-- | plugin.cpp | 1 | ||||
-rw-r--r-- | response.cpp | 15 | ||||
-rw-r--r-- | server.cpp | 12 | ||||
-rw-r--r-- | webserver.conf | 17 |
8 files changed, 96 insertions, 28 deletions
@@ -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 @@ -4,11 +4,27 @@ #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/xml_parser.hpp> +#include <algorithm> +#include <exception> #include <iostream> 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()) { @@ -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<ssl::context> 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<ssl::context> ctx_dummy{std::make_shared<ssl::context>(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<unsigned short>(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<listener>( m_ioc, - m_ctx_dummy, + *m_ctx[""], tcp::endpoint{address, port}, *this)->run(); @@ -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); @@ -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() = "<html><body><h1>"s + VersionString + " Error</h1><p>"s + status + " "s + message + "</p></body></html>"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::string(const std::string& key)>(std::bind(GetServerParam, _1, std::ref(server)))}; @@ -18,6 +18,8 @@ #include <boost/asio/strand.hpp> #include <boost/config.hpp> +#include <exception> +#include <iostream> #include <thread> #include <vector> @@ -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 @@ <host>lists.antcom.de</host> <host>antcom.de</host> <host>www.antcom.de</host> + <host>ip6-localhost</host> + <host>127.0.0.1</host> + <host>[::1]</host> <path requested="/"> <plugin>static-files</plugin> <target>/home/ernie/homepage/test</target> @@ -54,11 +57,25 @@ --> </socket> <socket> + <address>::1</address> + <port>8080</port> + <protocol>http</protocol> + <!-- + <site>antcom.de</site> + <site>reichwein.it</site> + --> + </socket> + <socket> <address>127.0.0.1</address> <port>8081</port> <protocol>https</protocol> </socket> <socket> + <address>::1</address> + <port>8081</port> + <protocol>https</protocol> + </socket> + <socket> <address>127.0.0.1</address> <port>443</port> <protocol>https</protocol> |