]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Basic tls support
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 21 Jan 2025 14:25:13 +0000 (15:25 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 11 Feb 2025 15:28:22 +0000 (16:28 +0100)
12 files changed:
pdns/recursordist/rec-main.cc
pdns/recursordist/reczones.cc
pdns/recursordist/settings/generate.py
pdns/recursordist/settings/rust-bridge-in.rs
pdns/recursordist/settings/rust/Cargo.lock
pdns/recursordist/settings/rust/Cargo.toml
pdns/recursordist/settings/rust/build_settings
pdns/recursordist/settings/rust/src/bridge.hh
pdns/recursordist/settings/rust/src/bridge.rs
pdns/recursordist/settings/rust/src/web.rs
pdns/recursordist/settings/table.py
pdns/recursordist/ws-recursor.cc

index 98dbff074d64d176250c5f49c32accd084be499b..b258792664d2be16a04ed9f287d49aac68a462d3 100644 (file)
@@ -275,6 +275,7 @@ int RecThreadInfo::runThreads(Logr::log_t log)
 
     if (::arg().mustDo("webserver")) {
       extern void serveRustWeb();
+      cerr << "CALL serveRustWeb" << endl;
       serveRustWeb();
     }
 
@@ -358,9 +359,12 @@ int RecThreadInfo::runThreads(Logr::log_t log)
 
     if (::arg().mustDo("webserver")) {
       extern void serveRustWeb();
+      cerr << "WS is CALLED " << endl;
       serveRustWeb();
     }
-
+    else {
+      cerr << "WS is FALSE " << endl;
+    }
     for (auto& tInfo : RecThreadInfo::infos()) {
       tInfo.thread.join();
       if (tInfo.exitCode != 0) {
index 9410d7589f900ef267dc27c3f773afe02e5aa846..859894d70329a019a6fb32d489fadcc9a6777b4f 100644 (file)
@@ -112,7 +112,6 @@ static void* pleaseUseNewSDomainsMap(std::shared_ptr<SyncRes::domainmap_t> newma
 
 string reloadZoneConfiguration(bool yaml)
 {
-  std::shared_ptr<SyncRes::domainmap_t> original = SyncRes::getDomainMap();
   auto log = g_slog->withName("config");
 
   string configname = ::arg()["config-dir"] + "/recursor";
@@ -199,17 +198,21 @@ string reloadZoneConfiguration(bool yaml)
       oldAndNewDomains.insert(entry.first);
     }
 
-    if (original) {
-      for (const auto& entry : *original) {
-        oldAndNewDomains.insert(entry.first);
+    extern LockGuarded<std::shared_ptr<SyncRes::domainmap_t>> g_initialDomainMap; // XXX
+    {
+      auto lock = g_initialDomainMap.lock();
+      if (*lock) {
+        for (const auto& entry : **lock) {
+          oldAndNewDomains.insert(entry.first);
+        }
       }
     }
 
     // these explicitly-named captures should not be necessary, as lambda
     // capture of tuple-like structured bindings is permitted, but some
     // compilers still don't allow it
-    broadcastFunction([dmap = std::move(newDomainMap)] { return pleaseUseNewSDomainsMap(dmap); });
-    broadcastFunction([nsset = std::move(newNotifySet)] { return pleaseSupplantAllowNotifyFor(nsset); });
+    broadcastFunction([dmap = newDomainMap] { return pleaseUseNewSDomainsMap(dmap); });
+    broadcastFunction([nsset = newNotifySet] { return pleaseSupplantAllowNotifyFor(nsset); });
 
     // Wipe the caches *after* the new auth domain info has been set
     // up, as a query during setting up might fill the caches
@@ -217,10 +220,9 @@ string reloadZoneConfiguration(bool yaml)
     for (const auto& entry : oldAndNewDomains) {
       wipeCaches(entry, true, 0xffff);
     }
-    extern LockGuarded<std::shared_ptr<SyncRes::domainmap_t>> g_initialDomainMap; // XXX
-    *g_initialDomainMap.lock() = newDomainMap;
+    *g_initialDomainMap.lock() = std::move(newDomainMap);
     extern LockGuarded<std::shared_ptr<notifyset_t>> g_initialAllowNotifyFor; // XXX
-    *g_initialAllowNotifyFor.lock() = newNotifySet;
+    *g_initialAllowNotifyFor.lock() = std::move(newNotifySet);
     return "ok\n";
   }
   catch (const std::exception& e) {
index d1e53e26d07d38e731d5015865181d2c477c0fb6..cf9dfd1ea8d7a3ffb1f51ce8899298f5ef310cfc 100644 (file)
@@ -100,6 +100,8 @@ class LType(Enum):
     ListDNSTapFrameStreamServers = auto()
     ListDNSTapNODFrameStreamServers = auto()
     ListForwardZones = auto()
+    ListForwardingCatalogZones = auto()
+    ListIncomingWSConfigs = auto()
     ListNegativeTrustAnchors = auto()
     ListProtobufServers = auto()
     ListProxyMappings = auto()
@@ -110,7 +112,6 @@ class LType(Enum):
     ListSubnets = auto()
     ListTrustAnchors = auto()
     ListZoneToCaches = auto()
-    ListForwardingCatalogZones = auto()
     String = auto()
     Uint64 = auto()
 
@@ -118,7 +119,7 @@ listOfStringTypes = (LType.ListSocketAddresses,  LType.ListStrings, LType.ListSu
 listOfStructuredTypes = (LType.ListAuthZones, LType.ListForwardZones, LType.ListTrustAnchors, LType.ListNegativeTrustAnchors,
                          LType.ListProtobufServers, LType.ListDNSTapFrameStreamServers, LType.ListDNSTapNODFrameStreamServers,
                          LType.ListSortLists, LType.ListRPZs, LType.ListZoneToCaches, LType.ListAllowedAdditionalQTypes,
-                         LType.ListProxyMappings, LType.ListForwardingCatalogZones)
+                         LType.ListProxyMappings, LType.ListForwardingCatalogZones, LType.ListIncomingWSConfigs)
 
 def get_olddoc_typename(typ):
     """Given a type from table.py, return the old-style type name"""
@@ -140,7 +141,7 @@ def get_olddoc_typename(typ):
         return 'Comma separated list of \'zonename=IP\' pairs'
     if typ == LType.ListAuthZones:
         return 'Comma separated list of \'zonename=filename\' pairs'
-    return 'Unknown' + str(typ)
+    return 'Unknown1' + str(typ)
 
 def get_newdoc_typename(typ):
     """Given a type from table.py, return the new-style type name"""
@@ -184,7 +185,7 @@ def get_newdoc_typename(typ):
         return 'Sequence of `ProxyMapping`_'
     if typ == LType.ListForwardingCatalogZones:
         return 'Sequence of `ForwardingCatalogZone`_'
-    return 'Unknown' + str(typ)
+    return 'Unknown2' + str(typ)
 
 def get_default_olddoc_value(typ, val):
     """Given a type and a value from table.py return the old doc representation of the value"""
@@ -225,7 +226,7 @@ def list_to_base_type(typ):
     if typeName.startswith('List') and typeName.endswith('s'):
         baseName = typeName[4:len(typeName) - 1]
         return baseName
-    return 'Unknown: ' + typeName
+    return 'Unknown3: ' + typeName
 
 def get_rust_type(typ):
     """Determine which Rust type is used for a logical type"""
index d43bfe502eccaae294c5b212809dd9142c2a55af..731cc15a8584f9ab39d846c2cdc549f24491ccb6 100644 (file)
@@ -303,6 +303,25 @@ pub struct ForwardingCatalogZone {
     groups: Vec<FCZDefault>,
 }
 
+#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+pub struct IncomingTLS {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    certificate: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    key: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    password: String,
+}
+#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+pub struct IncomingWSConfig {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    addresses: Vec<String>,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    tls: IncomingTLS,
+}
+
 // Two structs used to generated YAML based on a vector of name to value mappings
 // Cannot use Enum as CXX has only very basic Enum support
 struct Value {
index 6dc7ce9f48559c614ae08da30991308198c6541a..ecf54ad33ef9a3833839d573d2177e30a6163a31 100644 (file)
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "addr2line"
@@ -173,6 +173,17 @@ dependencies = [
  "pin-utils",
 ]
 
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
 [[package]]
 name = "gimli"
 version = "0.31.1"
@@ -254,6 +265,24 @@ dependencies = [
  "pin-project-lite",
  "smallvec",
  "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
+dependencies = [
+ "futures-util",
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
 ]
 
 [[package]]
@@ -263,12 +292,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
 dependencies = [
  "bytes",
+ "futures-channel",
  "futures-util",
  "http",
  "http-body",
  "hyper",
  "pin-project-lite",
+ "socket2",
  "tokio",
+ "tower-service",
+ "tracing",
 ]
 
 [[package]]
@@ -396,12 +429,67 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "rustc-demangle"
 version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
+[[package]]
+name = "rustls"
+version = "0.23.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
+dependencies = [
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
 [[package]]
 name = "ryu"
 version = "1.0.18"
@@ -460,12 +548,17 @@ dependencies = [
  "form_urlencoded",
  "http-body-util",
  "hyper",
+ "hyper-rustls",
  "hyper-util",
  "ipnet",
  "once_cell",
+ "rustls",
+ "rustls-pemfile",
+ "rustls-pki-types",
  "serde",
  "serde_yml",
  "tokio",
+ "tokio-rustls",
 ]
 
 [[package]]
@@ -490,11 +583,23 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
 [[package]]
 name = "syn"
-version = "2.0.77"
+version = "2.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -524,6 +629,47 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "tokio-rustls"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
@@ -536,12 +682,27 @@ version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
 
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
 [[package]]
 name = "version_check"
 version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
@@ -638,3 +799,9 @@ name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
index 6765cbb99c08bcdf17302a47c6a00c8c761c8ee4..f5170c5c2e144cc2d7720c1023ce88fd675ecc36 100644 (file)
@@ -21,6 +21,11 @@ http-body-util = "0.1"
 hyper-util = { version = "0.1", features = ["tokio"]}
 bytes = "1.8"
 form_urlencoded = "1.2"
+hyper-rustls = { version = "0.27",  default-features = false }
+rustls = { version = "0.23", default-features = false, features = ["ring"] }
+rustls-pemfile = "2.2"
+pki-types = { package = "rustls-pki-types", version = "1.10" }
+tokio-rustls = { version = "0.26", default-features = false }
 
 [build-dependencies]
 cxx-build = "1.0"
index 831323850a6004a4c465c7feab9c33ed3849c8c0..34c6170de0b7e22bee80e1b85d583cefab26059b 100755 (executable)
@@ -7,8 +7,10 @@
 $CARGO build --release $RUST_TARGET --target-dir=$builddir/target --manifest-path $srcdir/Cargo.toml
 
 
-cp -p target/$RUSTC_TARGET_ARCH/release/libsettings.a $builddir/settings/rust/libsettings.a
-cp -p target/$RUSTC_TARGET_ARCH/cxxbridge/settings/src/lib.rs.h $srcdir/lib.rs.h
-cp -p target/$RUSTC_TARGET_ARCH/cxxbridge/settings/src/lib.rs.h $builddir/settings/rust/lib.rs.h
-cp -p target/$RUSTC_TARGET_ARCH/cxxbridge/rust/cxx.h $srcdir/cxx.h
-cp -p target/$RUSTC_TARGET_ARCH/cxxbridge/rust/cxx.h $builddir/settings/rust/cxx.h
+cp -vp target/$RUSTC_TARGET_ARCH/release/libsettings.a $builddir/settings/rust/libsettings.a
+cp -vp target/$RUSTC_TARGET_ARCH/cxxbridge/settings/src/lib.rs.h $srcdir/lib.rs.h
+cp -vp target/$RUSTC_TARGET_ARCH/cxxbridge/settings/src/lib.rs.h $builddir/settings/rust/lib.rs.h
+cp -vp target/$RUSTC_TARGET_ARCH/cxxbridge/rust/cxx.h $srcdir/cxx.h
+cp -vp target/$RUSTC_TARGET_ARCH/cxxbridge/rust/cxx.h $builddir/settings/rust/cxx.h
+cp -vp target/$RUSTC_TARGET_ARCH/cxxbridge/settings/src/web.rs.h $srcdir/web.rs.h
+cp -vp target/$RUSTC_TARGET_ARCH/cxxbridge/settings/src/web.rs.h $builddir/settings/rust/web.rs.h
index 03dca2e89d2105ffe34b858d224d4bce7523c314..3a33aab91ee28fa6b2e5b9e9f9df8b3cd795225b 100644 (file)
@@ -21,6 +21,8 @@
  */
 #pragma once
 
+#include <memory>
+
 #include "rust/cxx.h"
 #include "credentials.hh"
 
@@ -41,6 +43,7 @@ using CredentialsHolder = ::CredentialsHolder;
 struct KeyValue;
 struct Request;
 struct Response;
+struct IncomingWSConfig;
 
 template <typename A>
 class Wrapper
index 83fc607bee9e2584a688568ac7f08a82de992b15..752b8830c8500bd6043c9ef99f6c723869070a50 100644 (file)
@@ -88,6 +88,20 @@ impl Default for ForwardingCatalogZone {
     }
 }
 
+impl Default for IncomingTLS {
+    fn default() -> Self {
+        let deserialized: IncomingTLS = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+impl Default for IncomingWSConfig {
+    fn default() -> Self {
+        let deserialized: IncomingWSConfig = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
 pub fn validate_socket_address(field: &str, val: &String) -> Result<(), ValidationError> {
     let sa = SocketAddr::from_str(val);
     if sa.is_err() {
@@ -746,6 +760,13 @@ impl ForwardingCatalogZone {
     }
 }
 
+impl IncomingWSConfig {
+    pub fn validate(&self, _field: &str) -> Result<(), ValidationError> {
+        // XXX
+        Ok(())
+    }
+}
+
 #[allow(clippy::ptr_arg)] //# Avoids creating a rust::Slice object on the C++ side.
 pub fn validate_auth_zones(field: &str, vec: &Vec<AuthZone>) -> Result<(), ValidationError> {
     validate_vec(field, vec, |field, element| element.validate(field))
index 50f3635d952c3722c4d9266748d8d96203148e9b..e0a1852f6a492d3bef097a9586c3dcb445102570 100644 (file)
@@ -397,47 +397,102 @@ async fn process_request(
     Ok(rust_response)
 }
 
-async fn serveweb_async(listener: TcpListener, ctx: Arc<Context>) -> MyResult<()> {
-
-    // We start a loop to continuously accept incoming connections
-    loop {
-        let ctx = Arc::clone(&ctx);
-        let (stream, _) = listener.accept().await?;
-
-        match stream.peer_addr() {
-            Ok(address) => {
-                eprintln!("Peer: {:?}", address);
-                let combo = rustweb::comboaddress(&address.to_string());
-                if !rustweb::matches(&ctx.acl, &combo) {
-                    eprintln!("No acl match! {:?}", address);
-                    continue;
+async fn serveweb_async(listener: TcpListener, config: crate::web::rustweb::IncomingTLS, ctx: Arc<Context>) -> MyResult<()> {
+
+    if !config.certificate.is_empty() {
+        let certs = load_certs(&config.certificate)?;
+        let key = load_private_key(&config.key)?;
+        let mut server_config = rustls::ServerConfig::builder()
+            .with_no_client_auth()
+            .with_single_cert(certs, key)
+            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+        server_config.alpn_protocols = vec![b"http/1.1".to_vec(), b"http/1.0".to_vec()]; // b"h2".to_vec()
+        let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(server_config));
+        // We start a loop to continuously accept incoming connections
+        loop {
+            let ctx = Arc::clone(&ctx);
+            let (stream, _) = listener.accept().await?;
+
+            match stream.peer_addr() {
+                Ok(address) => {
+                    eprintln!("Peer: {:?}", address);
+                    let combo = rustweb::comboaddress(&address.to_string());
+                    if !rustweb::matches(&ctx.acl, &combo) {
+                        eprintln!("No acl match! {:?}", address);
+                        continue;
+                    }
+                }
+                Err(err) => {
+                    eprintln!("Can't get: {:?}", err);
+                    continue; // If we can't determine the peer address, don't
                 }
             }
-            Err(err) => {
-                eprintln!("Can't get: {:?}", err);
-                continue; // If we can't determine the peer address, don't
-            }
+            // Use an adapter to access something implementing `tokio::io` traits as if they implement
+            // `hyper::rt` IO traits.
+            let tls_acceptor = tls_acceptor.clone();
+            let tls_stream = match tls_acceptor.accept(stream).await {
+                Ok(tls_stream) => tls_stream,
+                Err(err) => {
+                    eprintln!("failed to perform tls handshake: {err:#}");
+                    continue;
+                }
+            };
+            let io = TokioIo::new(tls_stream);
+            let fut =
+                http1::Builder::new().serve_connection(io, service_fn(move |req| {
+                    let ctx = Arc::clone(&ctx);
+                    process_request(req, ctx)
+                }));
+
+            // Spawn a tokio task to serve the request
+            tokio::task::spawn(async move {
+                // Finally, we bind the incoming connection to our `process_request` service
+                if let Err(err) = fut.await {
+                    eprintln!("Error serving connection: {:?}", err);
+                }
+            });
         }
-        // Use an adapter to access something implementing `tokio::io` traits as if they implement
-        // `hyper::rt` IO traits.
-        let io = TokioIo::new(stream);
-        let fut =
-            http1::Builder::new().serve_connection(io, service_fn(move |req| {
-                let ctx = Arc::clone(&ctx);
-                process_request(req, ctx)
-            }));
-
-        // Spawn a tokio task to serve the request
-        tokio::task::spawn(async move {
-            // Finally, we bind the incoming connection to our `process_request` service
-            if let Err(err) = fut.await {
-                eprintln!("Error serving connection: {:?}", err);
+    }
+    else {
+        // We start a loop to continuously accept incoming connections
+        loop {
+            let ctx = Arc::clone(&ctx);
+            let (stream, _) = listener.accept().await?;
+
+            match stream.peer_addr() {
+                Ok(address) => {
+                    eprintln!("Peer: {:?}", address);
+                    let combo = rustweb::comboaddress(&address.to_string());
+                    if !rustweb::matches(&ctx.acl, &combo) {
+                        eprintln!("No acl match! {:?}", address);
+                        continue;
+                    }
+                }
+                Err(err) => {
+                    eprintln!("Can't get: {:?}", err);
+                    continue; // If we can't determine the peer address, don't
+                }
             }
-        });
+            let io = TokioIo::new(stream);
+            let fut =
+                http1::Builder::new().serve_connection(io, service_fn(move |req| {
+                    let ctx = Arc::clone(&ctx);
+                    process_request(req, ctx)
+                }));
+
+            // Spawn a tokio task to serve the request
+            tokio::task::spawn(async move {
+                // Finally, we bind the incoming connection to our `process_request` service
+                if let Err(err) = fut.await {
+                    eprintln!("Error serving connection: {:?}", err);
+                }
+            });
+        }
     }
 }
 
-pub fn serveweb(addresses: &Vec<String>, urls: &[String], password_ch: cxx::UniquePtr<rustweb::CredentialsHolder>, api_ch: cxx::UniquePtr<rustweb::CredentialsHolder>, acl: cxx::UniquePtr<rustweb::NetmaskGroup>) -> Result<(), std::io::Error> {
+pub fn serveweb(incoming: &Vec<rustweb::IncomingWSConfig>, urls: &[String], password_ch: cxx::UniquePtr<rustweb::CredentialsHolder>, api_ch: cxx::UniquePtr<rustweb::CredentialsHolder>, acl: cxx::UniquePtr<rustweb::NetmaskGroup>) -> Result<(), std::io::Error> {
+    println!("SERVEWEB");
     // Context, atomically reference counted
     let ctx = Arc::new(Context {
         urls: urls.to_vec(),
@@ -457,25 +512,34 @@ pub fn serveweb(addresses: &Vec<String>, urls: &[String], password_ch: cxx::Uniq
     // For each listening address we spawn a tokio handler an then a single Posix thread is created that
     // waits (forever) for all of them to complete by joining them all.
     let mut set = JoinSet::new();
-    for addr_str in addresses {
-        let addr = match SocketAddr::from_str(addr_str) {
-            Ok(val) => val,
-            Err(err) => {
-                let msg = format!("`{}' is not a IP:port combination: {}", addr_str, err);
-                return Err(std::io::Error::new(ErrorKind::Other, msg));
-            }
-        };
+    for config in incoming {
+        println!("Config");
+        for addr_str in &config.addresses {
+            println!("Config Addr {}", addr_str);
+            let addr = match SocketAddr::from_str(addr_str) {
+                Ok(val) => val,
+                Err(err) => {
+                    let msg = format!("`{}' is not a IP:port combination: {}", addr_str, err);
+                    return Err(std::io::Error::new(ErrorKind::Other, msg));
+                }
+            };
 
-        let listener = runtime.block_on(async { TcpListener::bind(addr).await });
-        let ctx = Arc::clone(&ctx);
-        match listener {
-            Ok(val) => {
-                println!("Listening on {}", addr);
-                set.spawn_on(serveweb_async(val, ctx), runtime.handle());
-            }
-            Err(err) => {
-                let msg = format!("Unable to bind web socket: {}", err);
-                return Err(std::io::Error::new(ErrorKind::Other, msg));
+            let listener = runtime.block_on(async { TcpListener::bind(addr).await });
+            let ctx = Arc::clone(&ctx);
+            match listener {
+                Ok(val) => {
+                    let tls = crate::web::rustweb::IncomingTLS {
+                        certificate: config.tls.certificate.clone(),
+                        key: config.tls.key.clone(),
+                        password: config.tls.password.clone(),
+                    };
+                    println!("Listening on {}", addr);
+                    set.spawn_on(serveweb_async(val, tls, ctx), runtime.handle());
+                }
+                Err(err) => {
+                    let msg = format!("Unable to bind web socket: {}", err);
+                    return Err(std::io::Error::new(ErrorKind::Other, msg));
+                }
             }
         }
     }
@@ -491,6 +555,28 @@ pub fn serveweb(addresses: &Vec<String>, urls: &[String], password_ch: cxx::Uniq
     Ok(())
 }
 
+// Load public certificate from file.
+fn load_certs(filename: &str) -> std::io::Result<Vec<pki_types::CertificateDer<'static>>> {
+    // Open certificate file.
+    let certfile = std::fs::File::open(filename)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("failed to open {}: {}", filename, e)))?;
+    let mut reader = std::io::BufReader::new(certfile);
+
+    // Load and return certificate.
+    rustls_pemfile::certs(&mut reader).collect()
+}
+
+// Load private key from file.
+fn load_private_key(filename: &str) -> std::io::Result<pki_types::PrivateKeyDer<'static>> {
+    // Open keyfile.
+    let keyfile = std::fs::File::open(filename)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("failed to open {}: {}", filename, e)))?;
+    let mut reader = std::io::BufReader::new(keyfile);
+
+    // Load and return a single private key.
+    rustls_pemfile::private_key(&mut reader).map(|key| key.unwrap())
+}
+
 // impl below needed because the classes are used in the Context, which gets passed around.
 unsafe impl Send for rustweb::CredentialsHolder {}
 unsafe impl Sync for rustweb::CredentialsHolder {}
@@ -505,12 +591,22 @@ mod rustweb {
         type ComboAddress;
     }
 
+    pub struct IncomingTLS {
+        certificate: String,
+        key: String,
+        password: String,
+    }
+
+    struct IncomingWSConfig {
+        addresses: Vec<String>,
+        tls: IncomingTLS,
+    }
     /*
      * Functions callable from C++
      */
     extern "Rust" {
         // The main entry point, This function will return, but will setup thread(s) to handle requests.
-        fn serveweb(addreses: &Vec<String>, urls: &[String], pwch: UniquePtr<CredentialsHolder>, apikeych: UniquePtr<CredentialsHolder>, acl: UniquePtr<NetmaskGroup>) -> Result<()>;
+        fn serveweb(incoming: &Vec<IncomingWSConfig>, urls: &[String], pwch: UniquePtr<CredentialsHolder>, apikeych: UniquePtr<CredentialsHolder>, acl: UniquePtr<NetmaskGroup>) -> Result<()>;
     }
 
     struct KeyValue {
index 06c65a985c7b63b89c1c310e2034d30e442871e0..dbdcd4867a8b92849c69b592b1739fdaa0c33f2a 100644 (file)
@@ -3204,13 +3204,13 @@ IP address for the webserver to listen on.
  ''',
     },
     {
-        'name' : 'addresses',
+        'name' : 'listen',
         'section' : 'webservice',
-        'type' : LType.ListSocketAddresses,
-        'default' : '127.0.0.1:8082',
-        'help' : 'IP Addresses of webserver to listen on',
+        'type' : LType.ListIncomingWSConfigs,
+        'default' : '',
+        'help' : 'XXXX',
         'doc' : '''
-IP addresses for the webserver to listen on.
+XXXXX IP addresses for the webserver to listen on.
 If this setting has a non-default value, :ref:`setting-yaml-webservice.address`  :ref:`setting-yaml-webservice.port` and will be ignored.
  ''',
         'skip-old': 'No equivalent old-style setting',
index 37a1ade4cac412caaf55a14af79c3b2fbf2c957c..1bd995ad675875ae49ed9adccd54875e3afd5cca 100644 (file)
@@ -985,20 +985,31 @@ void AsyncWebServer::go()
 
 void serveRustWeb()
 {
-  ::rust::Vec<::rust::String> urls;
-  for (const auto& [url, _] : g_urlmap) {
-    urls.emplace_back(url);
-  }
-  auto address = ComboAddress(arg()["webserver-address"], arg().asNum("webserver-port"));
-  ::rust::Vec<::rust::String> addressList{address.toStringWithPort()};
+  cerr << "SERVERUSTWEB" << endl;
+  ::rust::Vec<pdns::rust::web::rec::IncomingWSConfig> config;
 
   if (g_yamlSettings) {
-    auto addresses = g_yamlStruct.lock()->webservice.addresses;
-    if (addresses.size() != 1 || addresses.at(0) != "127.0.0.1:8082") {
-      addressList = std::move(addresses);
+    auto settings = g_yamlStruct.lock();
+    for (const auto& listen : settings->webservice.listen) {
+      pdns::rust::web::rec::IncomingWSConfig tmp;
+      for (const auto& address : listen.addresses) {
+        tmp.addresses.emplace_back(address);
+      }
+      tmp.tls = pdns::rust::web::rec::IncomingTLS{listen.tls.certificate, listen.tls.key, listen.tls.password};
+      config.emplace_back(tmp);
     }
   }
+  cerr << "Config ahs " << config.size() << " entries" << endl;
+  if (config.empty()) {
+    auto address = ComboAddress(arg()["webserver-address"], arg().asNum("webserver-port"));
+    pdns::rust::web::rec::IncomingWSConfig tmp{{::rust::String{address.toStringWithPort()}}, {}};
+    config.emplace_back(tmp);
+  }
 
+  ::rust::Vec<::rust::String> urls;
+  for (const auto& [url, _] : g_urlmap) {
+    urls.emplace_back(url);
+  }
   auto passwordString = arg()["webserver-password"];
   std::unique_ptr<CredentialsHolder> password;
   if (!passwordString.empty()) {
@@ -1013,7 +1024,8 @@ void serveRustWeb()
   acl.toMasks(::arg()["webserver-allow-from"]);
   auto aclPtr = std::make_unique<pdns::rust::web::rec::NetmaskGroup>(acl);
 
-  pdns::rust::web::rec::serveweb(addressList, ::rust::Slice<const ::rust::String>{urls.data(), urls.size()}, std::move(password), std::move(apikey), std::move(aclPtr));
+ cerr << "CALL SERVEWEB" << endl;
+   pdns::rust::web::rec::serveweb(config, ::rust::Slice<const ::rust::String>{urls.data(), urls.size()}, std::move(password), std::move(apikey), std::move(aclPtr));
 }
 
 static void fromCxxToRust(const HttpResponse& cxxresp, pdns::rust::web::rec::Response& rustResponse)