diff options
Diffstat (limited to 'src/dns.erl')
-rw-r--r-- | src/dns.erl | 168 |
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)]))) + ]. |