From: Otto Moerbeek Date: Tue, 21 Jan 2025 14:25:13 +0000 (+0100) Subject: Basic tls support X-Git-Tag: dnsdist-2.0.0-alpha1~95^2~20 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1d20d866bad04a4e46c464eebc7df9a528104bae;p=thirdparty%2Fpdns.git Basic tls support --- diff --git a/pdns/recursordist/rec-main.cc b/pdns/recursordist/rec-main.cc index 98dbff074d..b258792664 100644 --- a/pdns/recursordist/rec-main.cc +++ b/pdns/recursordist/rec-main.cc @@ -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) { diff --git a/pdns/recursordist/reczones.cc b/pdns/recursordist/reczones.cc index 9410d7589f..859894d703 100644 --- a/pdns/recursordist/reczones.cc +++ b/pdns/recursordist/reczones.cc @@ -112,7 +112,6 @@ static void* pleaseUseNewSDomainsMap(std::shared_ptr newma string reloadZoneConfiguration(bool yaml) { - std::shared_ptr 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> 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> g_initialDomainMap; // XXX - *g_initialDomainMap.lock() = newDomainMap; + *g_initialDomainMap.lock() = std::move(newDomainMap); extern LockGuarded> g_initialAllowNotifyFor; // XXX - *g_initialAllowNotifyFor.lock() = newNotifySet; + *g_initialAllowNotifyFor.lock() = std::move(newNotifySet); return "ok\n"; } catch (const std::exception& e) { diff --git a/pdns/recursordist/settings/generate.py b/pdns/recursordist/settings/generate.py index d1e53e26d0..cf9dfd1ea8 100644 --- a/pdns/recursordist/settings/generate.py +++ b/pdns/recursordist/settings/generate.py @@ -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""" diff --git a/pdns/recursordist/settings/rust-bridge-in.rs b/pdns/recursordist/settings/rust-bridge-in.rs index d43bfe502e..731cc15a85 100644 --- a/pdns/recursordist/settings/rust-bridge-in.rs +++ b/pdns/recursordist/settings/rust-bridge-in.rs @@ -303,6 +303,25 @@ pub struct ForwardingCatalogZone { groups: Vec, } +#[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, + #[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 { diff --git a/pdns/recursordist/settings/rust/Cargo.lock b/pdns/recursordist/settings/rust/Cargo.lock index 6dc7ce9f48..ecf54ad33e 100644 --- a/pdns/recursordist/settings/rust/Cargo.lock +++ b/pdns/recursordist/settings/rust/Cargo.lock @@ -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" diff --git a/pdns/recursordist/settings/rust/Cargo.toml b/pdns/recursordist/settings/rust/Cargo.toml index 6765cbb99c..f5170c5c2e 100644 --- a/pdns/recursordist/settings/rust/Cargo.toml +++ b/pdns/recursordist/settings/rust/Cargo.toml @@ -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" diff --git a/pdns/recursordist/settings/rust/build_settings b/pdns/recursordist/settings/rust/build_settings index 831323850a..34c6170de0 100755 --- a/pdns/recursordist/settings/rust/build_settings +++ b/pdns/recursordist/settings/rust/build_settings @@ -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 diff --git a/pdns/recursordist/settings/rust/src/bridge.hh b/pdns/recursordist/settings/rust/src/bridge.hh index 03dca2e89d..3a33aab91e 100644 --- a/pdns/recursordist/settings/rust/src/bridge.hh +++ b/pdns/recursordist/settings/rust/src/bridge.hh @@ -21,6 +21,8 @@ */ #pragma once +#include + #include "rust/cxx.h" #include "credentials.hh" @@ -41,6 +43,7 @@ using CredentialsHolder = ::CredentialsHolder; struct KeyValue; struct Request; struct Response; +struct IncomingWSConfig; template class Wrapper diff --git a/pdns/recursordist/settings/rust/src/bridge.rs b/pdns/recursordist/settings/rust/src/bridge.rs index 83fc607bee..752b8830c8 100644 --- a/pdns/recursordist/settings/rust/src/bridge.rs +++ b/pdns/recursordist/settings/rust/src/bridge.rs @@ -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) -> Result<(), ValidationError> { validate_vec(field, vec, |field, element| element.validate(field)) diff --git a/pdns/recursordist/settings/rust/src/web.rs b/pdns/recursordist/settings/rust/src/web.rs index 50f3635d95..e0a1852f6a 100644 --- a/pdns/recursordist/settings/rust/src/web.rs +++ b/pdns/recursordist/settings/rust/src/web.rs @@ -397,47 +397,102 @@ async fn process_request( Ok(rust_response) } -async fn serveweb_async(listener: TcpListener, ctx: Arc) -> 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) -> 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, urls: &[String], password_ch: cxx::UniquePtr, api_ch: cxx::UniquePtr, acl: cxx::UniquePtr) -> Result<(), std::io::Error> { +pub fn serveweb(incoming: &Vec, urls: &[String], password_ch: cxx::UniquePtr, api_ch: cxx::UniquePtr, acl: cxx::UniquePtr) -> 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, 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, urls: &[String], password_ch: cxx::Uniq Ok(()) } +// Load public certificate from file. +fn load_certs(filename: &str) -> std::io::Result>> { + // 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> { + // 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, + 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, urls: &[String], pwch: UniquePtr, apikeych: UniquePtr, acl: UniquePtr) -> Result<()>; + fn serveweb(incoming: &Vec, urls: &[String], pwch: UniquePtr, apikeych: UniquePtr, acl: UniquePtr) -> Result<()>; } struct KeyValue { diff --git a/pdns/recursordist/settings/table.py b/pdns/recursordist/settings/table.py index 06c65a985c..dbdcd4867a 100644 --- a/pdns/recursordist/settings/table.py +++ b/pdns/recursordist/settings/table.py @@ -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', diff --git a/pdns/recursordist/ws-recursor.cc b/pdns/recursordist/ws-recursor.cc index 37a1ade4ca..1bd995ad67 100644 --- a/pdns/recursordist/ws-recursor.cc +++ b/pdns/recursordist/ws-recursor.cc @@ -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 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 password; if (!passwordString.empty()) { @@ -1013,7 +1024,8 @@ void serveRustWeb() acl.toMasks(::arg()["webserver-allow-from"]); auto aclPtr = std::make_unique(acl); - pdns::rust::web::rec::serveweb(addressList, ::rust::Slice{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{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)