I/O-free DNS client library written in Rust, based on io-socket
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 |
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}");
}
}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);
}
}
}
}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.
Have a look at projects built on top of this library:
- TODO
This project is licensed under either of:
at your option.
- Chat on Matrix
- News on Mastodon or RSS
- Mail at pimalaya.org@posteo.net
Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:
- 2022: NGI Assure
- 2023: NGI Zero Entrust
- 2024: NGI Zero Core (still ongoing in 2026)
If you appreciate the project, feel free to donate using one of the following providers:
