summaryrefslogtreecommitdiff
path: root/src/dns.erl
blob: b8a8ffe4ba41c9453c324e9917b478a84afd7b2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
%%% Copyright (c) 2016, NORDUnet A/S.
%%% See LICENSE for licensing information.

-module(dns).
-export([decode_rrset/1, decode_rr/1, encode_rrset/1, encode_rr/1,
         canonicalize_dsrr/2]).

-record(rr, {name :: list(),                    % List of name labels.
             type :: binary(),
             class :: binary(),
             ttl :: integer(),
             rdata :: binary()}).
-type rr() :: #rr{}.

-spec decode_name_label(binary()) -> tuple().
decode_name_label(RRbin) ->
    <<Len:8/integer, Label:Len/binary, Rest/binary>> = RRbin,
    {binary_to_list(Label), Rest}.

-spec encode_name_label(string()) -> binary().
encode_name_label(Label) ->
    LabelBin = list_to_binary(Label),
    Len = byte_size(LabelBin),
    <<Len:8/integer, LabelBin/binary>>.

decode_name(RRbin) ->
    decode_name(RRbin, []).
decode_name(<<0, Rest/binary>>, Acc) ->
    {lists:reverse(Acc), Rest};
decode_name(RRbin, Acc) ->
    {Label, Rest} = decode_name_label(RRbin),
    decode_name(Rest, [Label | Acc]).

-spec encode_name(list()) -> binary().
encode_name(Name) ->
    encode_name(Name, []).
encode_name([], Acc) ->
    Bin = list_to_binary(lists:reverse(Acc)),
    <<Bin/binary, 0>>;
encode_name([H|T], Acc) ->
    encode_name(T, [encode_name_label(H) | Acc]).

-spec decode_rr(binary()) -> {rr(), binary()}.
decode_rr(RRBin) ->
    {Name, RestRR} = decode_name(RRBin),
    <<Type:2/binary,
      Class:2/binary,
      TTL:4/integer-unit:8,
      RDLength:2/integer-unit:8,
      RDATA:RDLength/binary,
      Rest/binary>> = RestRR,
    {#rr{name = Name, type = Type, class = Class, ttl = TTL, rdata = RDATA},
     Rest}.

-spec decode_rrset(binary()) -> [rr()].
decode_rrset(RRSet) ->
    decode_rrset(RRSet, []).
decode_rrset(<<>>, Acc) ->
    lists:reverse(Acc);
decode_rrset(RRSet, Acc) ->
    {RR, Rest} = decode_rr(RRSet),
    decode_rrset(Rest, [RR | Acc]).

-spec encode_rr(rr()) -> binary().
encode_rr(#rr{name = Name, type = Type, class = Class, ttl = TTL, rdata = RDATA}) ->
    EncodedName = encode_name(Name),
    RDLength = byte_size(RDATA),
    <<EncodedName/binary,
      Type:2/binary,
      Class:2/binary,
      TTL:4/integer-unit:8,
      RDLength:2/integer-unit:8,
      RDATA/binary>>.

-spec encode_rrset(list()) -> binary().
encode_rrset(RRSet) ->
    encode_rrset(RRSet, []).
encode_rrset([], Acc) ->
    list_to_binary(lists:reverse(Acc));
encode_rrset([H|T], Acc) ->
    encode_rrset(T, [encode_rr(H) | Acc]).

%% Cacnonicalise a single DS RR according to RFC4034 section 6.2.
canonicalize_dsrr(DS, RRSIG) ->
    %% 1. expand domain name
    %% FIXME: What does a compressed name look like?

    %% 2. lowercase
    LCName = lists:map(fun(L) -> string:to_lower(L) end, DS#rr.name),

    %% 3. N/A for DS
    %% 4. N/A for DS FIXME: verify

    %% 5. set TTL to that of the RRSIG
    OrigTTL = RRSIG#rr.ttl,

    DS#rr{name = LCName, ttl = OrigTTL}.

%% TODO: Add unit tests.