dns-mock-server 0.1.0

Mock DNS server for tests
Documentation
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4};

use hickory_resolver::config::{NameServerConfig, Protocol, ResolverConfig, ResolverOpts};
use hickory_resolver::error::ResolveErrorKind;
use hickory_resolver::proto::op::ResponseCode;
use hickory_resolver::AsyncResolver;
use tokio::net::UdpSocket;

use dns_mock_server::Server;

type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[tokio::test]
async fn can_query_dns_records_from_the_server() -> Result<()> {
    let expected_addr = IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4));

    let mut server = Server::default();
    server.add_records("www.example.com.", vec![expected_addr])?;

    let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
    let socket = UdpSocket::bind(&addr).await?;

    let local_addr = socket.local_addr()?;

    tokio::spawn(async move {
        server.start(socket).await.unwrap();
    });

    let mut config = ResolverConfig::new();
    let nameserver_config = NameServerConfig::new(local_addr, Protocol::Udp);
    config.add_name_server(nameserver_config);

    let resolver = AsyncResolver::tokio(config, ResolverOpts::default());
    let result = resolver.lookup_ip("www.example.com.").await?;

    let addrs: Vec<_> = result.into_iter().collect();

    assert_eq!(addrs.len(), 1);
    assert_eq!(addrs[0], expected_addr);

    Ok(())
}

#[tokio::test]
async fn unknown_names_return_errors() -> Result<()> {
    let server = Server::default();

    let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
    let socket = UdpSocket::bind(&addr).await?;

    let local_addr = socket.local_addr()?;

    tokio::spawn(async move {
        server.start(socket).await.unwrap();
    });

    let mut config = ResolverConfig::new();
    let nameserver_config = NameServerConfig::new(local_addr, Protocol::Udp);
    config.add_name_server(nameserver_config);

    let resolver = AsyncResolver::tokio(config, ResolverOpts::default());

    let Err(err) = resolver.lookup_ip("www.example.com.").await else {
        return Err("got successful response back".into());
    };

    let ResolveErrorKind::NoRecordsFound { response_code, .. } = err.kind() else {
        return Err("got unexpected error kind back".into());
    };

    assert_eq!(*response_code, ResponseCode::ServFail);

    Ok(())
}