packages feed

dnsbase-1.0.0.0: src/Net/DNSBase/RRSet.hs

{-|
Module      : Net.DNSBase.RRSet
Description : Owner/class/type RR sets with associated DNSSEC signatures
Copyright   : (c) Viktor Dukhovni, 2026
License     : BSD-3-Clause
Maintainer  : ietf-dane@dukhovni.org
Stability   : unstable

An 'RRSet' groups a flat list of 'RR' values into the standard
DNS unit: records sharing the same (owner, class, type),
together with any 'RRSIG' records covering them.  The
'rrSetsFromList' function partitions a flat list — typically
the answer or authority section of a 'Net.DNSBase.Message.DNSMessage' — into the
corresponding RRSets, attaching each RRSIG to the set it
covers based on the type-covered field.
-}
{-# LANGUAGE RecordWildCards #-}
module Net.DNSBase.RRSet
    ( RRSet(..)
    , rrSetsFromList
    )
    where
import qualified Data.List as L
import qualified Data.List.NonEmpty as NE

import Net.DNSBase.Internal.Domain
import Net.DNSBase.Internal.RR
import Net.DNSBase.Internal.RRTYPE
import Net.DNSBase.Internal.RRCLASS
import Net.DNSBase.Internal.Util
import Net.DNSBase.RData.Dnssec

data RRSet = RRSet
    { rrSetOwner :: Domain
    , rrSetClass :: RRCLASS
    , rrSetType  :: RRTYPE
    , rrSetRecs  :: [RR]
    , rrSetSigs  :: [RR]
    }

rrSetsFromList :: [RR] -> [RRSet]
rrSetsFromList rrs = rrs
    & map decorate
    & L.sortBy (comparing drrKey)
    & NE.groupWith drrKey
    & makeSets
  where
    decorate :: RR -> (RR, RRTYPE, Domain)
    decorate rr =
        let !styp = maybe (rrType rr) rrsigType (rrDataCast rr)
            !host = canonicalise (rrOwner rr)
         in (rr, styp, host)

    drrKey :: (RR, RRTYPE, Domain) -> (RRTYPE, RRCLASS, Domain)
    drrKey (rr, typ, host) = (typ, rrClass rr, host)

    makeSets :: [NonEmpty (RR, RRTYPE, Domain)] -> [RRSet]
    makeSets [] = []
    makeSets (((rr@(rrClass -> rrSetClass), rrSetType, rrSetOwner) :| rest) : grps)
        | !owner <- rrOwner rr
        , (rrSetRecs, rrSetSigs) <- L.partition ((== rrSetType) . rrType)
            $ rr : rrsOfWithOwner owner rest
        , not (null rrSetRecs) = RRSet {..} : makeSets grps
        | otherwise = makeSets grps

    rrsOfWithOwner owner = foldr go []
      where
        go (setOwner -> !h) !t = h : t
        setOwner (r, _, _) = r {rrOwner = owner}