dns_zone (dns_erlang v5.0.11)
View SourceDNS Zone File Parser and Encoder
This module provides functionality to parse and encode DNS zone files according to RFC 1035 and related specifications.
Specification Compliance
RFC-Defined Features (Standard):
- RFC 1035 §5: Master file format, resource record syntax
- RFC 1034 §3.6.1: Resource record conceptual model
- RFC 2308 §4: $TTL directive and time unit syntax
Supported RFC Features:
- All DNS record types supported by this library
- Zone file directives: $ORIGIN, $TTL, $INCLUDE (RFC 1035)
- Multi-line records using parentheses (RFC 1035 §5.1)
- Comments (semicolon to end-of-line, RFC 1035 §5.1)
- Relative and absolute domain names (RFC 1035 §5.1)
- Time values with units: w, d, h, m, s (RFC 2308 §4)
- All DNS classes: IN, CH, HS, CS (RFC 1035)
- @ symbol for current origin (RFC 1035 §5.1)
- Blank owner names inheriting from previous RR (RFC 1035 §5.1)
BIND Extensions (Non-Standard):
- $GENERATE: BIND-specific directive for generating multiple similar RRs
- Status: Parsed but NOT implemented (template expansion TODO)
- Warning: Not portable to all DNS software
- See: https://bind9.readthedocs.io/en/latest/chapter3.html
The parser uses Erlang's parsetools (leex and yecc) for lexical analysis and parsing.
Examples
Parsing
% Parse a zone file from disk
{ok, Records} = dns_zone:parse_file("example.com.zone").
% Parse zone data from a string
ZoneData = <<"
example.com. 3600 IN SOA ns1.example.com. admin.example.com. (
2024010101 ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
86400 ) ; minimum
example.com. 3600 IN NS ns1.example.com.
www 3600 IN A 192.0.2.1
">>,
{ok, Records} = dns_zone:parse_string(ZoneData).Encoding
% Encode a single record
RR = #dns_rr{
name = ~"www.example.com.",
type = ?DNS_TYPE_A,
class = ?DNS_CLASS_IN,
ttl = 3600,
data = #dns_rrdata_a{ip = {192, 0, 2, 1}}
},
Line = dns_zone:encode_rr(RR, #{origin => ~"example.com.", relative_names => true}).
% Returns: "www 3600 IN A 192.0.2.1"
% Encode a complete zone
Records = [...],
ZoneData = dns_zone:encode_string(Records, #{origin => ~"example.com", default_ttl => 3600}).
% Write zone to file
ok = dns_zone:encode_file(Records, ~"example.com.", "output.zone").
Summary
Types
Options for encoding zone files.
Detailed error information with context and suggestions.
Error location information.
Error type classification.
Options for parsing zone files.
Functions
Encode a list of DNS resource records and write to a zone file with options.
Equivalent to encode_rdata(Type, RData, #{}).
Encode RDATA (record data) to zone file format with options.
Encode a single DNS resource record to zone file format.
Encode a single DNS resource record to zone file format with options.
Equivalent to encode_string(Records, #{}).
Encode a list of DNS resource records to zone file format with options.
Format a parse error into a human-readable string.
Parse a zone file from disk.
Parse a zone file from disk with options.
Parse zone file content from a string or binary.
Parse zone file content from a string or binary with options.
Types
-type encode_options() :: #{origin => dns:dname(), relative_names => boolean(), ttl_format => seconds | units, default_ttl => dns:ttl() | undefined, omit_class => boolean(), separator => binary()}.
Options for encoding zone files.
origin => Domain- Origin domain for relative name calculation (default:<<>>)relative_names => boolean()- Use @ and relative names (default:true)ttl_format => seconds | units- TTL format:3600or1h(default:seconds)default_ttl => TTL- Include $TTL directive if set (default:undefined)omit_class => boolean()- Omit IN class (default:false)separator => binary()- Separator between fields (default:~" ")
-type error_detail() :: #{type := error_type(), message := unicode:unicode_binary(), location => error_location(), context => binary(), suggestion => unicode:unicode_binary(), details => term()}.
Detailed error information with context and suggestions.
type- Classification of the errorlocation- Where the error occurred (line, column, file)message- Human-readable error descriptioncontext- The line of text where error occurred (if available)suggestion- Helpful suggestion for fixing the error (if available)details- Original technical error details
-type error_location() :: #{line => pos_integer(), column => pos_integer() | undefined, file => file:filename_all() | undefined}.
Error location information.
line- Line number where error occurred (1-indexed)column- Column number if available (1-indexed)file- Filename if parsing from file
-type error_type() :: file | lexer | parser | semantic.
Error type classification.
file- File I/O error (e.g., file not found)lexer- Lexical analysis error (invalid tokens)parser- Syntax parsing error (grammar violation)semantic- Semantic validation error (invalid data)
-type parse_options() :: #{origin => dns:dname(), default_ttl => dns:ttl(), default_class => dns:class(), base_dir => file:name_all(), filename => file:name_all(), chunk_size => non_neg_integer()}.
Options for parsing zone files.
origin- Initial $ORIGIN for relative domain names (default:<<>>)default_ttl- Default TTL for records without explicit TTL (default:0)default_class- Default DNS class (default:?DNS_CLASS_IN)base_dir- Base directory for $INCLUDE directives (default:"")filename- Source filename for error reporting (internal, set by parse_file)
Functions
-spec encode_file([dns:rr()], file:filename()) -> ok | {error, term()}.
Equivalent to encode_file(Records, Filename, #{}).
-spec encode_file([dns:rr()], file:filename(), encode_options()) -> ok | {error, term()}.
Encode a list of DNS resource records and write to a zone file with options.
-spec encode_rdata(dns:type(), dns:rrdata()) -> iodata().
Equivalent to encode_rdata(Type, RData, #{}).
-spec encode_rdata(dns:type(), dns:rrdata(), encode_options()) -> iodata().
Encode RDATA (record data) to zone file format with options.
Options (all optional):
origin => Domain- Origin domain for relative name calculation (default:<<>>)relative_names => boolean()- Use @ and relative names (default:true)separator => binary()- Separator between fields (default:~" ")
Examples
% Encode an MX record RDATA with custom separator
RData = #dns_rrdata_mx{preference = 10, exchange = ~"mail.example.com."},
RDataStr = dns_zone:encode_rdata(?DNS_TYPE_MX, RData, #{separator => ~"\t"}).
% Returns: "10\tmail.example.com."
% Encode an NS record RDATA with relative names
RData = #dns_rrdata_ns{dname = ~"ns1.example.com."},
RDataStr = dns_zone:encode_rdata(?DNS_TYPE_NS, RData, #{
origin => ~"example.com.",
relative_names => true
}).
% Returns: "ns1" (if ns1 is under example.com.)
Encode a single DNS resource record to zone file format.
Returns a string representing the record in zone file format.
Examples
RR = #dns_rr{
name = ~"www.example.com.",
type = ?DNS_TYPE_A,
class = ?DNS_CLASS_IN,
ttl = 3600,
data = #dns_rrdata_a{ip = {192, 0, 2, 1}}
},
Line = dns_zone:encode_rr(RR).
% Returns: "www.example.com. 3600 IN A 192.0.2.1"
-spec encode_rr(dns:rr(), encode_options()) -> iodata().
Encode a single DNS resource record to zone file format with options.
Options (all optional):
origin => Domain- Origin domain for relative name calculationrelative_names => boolean()- Use @ and relative names (default:true)ttl_format => seconds | units- TTL format:3600or1h(default:seconds)omit_class => boolean()- Omit IN class (default:false)
Examples
RR = #dns_rr{
name = ~"www.example.com.",
type = ?DNS_TYPE_A,
class = ?DNS_CLASS_IN,
ttl = 3600,
data = #dns_rrdata_a{ip = {192, 0, 2, 1}}
},
Line = dns_zone:encode_rr(RR, #{origin => ~"example.com.", relative_names => true}).
% Returns: "www 3600 IN A 192.0.2.1"
Equivalent to encode_string(Records, #{}).
-spec encode_string([dns:rr()], encode_options()) -> iodata().
Encode a list of DNS resource records to zone file format with options.
Examples
Records = [...],
ZoneData = dns_zone:encode_string(Records, #{
origin => ~"example.com",
default_ttl => 3600,
relative_names => true
}).
-spec format_error(error_detail()) -> iolist().
Format a parse error into a human-readable string.
Takes an error from parse_file/1,2 or parse_string/1,2 and returns
a formatted string suitable for display to users.
Examples
case dns_zone:parse_file("bad.zone") of
{ok, Records} -> ok;
{error, Error} ->
io:format("~s", [dns_zone:format_error(Error)])
end.
-spec parse_file(file:filename()) -> {ok, [dns:rr()]} | {error, error_detail()}.
Parse a zone file from disk.
Returns {ok, Records} where Records is a list of #dns_rr{} records,
or {error, Reason} if parsing fails.
Examples
{ok, Records} = dns_zone:parse_file("/path/to/zone.db").
-spec parse_file(file:filename(), parse_options()) -> {ok, [dns:rr()]} | {error, error_detail()}.
Parse a zone file from disk with options.
Options (all optional):
origin => Domain- Set the initial $ORIGINdefault_ttl => TTL- Set the default TTLdefault_class => Class- Set the default class (defaults to IN)base_dir => Dir- Set base directory for $INCLUDE directives
Examples
{ok, Records} = dns_zone:parse_file("zone.db", #{origin => ~"example.com."}).
{ok, Records} = dns_zone:parse_file("zone.db", #{
origin => ~"example.com.",
default_ttl => 3600
}).
-spec parse_string(binary() | string()) -> {ok, [dns:rr()]} | {error, error_detail()}.
Parse zone file content from a string or binary.
Examples
ZoneData = ~"example.com. IN A 192.0.2.1",
{ok, Records} = dns_zone:parse_string(ZoneData).
-spec parse_string(binary() | string(), parse_options()) -> {ok, [dns:rr()]} | {error, error_detail()}.
Parse zone file content from a string or binary with options.