Packages
An Erlang DNS message library that supports most common record types, TSIG authenticated messages, EDNS0 and DNSSEC.
Current section
Files
Jump to
Current section
Files
scripts/generate_rfc_list.escript
#!/usr/bin/env escript
%% -*- erlang -*-
%% Extract RFC references from the codebase and generate a markdown list.
%%
%% This script extracts RFC references from anywhere in the source files.
%% Only uppercase "RFC" is matched (e.g., "RFC 1035" or "RFC1035").
%% Lowercase "rfc" is NOT matched, which naturally filters out identifiers
%% like rfc3597_data, parse_rfc3597_token, calendar:system_time_to_rfc3999/2.
%%
%% Usage:
%% # Generate RFC list to stdout
%% escript scripts/generate_rfc_list.escript
%%
%% # Generate RFC list and save to file
%% escript scripts/generate_rfc_list.escript --output RFC_LIST.md
%%
%% To update the README.md:
%% 1. Run: escript scripts/generate_rfc_list.escript > /tmp/rfc_list.md
%% 2. Copy the output and replace the "## Supported RFCs" section in README.md
%% 3. Or use: escript scripts/generate_rfc_list.escript --output /tmp/rfc_list.md
-mode(compile).
-define(RFC_TITLES, #{
~"1034" => ~"Domain Names - Concepts and Facilites",
~"1035" => ~"Domain Names - Implementation and Specification",
~"3596" => ~"DNS Extensions to Support IP Version 6",
~"1183" => ~"New DNS RR Definitions",
~"1876" => ~"A Means for Expressing Location Information in the Domain Name System",
~"2230" => ~"Key Exchange Delegation Record for the DNS",
~"2782" => ~"A DNS RR for specifying the location of services (DNS SRV)",
~"3403" =>
~"Dynamic Delegation Discovery System (DDDS) Part Three: The Domain Name System (DNS) Database",
~"2308" => ~"Negative Caching of DNS Queries (DNS NCACHE)",
~"3597" => ~"Handling of Unknown DNS Resource Record (RR) Types",
~"4025" => ~"A Method for Storing IPsec Keying Material in DNS",
~"4255" => ~"Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints",
~"4398" => ~"Storing Certificates in the Domain Name System (DNS)",
~"4408" =>
~"Sender Policy Framework (SPF) for Authorizing Use of Domains in E-Mail, Version 1",
~"4701" =>
~"A DNS Resource Record (RR) for Encoding Dynamic Host Configuration Protocol (DHCP) Information (DHCID RR)",
~"6672" => ~"DNAME Redirection in the DNS",
~"6698" =>
~"The DNS-Based Authentication of Named Entities (DANE) Transport Layer Security (TLS) Protocol: TLSA",
~"6844" => ~"DNS Certification Authority Authorization (CAA) Resource Record",
~"9460" =>
~"Service Binding and Parameter Specification via the DNS (DNS SVCB and HTTPS Resource Records)",
~"2535" => ~"Domain Name System Security Extensions",
~"4034" => ~"Resource Records for the DNS Security Extensions",
~"4431" => ~"The DNSSEC Lookaside Validation (DLV) DNS Resource Record",
~"5155" => ~"DNS Security (DNSSEC) Hashed Authenticated Denial of Existence",
~"6605" => ~"Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC",
~"7344" => ~"Automating DNSSEC Delegation Trust Maintenance",
~"8080" => ~"Ed25519 and Ed448 for DNSSEC",
~"9077" => ~"NSEC and NSEC3 TTL Values",
~"7553" => ~"The Uniform Resource Identifier (URI) DNS Resource Record",
~"7043" => ~"Resource Records for EUI-48 and EUI-64 Addresses in the DNS",
~"8162" => ~"Using Secure DNS to Associate Certificates with Domain Names for S/MIME",
~"9606" => ~"DNS Resolver Information",
~"7929" => ~"DNS-Based Authentication of Named Entities (DANE) Bindings for OpenPGP",
~"2845" => ~"Secret Key Transaction Authentication for DNS (TSIG)",
~"7477" => ~"Child-to-Parent Synchronization in DNS",
~"9859" => ~"Generalized DNS Notifications",
~"6891" => ~"Extension Mechanisms for DNS (EDNS(0))",
~"8976" => ~"Message Digest for DNS Zones",
~"5001" => ~"DNS Name Server Identifier (NSID) Option",
~"7871" => ~"Client Subnet in DNS Queries",
~"7873" => ~"Domain Name System (DNS) Cookies",
~"8764" => ~"DNS Long-Lived Queries (LLQ)",
~"8914" => ~"Extended DNS Errors",
~"9619" => ~"In the DNS, QDCOUNT Is (Usually) One"
}).
main(Args) ->
RootDir = get_root_dir(),
Opts = parse_args(Args, []),
%% Only match uppercase "RFC" (not lowercase "rfc")
{ok, RfcPattern} = re:compile(~B"RFC[\s-]?(\d+)", [unicode]),
io:format(standard_error, "Searching for RFC references in ~s...~n", [RootDir]),
RfcRefs = find_rfc_references(RootDir, RfcPattern),
RfcCount = maps:size(RfcRefs),
io:format(standard_error, "Found ~p unique RFCs:~n", [RfcCount]),
Markdown = generate_markdown(RfcRefs),
case proplists:get_value(output, Opts, undefined) of
undefined ->
io:format("~s", [Markdown]);
File ->
file:write_file(File, Markdown),
io:format(standard_error, "Generated RFC list: ~s~n", [File])
end.
parse_args([], Acc) ->
lists:reverse(Acc);
parse_args(["--output", File | Rest], Acc) ->
parse_args(Rest, [{output, File} | Acc]);
parse_args([Unknown | Rest], Acc) ->
io:format(standard_error, "Warning: Unknown option ~s~n", [Unknown]),
parse_args(Rest, Acc).
get_root_dir() ->
ScriptPath = escript:script_name(),
ScriptDir = filename:dirname(filename:absname(ScriptPath)),
filename:dirname(ScriptDir).
find_rfc_references(RootDir, RfcPattern) ->
Dirs = [~"src", ~"include"],
Extensions = ["*.erl", "*.hrl"],
Files = [
File
|| Ext <- Extensions,
Dir <- Dirs,
File <- filelib:wildcard(binary_to_list(filename:join([RootDir, Dir, "**", Ext])))
],
lists:foldl(fun(File, Acc) -> scan_file(File, Acc, RfcPattern) end, #{}, Files).
scan_file(File, Acc, RfcPattern) ->
case file:read_file(File) of
{ok, Content} ->
extract_rfc_numbers(Content, RfcPattern, File, Acc);
{error, Reason} ->
io:format(standard_error, "Warning: Could not read ~s: ~p~n", [File, Reason]),
Acc
end.
extract_rfc_numbers(Content, RfcPattern, File, Acc) ->
%% Extract all RFC references from the content
%% Only uppercase "RFC" is matched, so lowercase identifiers like rfc3597_data won't match
case re:run(Content, RfcPattern, [global, {capture, all, binary}]) of
{match, Matches} ->
RfcNums = [RfcNum || [_, RfcNum] <- Matches],
lists:foldl(
fun(RfcNum, AccAcc) ->
maps:update_with(
RfcNum,
fun(Refs) -> [File | Refs] end,
[File],
AccAcc
)
end,
Acc,
RfcNums
);
nomatch ->
Acc
end.
generate_markdown(RfcRefs) ->
Header =
~"""
# Supported RFCs
This library implements encoding and decoding of DNS packets according to the following RFCs.
Note that this library focuses on packet encoding/decoding only and does not implement DNS server
functionality such as socket handling or query resolution.
""",
SortedRfcs = lists:sort(
fun(A, B) -> binary_to_integer(A) < binary_to_integer(B) end, maps:keys(RfcRefs)
),
RfcItems = lists:map(
fun(RfcNum) ->
Title = maps:get(RfcNum, ?RFC_TITLES, ~"Unknown"),
RfcUrl = <<"https://tools.ietf.org/html/rfc", RfcNum/binary>>,
<<"- **[RFC ", RfcNum/binary, "](", RfcUrl/binary, ")**: ", Title/binary, "\n">>
end,
SortedRfcs
),
iolist_to_binary([Header | RfcItems]).