Skip to content

pimalaya/io-dns

I/O DNS Documentation Matrix Mastodon

I/O-free DNS client library written in Rust, based on io-socket

Table of contents

RFC coverage

This library implements the DNS wire protocol as an I/O-agnostic coroutine — no sockets, no async runtime, no std required. It covers query encoding, response decoding, and record-type parsing for several RFCs:

RFC What it covers
1035 Wire format, message structure, UDP/TCP coroutines, record types A, NS, CNAME, SOA, PTR, MX, TXT
2782 SRV record — service location (priority, weight, port, target)
3596 AAAA record — IPv6 host addresses
6698 TLSA record — TLS certificate binding via DANE
6763 DNS-SD — zero-config service discovery via PTR + SRV + TXT
6891 EDNS0 OPT pseudo-RR — larger UDP payloads, extended RCODE, DO bit

Examples

Query A records over UDP (blocking)

use std::net::UdpSocket;

use io_dns::rfc1035::{
    query_udp::{DnsUdpQuery, DnsUdpQueryResult},
    types::{record_data::DnsRecordData, r#type::A},
};
use io_socket::runtimes::std_udp_socket::handle;

let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.connect("8.8.8.8:53").unwrap();

let mut query = DnsUdpQuery::new(0x1234, "example.com", A);
let mut arg = None;

let message = loop {
    match query.resume(arg.take()) {
        DnsUdpQueryResult::Ok { message } => break message,
        DnsUdpQueryResult::Io { input } => arg = Some(handle(&mut socket, input).unwrap()),
        DnsUdpQueryResult::Err { err } => panic!("{err}"),
    }
};

for record in &message.answers {
    if let DnsRecordData::A(addr) = &record.data {
        println!("A {addr}");
    }
}

Query AAAA records over UDP (blocking)

use std::net::UdpSocket;

use io_dns::rfc1035::{
    query_udp::{DnsUdpQuery, DnsUdpQueryResult},
    types::record_data::DnsRecordData,
};
use io_dns::rfc3596::aaaa::{DnsAaaaData, AAAA};
use io_socket::runtimes::std_udp_socket::handle;

let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
socket.connect("8.8.8.8:53").unwrap();

let mut query = DnsUdpQuery::new(0x1234, "example.com", AAAA);
let mut arg = None;

let message = loop {
    match query.resume(arg.take()) {
        DnsUdpQueryResult::Ok { message } => break message,
        DnsUdpQueryResult::Io { input } => arg = Some(handle(&mut socket, input).unwrap()),
        DnsUdpQueryResult::Err { err } => panic!("{err}"),
    }
};

for record in &message.answers {
    if record.r#type == AAAA {
        if let DnsRecordData::Other(data) = &record.data {
            if let Ok(aaaa) = DnsAaaaData::decode(data) {
                println!("AAAA {}", aaaa.addr);
            }
        }
    }
}

Query A records over TCP (async)

use io_dns::rfc1035::{
    query_tcp::{DnsTcpQuery, DnsTcpQueryResult},
    types::{record_data::DnsRecordData, r#type::A},
};
use io_socket::runtimes::tokio_stream::handle;
use tokio::net::TcpStream;

let mut stream = TcpStream::connect("8.8.8.8:53").await.unwrap();

let mut query = DnsTcpQuery::new(0x1234, "example.com", A);
let mut arg = None;

let message = loop {
    match query.resume(arg.take()) {
        DnsTcpQueryResult::Ok { message } => break message,
        DnsTcpQueryResult::Io { input } => arg = Some(handle(&mut stream, input).await.unwrap()),
        DnsTcpQueryResult::Err { err } => panic!("{err}"),
    }
};

for record in &message.answers {
    if let DnsRecordData::A(addr) = &record.data {
        println!("A {addr}");
    }
}

See complete examples at ./examples.

More examples

Have a look at projects built on top of this library:

  • TODO

License

This project is licensed under either of:

at your option.

Social

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:

If you appreciate the project, feel free to donate using one of the following providers:

GitHub Ko-fi Buy Me a Coffee Liberapay thanks.dev PayPal