%%% 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) -> <> = 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), <>. 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)), <>; 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), <> = 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), <>. -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.