An Elixir library for DNS packet parsing and creation. Tenbin.DNS provides handling of DNS protocol operations with support for 19+ DNS record types, DNSSEC, web optimization features, and EDNS0 extensions.
# 1. Add the entry to your mix.exs deps list
defp deps do
[
# ...
{:tenbin_dns, "~> 0.7.1"}
]
end# 2. Build a DNS query packet and serialize it to wire format.
# Note: qname must be an FQDN with a trailing dot ("example.com.")
# so the encoded name ends in the DNS root label (a zero-length
# label terminator).
iex> packet = %DNSpacket{
...> id: 0x1234,
...> rd: 1,
...> question: [%{qname: "example.com.", qtype: :a, qclass: :in}]
...> }
iex> binary = DNSpacket.create(packet)
iex> byte_size(binary)
29# 3. Parse a wire-format DNS packet back into a struct
# (parse/1 returns %DNSpacket{} directly — no {:ok, _} wrap)
iex> parsed = DNSpacket.parse(binary)
iex> parsed.id
4660
iex> hd(parsed.question)
%{qname: "example.com.", qtype: :a, qclass: :in}See Usage below for richer record types (HTTPS / SVCB, SRV, DNSSEC) and EDNS0 examples.
- DNS packet parsing and creation - Binary pattern matching with compile-time optimizations
- DNS record support - 19+ record types including:
- Basic records: A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, CAA
- Service discovery: SRV, NAPTR
- DNSSEC support: DNSKEY, DS, RRSIG, NSEC
- Web optimization: SVCB, HTTPS with Service Parameters (ALPN, IPv4/IPv6 hints)
- Delegation: DNAME
- Legacy: HINFO
- EDNS0 hybrid structure - 35-69% faster access to common EDNS options
- DNSSEC support - DNS Security Extensions
- HTTP/3 support - SVCB/HTTPS records with ALPN parameter support
- Domain name compression - Decompression support (parsing compressed names)
- DNS constants - Types, classes, opcodes, and response codes
- Test coverage - 161 tests with binary data validation
Add tenbin_dns to your list of dependencies in mix.exs:
defp deps do
[
# ... existing dependencies
{:tenbin_dns, "~> 0.7.1"}
]
endThe supported public API is DNSpacket.create/1, DNSpacket.parse/1, the
DNSpacket struct and the hybrid edns_info structure shown below. Other
public functions are internal implementation details and may change in any
release without notice.
# Create a DNS query packet
packet = %DNSpacket{
id: 12345,
rd: 1,
question: [
%{qname: "example.com.", qtype: :a, qclass: :in}
]
}
# Convert to binary format
binary_packet = DNSpacket.create(packet)# HTTPS record with Service Parameters (HTTP/3 optimization)
https_packet = %DNSpacket{
id: 12346,
qr: 1,
question: [%{qname: "example.com.", qtype: :https, qclass: :in}],
answer: [%{
name: "example.com.",
type: :https,
class: :in,
ttl: 300,
rdata: %{
priority: 1,
target: ".",
svc_params: %{
alpn: ["h3", "h2"], # HTTP/3, HTTP/2 support
ipv4_hints: [{104, 16, 132, 229}], # IPv4 optimization hints
port: 443
}
}
}]
}
# SRV record for service discovery
srv_packet = %DNSpacket{
id: 12347,
qr: 1,
question: [%{qname: "_sip._tcp.example.com.", qtype: :srv, qclass: :in}],
answer: [%{
name: "_sip._tcp.example.com.",
type: :srv,
class: :in,
ttl: 300,
rdata: %{priority: 10, weight: 5, port: 5060, target: "sip.example.com."}
}]
}
# DNSSEC records
dnskey_packet = %DNSpacket{
id: 12348,
qr: 1,
answer: [%{
name: "example.com.",
type: :dnskey,
class: :in,
ttl: 3600,
rdata: %{flags: 257, protocol: 3, algorithm: 8, public_key: <<0x03, 0x01, 0x00, 0x01>>}
}]
}# Parse a binary DNS packet
parsed_packet = DNSpacket.parse(binary_data)
# Access packet fields
IO.puts("Query ID: #{parsed_packet.id}")
IO.puts("Response code: #{DNS.rcode(parsed_packet.rcode)}")
# Access HTTPS Service Parameters
if packet.answer do
Enum.each(packet.answer, fn record ->
if record.type == :https and record.rdata.svc_params do
alpn = record.rdata.svc_params[:alpn]
IO.puts("Supported protocols: #{inspect(alpn)}")
end
end)
end# Convert between numeric codes and atoms
DNS.type_code(:a) # Returns 1
DNS.type(1) # Returns :a
DNS.type_code(:https) # Returns 65
DNS.class_code(:in) # Returns 1
DNS.rcode_code(:noerror) # Returns 0
# New record types
DNS.type_code(:srv) # Returns 33
DNS.type_code(:dnskey) # Returns 48# Install dependencies
mix deps.get
# Run tests
mix test
# Run code analysis
mix credo
mix dialyzer
# Generate documentation
mix docsThis project uses Lefthook for Git hooks management to ensure code quality before commits.
# Install Lefthook (if not already installed)
# On macOS with Homebrew:
brew install lefthook
# On other systems:
# See https://github.com/evilmartians/lefthook/blob/master/docs/install.md
# Install hooks in the repository
lefthook installThe following checks run automatically before each commit:
- Code formatting -
mix format(auto-fixes files) - Tests -
mix test --cover(with coverage analysis) - Code quality -
mix credo --strict
If you need to skip hooks for an emergency commit:
LEFTHOOK=0 git commit -m "Emergency fix"Tenbin.DNS includes optimizations for DNS operations:
- Compile-time optimization with native compilation
- Function inlining for speed-critical paths
- Binary pattern matching for protocol handling
- O(1) constant lookups using compile-time generated maps
- EDNS hybrid structure providing 35-69% faster access to common options
- Fast paths for A/AAAA record parsing
- 161 tests for reliability and performance validation
This project is licensed under the MIT License - see the LICENSE file for details.