summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2020-04-11 13:17:32 +0200
committerRoland Reichwein <mail@reichwein.it>2020-04-11 13:17:32 +0200
commit6081819802972c716745a44e3521ccb3b3cf7b1a (patch)
treed1dafa4b821acff243f1613d2dd483e49782ff0b
parent07f01d1ab5e68fc042356fd90fa07c199791b29c (diff)
Support IPv6
-rw-r--r--TODO1
-rw-r--r--config.cpp50
-rw-r--r--https.cpp27
-rw-r--r--https.h1
-rw-r--r--plugin.cpp1
-rw-r--r--response.cpp15
-rw-r--r--server.cpp12
-rw-r--r--webserver.conf17
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 <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()) {
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<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();
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() = "<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)))};
diff --git a/server.cpp b/server.cpp
index 26feabc..395be7d 100644
--- a/server.cpp
+++ b/server.cpp
@@ -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>