systemd-resolved-rs 0.1.1

A compatibility-oriented reimplementation of systemd-resolved
#[cfg(test)]
mod test_28_configuration_reload {
    use super::*;
    use crate::routing::KernelLinkState;

    fn kernel_link(ifindex: i32) -> KernelLinkState {
        KernelLinkState {
            ifindex,
            ifname: format!("test{ifindex}"),
            flags: 0x0083,
            mtu: 1500,
            operstate: 0,
            has_ipv4_global: true,
            has_ipv4_link_local: false,
            has_ipv6_global: false,
            has_ipv6_link_local: false,
        }
    }

    #[test]
    fn reload_replaces_live_fallback_servers() {
        let resolver = Resolver::new(Config::default());
        assert!(!resolver.config().configured_fallback_upstreams().is_empty());

        let mut reloaded = Config::default();
        reloaded.fallback_upstreams.clear();
        reloaded.fallback_upstream_specs.clear();
        assert!(resolver.reload_config(reloaded));
        assert!(resolver.config().configured_fallback_upstreams().is_empty());

        let query = make_query(".", 33, 0x2800).expect("root SRV query");
        assert!(matches!(
            resolver.query_on_link_with_flags(&query, QueryMode::Full, None, 0),
            Err(ResolveError::NoNameServers)
        ));
    }

    #[test]
    fn reload_applies_stale_retention() {
        let resolver = Resolver::new(Config::default());
        let mut reloaded = Config::default();
        reloaded.stale_retention = Duration::from_secs(86_400);

        assert!(resolver.reload_config(reloaded));
        assert_eq!(resolver.config().stale_retention, Duration::from_secs(86_400));
    }

    #[test]
    fn every_reload_disconnects_transports_and_drops_non_link_server_state() {
        let resolver = Resolver::new(Config::default());
        let address: SocketAddr = "127.0.0.1:53".parse().expect("server address");
        let global = ServerKey::new(ScopeKind::Global, address);
        let link = ServerKey::new(ScopeKind::Link(7), address);
        resolver.record_failure(global, Duration::from_millis(10));
        resolver.record_failure(link, Duration::from_millis(10));
        resolver.recycle_udp_socket(
            global,
            UdpSocket::bind("127.0.0.1:0").expect("UDP socket"),
        );

        assert!(!resolver.reload_config(resolver.config()));
        assert!(resolver
            .udp_sockets
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner)
            .is_empty());
        let states = resolver.states();
        assert!(!states.contains_key(&global));
        assert!(states.contains_key(&link));
    }

    #[test]
    fn new_links_inherit_global_dnssec_and_tls_policy() {
        let resolver = Resolver::new(Config {
            dnssec: ValidationMode::Yes,
            dns_over_tls: TlsMode::Opportunistic,
            ..Config::default()
        });

        resolver
            .sync_kernel_links(vec![kernel_link(7)])
            .expect("kernel link state");

        let link = resolver.link(7).expect("link state");
        assert_eq!(link.dnssec, ValidationMode::Yes);
        assert_eq!(link.dns_over_tls, TlsMode::Opportunistic);
    }

    #[test]
    fn reload_updates_inherited_link_policy_and_preserves_explicit_overrides() {
        let resolver = Resolver::new(Config::default());
        resolver
            .sync_kernel_links(vec![kernel_link(7), kernel_link(8)])
            .expect("kernel link state");
        resolver
            .set_link_dnssec(8, ValidationMode::AllowDowngrade)
            .expect("explicit DNSSEC policy");
        resolver
            .set_link_dns_over_tls(8, TlsMode::No)
            .expect("explicit TLS policy");

        let reloaded = Config {
            dnssec: ValidationMode::Yes,
            dns_over_tls: TlsMode::Yes,
            ..Config::default()
        };
        assert!(resolver.reload_config(reloaded));

        let inherited = resolver.link(7).expect("inherited link state");
        assert_eq!(inherited.dnssec, ValidationMode::Yes);
        assert_eq!(inherited.dns_over_tls, TlsMode::Yes);
        let explicit = resolver.link(8).expect("explicit link state");
        assert_eq!(explicit.dnssec, ValidationMode::AllowDowngrade);
        assert_eq!(explicit.dns_over_tls, TlsMode::No);
    }

    #[test]
    fn clearing_link_overrides_restores_live_manager_policy() {
        let resolver = Resolver::new(Config {
            dnssec: ValidationMode::Yes,
            dns_over_tls: TlsMode::Yes,
            ..Config::default()
        });
        resolver
            .sync_kernel_links(vec![kernel_link(7)])
            .expect("kernel link state");
        resolver
            .set_link_dnssec(7, ValidationMode::No)
            .expect("explicit DNSSEC policy");
        resolver
            .set_link_dns_over_tls(7, TlsMode::No)
            .expect("explicit TLS policy");

        resolver
            .set_link_dnssec_override(7, None)
            .expect("clear DNSSEC override");
        resolver
            .set_link_dns_over_tls_override(7, None)
            .expect("clear TLS override");

        let link = resolver.link(7).expect("link state");
        assert_eq!(link.dnssec, ValidationMode::Yes);
        assert_eq!(link.dns_over_tls, TlsMode::Yes);
    }
}