systemd-resolved-rs 0.1.1

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

    fn append_test_answer(packet: &mut Vec<u8>, owner: &[u8], rr_type: u16, rdata: &[u8]) {
        packet.extend_from_slice(owner);
        packet.extend_from_slice(&rr_type.to_be_bytes());
        packet.extend_from_slice(&wire::CLASS_IN.to_be_bytes());
        packet.extend_from_slice(&60u32.to_be_bytes());
        packet.extend_from_slice(
            &u16::try_from(rdata.len())
                .expect("test RDATA length")
                .to_be_bytes(),
        );
        packet.extend_from_slice(rdata);
    }

    #[test]
    fn longest_suffix_routes_to_the_matching_link() {
        use crate::wire::question_end;
        use std::thread;

        let global = UdpSocket::bind("127.0.0.1:0").expect("bind global DNS server");
        global
            .set_read_timeout(Some(Duration::from_millis(200)))
            .expect("set global timeout");
        let link = UdpSocket::bind("127.0.0.1:0").expect("bind link DNS server");
        link.set_read_timeout(Some(Duration::from_secs(2)))
            .expect("set link timeout");
        let link_address = link.local_addr().expect("link DNS address");
        let worker = thread::spawn(move || {
            let mut buffer = [0; 4096];
            let (length, peer) = link.recv_from(&mut buffer).expect("receive link query");
            let query = &buffer[..length];
            let end = question_end(query).expect("question end");
            let mut response = query[..end].to_vec();
            response[2..4].copy_from_slice(&0x8180u16.to_be_bytes());
            response[6..8].copy_from_slice(&1u16.to_be_bytes());
            response[8..12].fill(0);
            append_test_answer(&mut response, &[0xc0, 0x0c], TYPE_A, &[192, 0, 2, 88]);
            link.send_to(&response, peer).expect("send link response");
        });

        let config = Config {
            upstreams: vec![global.local_addr().expect("global DNS address")],
            fallback_upstreams: Vec::new(),
            query_timeout: Duration::from_secs(1),
            attempts: 1,
            cache: true,
            cache_from_localhost: true,
            read_etc_hosts: false,
            ..Config::default()
        };
        let resolver = Resolver::new(config);
        resolver
            .set_link_dns(7, vec![link_address])
            .expect("set link DNS");
        resolver
            .set_link_domains(
                7,
                vec![Domain {
                    name: "corp.example".to_owned(),
                    route_only: true,
                }],
            )
            .expect("set link domain");

        let lookup = resolver
            .lookup_name("host.corp.example", 2)
            .expect("split DNS lookup");
        worker.join().expect("link DNS worker");
        assert_eq!(
            lookup.addresses,
            vec![IpAddr::V4(Ipv4Addr::new(192, 0, 2, 88))]
        );
        assert_eq!(lookup.address_ifindices, vec![Some(7)]);
        let cached = resolver
            .lookup_name("host.corp.example", 2)
            .expect("cached split DNS lookup");
        assert_eq!(cached.address_ifindices, vec![Some(7)]);
        let mut buffer = [0; 512];
        assert!(global.recv_from(&mut buffer).is_err());
    }

    #[test]
    fn longest_suffix_routes_to_the_matching_delegate() {
        use crate::wire::question_end;
        use std::thread;

        let global = UdpSocket::bind("127.0.0.1:0").expect("bind global DNS server");
        global
            .set_read_timeout(Some(Duration::from_millis(200)))
            .expect("set global timeout");
        let delegate = UdpSocket::bind("127.0.0.1:0").expect("bind delegate DNS server");
        delegate
            .set_read_timeout(Some(Duration::from_secs(2)))
            .expect("set delegate timeout");
        let delegate_address = delegate.local_addr().expect("delegate DNS address");
        let worker = thread::spawn(move || {
            let mut buffer = [0; 4096];
            let (length, peer) = delegate
                .recv_from(&mut buffer)
                .expect("receive delegate query");
            let query = &buffer[..length];
            let end = question_end(query).expect("question end");
            let mut response = query[..end].to_vec();
            response[2..4].copy_from_slice(&0x8180u16.to_be_bytes());
            response[6..8].copy_from_slice(&1u16.to_be_bytes());
            response[8..12].fill(0);
            append_test_answer(&mut response, &[0xc0, 0x0c], TYPE_A, &[192, 0, 2, 89]);
            delegate
                .send_to(&response, peer)
                .expect("send delegate response");
        });

        let config = Config {
            dns_delegates: vec![crate::dns_delegate::DnsDelegate {
                id: "corp".to_owned(),
                servers: vec![DnsServerSpec {
                    address: delegate_address,
                    interface: None,
                    server_name: None,
                }],
                domains: vec![Domain {
                    name: "corp.example".to_owned(),
                    route_only: true,
                }],
                default_route: None,
                firewall_mark: 0,
            }],
            upstreams: vec![global.local_addr().expect("global DNS address")],
            fallback_upstreams: Vec::new(),
            query_timeout: Duration::from_secs(1),
            attempts: 1,
            cache: false,
            read_etc_hosts: false,
            ..Config::default()
        };
        let lookup = Resolver::new(config)
            .lookup_name("host.corp.example", 2)
            .expect("delegate split DNS lookup");
        worker.join().expect("delegate DNS worker");
        assert_eq!(
            lookup.addresses,
            vec![IpAddr::V4(Ipv4Addr::new(192, 0, 2, 89))]
        );
        assert_eq!(lookup.address_ifindices, vec![None]);
        let mut buffer = [0; 512];
        assert!(global.recv_from(&mut buffer).is_err());
    }

}