summaryrefslogtreecommitdiff
path: root/src/dns.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns.erl')
-rw-r--r--src/dns.erl168
1 files changed, 155 insertions, 13 deletions
diff --git a/src/dns.erl b/src/dns.erl
index f327a8f..46cda38 100644
--- a/src/dns.erl
+++ b/src/dns.erl
@@ -5,11 +5,13 @@
-export([decode_rrset/1, decode_rr/1, encode_rrset/1, encode_rr/1,
canonicalize/1, validate/1]).
+-include_lib("eunit/include/eunit.hrl").
+
-record(rr, {name :: list(), % List of name labels.
- type :: non_neg_integer(),
- class :: binary(),
- ttl :: integer(),
- rdata :: binary()}).
+ type = 0 :: non_neg_integer(),
+ class = <<>> :: binary(),
+ ttl = 0 :: integer(),
+ rdata = <<>> :: binary()}).
-type rr() :: #rr{}.
-spec decode_name_label(binary()) -> tuple().
@@ -95,6 +97,7 @@ encode_rrset([H|T], Acc) ->
encode_rrset(T, [encode_rr(H) | Acc]).
%% Canonicalise a single RR according to RFC4034 section 6.2.
+-spec canonicalize_rr_form(rr(), rr()) -> rr().
canonicalize_rr_form(RR, RRSIG) ->
%% 1. Expand domain name -- a label with a length field >= 0xC0 is
%% a two octet pointer, which we can't expand (since we don't have
@@ -113,22 +116,72 @@ canonicalize_rr_form(RR, RRSIG) ->
RR#rr{name = LCName, ttl = OrigTTL}.
-%% Canonicalise an RRset with DNSKEY, DS, and RRSIG records according
-%% to RFC4034 section 6. Records of other types are removed. Duplicate
-%% records are removed.
-canonicalize(RRset) ->
+isValidType(#rr{type = Type}) ->
+ case Type of
+ 43 -> true; % DS
+ 46 -> true; % RRSIG
+ 48 -> true; % DNSKEY
+ _ -> false
+ end.
+
+%% Sort RR's within the same RRset, remove duplicate RR's and any RR's
+%% not of the types DNSKEY, DS or RRSIG.
+canonicalize_rr_ordering(RRs) ->
+ L1 = lists:takewhile(fun isValidType/1, RRs),
+ %%RRsets = splitOnName(L1),
+ %%SortedRRsets = lists:map(fun(L) -> lists:usort(fun cmpRR/2, L) end, L1),
+ %%OneList = lists:append(SortedRRsets),
+ lists:usort(fun cmpRR/2, L1).
+
+%% Canonicalise a list of RR's of the types DNSKEY, DS, and RRSIG
+%% according to RFC4034 section 6. Records of other types are
+%% removed. Duplicate records are removed.
+-spec canonicalize(list()) -> list().
+canonicalize(RRs) ->
%% 6.1 owner name order
- RRset61 = RRset, % TODO
+ RRs61 = RRs, % TODO
%% 6.2 RR form
- [DS, RRSIG | Rest] = RRset61,
+ [DS, RRSIG | Rest] = RRs61,
C14N_DS = canonicalize_rr_form(DS, RRSIG),
- RRset62 = [C14N_DS, RRSIG | Rest],
+ RRs62 = [C14N_DS, RRSIG | Rest],
%% 6.3 RR ordering (and dropping duplicates)
- RRset63 = RRset62,
+ RRs63 = canonicalize_rr_ordering(RRs62),
+
+ RRs63.
+
+%% cmpRR(A, B) when A#rr.type =< B#rr.type,
+%% A#rr.class =< B#rr.class,
+%% A#rr.ttl =< B#rr.ttl,
+%% A#rr.rdata =< B#rr.rdata ->
+%% cmpRRname(A#rr.name, B#rr.name);
+%% cmpRR(_, _) ->
+%% false.
+
+%% @doc
+-spec cmpRR(rr(), rr()) -> boolean().
+cmpRR(A, B) ->
+ case cmpRRname(A#rr.name, B#rr.name) of
+ equal ->
+ ?debugFmt("~p == ~p, next", [A#rr.name, B#rr.name]),
+ case A#rr.type == B#rr.type of
+ false -> A#rr.type =< B#rr.type;
+ true -> case A#rr.class == B#rr.class of
+ false -> A#rr.class =< B#rr.class;
+ true -> case A#rr.ttl == B#rr.ttl of
+ false -> A#rr.ttl =< B#rr.ttl;
+ true -> A#rr.rdata =< B#rr.rdata
+ end
+ end
+ end;
+ NameCmp -> NameCmp
+ end.
- RRset63.
+cmpRRname(A, B) when A == B -> equal;
+cmpRRname(A, B) when length(A) < length(B) -> true;
+cmpRRname(A, B) when length(A) > length(B) -> false;
+cmpRRname(A, B) -> A =< B.
%% Is the RR set valid for our needs from a DNS point of view? If so,
%% return the signature inception time of the RRSIG covering the DS
@@ -150,3 +203,92 @@ validate(RRsetBin) ->
%% TODO: Add unit tests.
+
+-define(TV_RR1_inbin,
+<<7,101,120,97,109,112,108,101,3,99,111,109,0,0,43,0,1,0,0,14,16,0,36,82,106,
+ 13,2,89,208,13,15,120,173,192,134,9,41,169,35,70,122,194,189,203,240,40,210,
+ 4,171,20,30,135,63,107,184,116,61,213,134,7,101,120,97,109,112,108,101,3,99,
+ 111,109,0,0,46,0,1,0,0,14,16,0,87,0,43,13,2,0,0,14,16,87,22,3,11,87,3,142,11,
+ 80,81,3,99,111,109,0,6,228,88,59,0,197,54,50,211,112,165,110,118,14,215,62,
+ 255,210,31,169,117,192,113,47,232,31,111,175,28,118,31,225,190,139,249,250,
+ 244,69,217,9,111,122,75,130,10,159,190,71,241,184,230,58,126,189,225,42,29,
+ 195,7,217,85,233,231,155,0,0,48,0,1,0,0,14,16,0,68,1,0,3,13,209,167,133,117,
+ 137,124,191,163,201,10,151,19,139,232,224,244,203,106,201,233,28,167,30,177,
+ 84,53,125,127,85,116,219,50,35,216,117,50,127,240,195,143,219,193,12,65,95,5,
+ 16,116,0,141,5,83,66,213,40,91,22,196,101,145,127,109,68,210,7,101,120,97,
+ 109,112,108,101,3,99,111,109,0,0,43,0,1,0,0,14,16,0,36,82,106,13,2,89,208,13,
+ 15,120,173,192,134,9,41,169,35,70,122,194,189,203,240,40,210,4,171,20,30,135,
+ 63,107,184,116,61,213,134,0,0,48,0,1,0,0,14,16,0,68,1,0,3,13,209,167,133,117,
+ 137,124,191,163,201,10,151,19,139,232,224,244,203,106,201,233,28,167,30,177,
+ 84,53,125,127,85,116,219,50,35,216,117,50,127,240,195,143,219,193,12,65,95,5,
+ 16,116,0,141,5,83,66,213,40,91,22,196,101,145,127,109,68,210>>).
+-define(TV_RR1_in, decode_rrset(?TV_RR1_inbin)).
+-define(TV_RR1_out, []).
+
+gen_rrset(Name) ->
+ gen_rrset(Name, 0, 0, <<>>).
+gen_rrset(Name, Type) ->
+ gen_rrset(Name, Type, 0, <<>>).
+gen_rrset(Name, Type, TTL, RDATA) ->
+ N = string:tokens(Name, "."),
+ RRs = [#rr{name = N, type = Type, class = <<0, 1>>, ttl = TTL, rdata = RDATA},
+ #rr{name = N, type = 46, class = <<0, 1>>, ttl = TTL,
+ rdata = <<0,0,0,0, TTL:32/integer>>}],
+ %%?debugFmt("gen_rrset: ~p", [RRs]),
+ RRs.
+
+basic_test_() ->
+ {ok, SingleRRbin} = file:read_file("test/testdata/dnssec/testrrsets/single-record"),
+ [SingleRR | _] = decode_rrset(SingleRRbin),
+ [G | _] = gen_rrset("example.com", 43, 3600,
+ <<82,106,13,2,89,208,13,15,120,173,192,134,9,41,
+ 169,35,70,122,194,189,203,240,40,210,4,171,20,
+ 30,135,63,107,184,116,61,213,134>>),
+ [?_assertMatch(#rr{name = ["example", "com"],
+ type = 43, class = <<0,1>>, ttl = 3600}, SingleRR),
+ ?_assertEqual(G, SingleRR)].
+
+-define(TV_R1, gen_rrset("example.com", 43, 3600, <<>>)).
+-define(TV_R2, gen_rrset("com", 43, 3600, <<>>)).
+-define(TV_R3, gen_rrset("com", 43, 300, <<>>)).
+
+c14n_test_() ->
+ [
+ %% Reverse order, names.
+ ?_assertEqual(lists:append([?TV_R2, ?TV_R1]),
+ canonicalize(lists:append([?TV_R1, ?TV_R2]))),
+ %% Remove duplicate R1.
+ ?_assertEqual(lists:append([?TV_R2, ?TV_R1]),
+ canonicalize(lists:append([?TV_R1, ?TV_R2, ?TV_R1]))),
+ %% Reverse order, TTL.
+ ?_assertEqual(lists:append([?TV_R3, ?TV_R2]),
+ canonicalize(lists:append([?TV_R2, ?TV_R3]))),
+ %% Sorting and removing.
+ %%?_assertEqual(lists:append([?TV_R3, ?TV_R2, ?TV_R1]),
+ %% canonicalize(lists:append([?TV_R2, ?TV_R3, ?TV_R1, ?TV_R3]))),
+ ?_assert(true)].
+
+sorting_test_() ->
+ [
+ ?_assertEqual(lists:append([
+ gen_rrset("example", 43),
+ gen_rrset("a.example", 43),
+ gen_rrset("yljkjljk.a.example", 43),
+ gen_rrset("Z.a.example", 43),
+ gen_rrset("zABC.a.EXAMPLE", 43),
+ gen_rrset("z.example", 43),
+ gen_rrset("\001.z.example", 43),
+ gen_rrset("*.z.example", 43),
+ gen_rrset("\200.z.example", 43)]),
+ canonicalize(
+ lists:append([
+ gen_rrset("\001.z.example", 43),
+ gen_rrset("a.example", 43),
+ gen_rrset("example", 43),
+ gen_rrset("Z.a.example", 43),
+ gen_rrset("zABC.a.EXAMPLE", 43),
+ gen_rrset("\200.z.example", 43),
+ gen_rrset("z.example", 43),
+ gen_rrset("*.z.example", 43),
+ gen_rrset("yljkjljk.a.example", 43)])))
+ ].