packages feed

dnsbase-1.0.0.0: internal/Net/DNSBase/Internal/Error.hs

-- |
-- Module      : Net.DNSBase.Internal.Error
-- Description : DNS protocol and library error types
-- Copyright   : (c) IIJ Innovation Institute Inc., 2009
--               (c) Viktor Dukhovni, 2020-2026
-- License     : BSD-3-Clause
-- Maintainer  : ietf-dane@dukhovni.org
-- Stability   : unstable
{-# LANGUAGE RecordWildCards #-}

module Net.DNSBase.Internal.Error
    ( DNSError(..)
    , DecodeContext(..)
    , DnsSection(..)
    , DnsXprt(..)
    , MessageSource(..)
    , NetworkContext(..)
    , ProtocolContext(..)
    , UserContext(..)
    , EncodeErr(..)
    , EncodeContext(..)
    ) where

import Control.Exception (Exception, IOException)

import Net.DNSBase.Internal.Domain
import Net.DNSBase.Internal.Peer
import Net.DNSBase.Internal.Present
import Net.DNSBase.Internal.RCODE (RCODE)
import Net.DNSBase.Internal.RRTYPE (RRTYPE)
import Net.DNSBase.Internal.Util

-- | DNS API errors.
--
data DNSError
    = BadConfiguration String
      -- ^ Resolver misconfiguration.
    | BadNameserver IOException
      -- ^ Nameserver name -> address lookup failure.
    | DecodeError DecodeContext String
      -- ^ Error while decoding from wire form.
    | EncodeError EncodeContext
      -- ^ Error while encoding to wire form.
    | InvalidDomain String
      -- ^ Invalid domain name presentation form.
    | NetworkError NetworkContext
      -- ^ Error in connection establishment, data transmission or a timeout.
    | ProtocolError ProtocolContext
      -- ^ Unexpected DNS message.
    | ResponseError RCODE
      -- ^ DNS message indicates a remote error condition.
    | UserError UserContext
      -- ^ Invalid request.
    deriving (Eq)

instance Exception DNSError
instance Show DNSError where
    showsPrec _ (BadConfiguration rc) = showString "Configuration error: " .
                                        showString rc
    showsPrec _ (BadNameserver io)    = showString "Unusable nameserver: " .
                                        shows io
    showsPrec _ (DecodeError ctx str) = showString "Decode error: " .
                                        presentString ctx . showChar ' ' . showString str
    showsPrec _ (EncodeError ec)      = showString "encoding error: " .
                                        shows ec
    showsPrec _ (InvalidDomain ed)    = showString "invalid domain: " .
                                        showString ed
    showsPrec _ (NetworkError en)     = shows en
    showsPrec _ (ProtocolError ep)    = shows ep
    showsPrec _ (ResponseError rc)    = showString "server error: rcode = " .
                                        showString (presentString rc mempty)
    showsPrec _ (UserError eu)        = shows eu

----------------------------------------------------------------

-- | Request or response context in which a failure occurred.  The
-- `decodeTriple` holds the name, class and type of the problem RR, provided
-- the error was not in one of those fields.
data DecodeContext
    = DecodeContext
    { decodeSection :: DnsSection
    , decodeSource  :: Maybe MessageSource
    , decodeTriple  :: Maybe DnsTriple
    } deriving (Eq)

instance Presentable DecodeContext where
    present DecodeContext {..} =
        maybe id ((present @String "from" .) . presentSp) decodeSource
        . presentSp @String "in" . presentSp decodeSection
        . maybe id ((presentSp @String "at" .) . presentSp) decodeTriple

-- | Message /section/ for error reporting.  The message header and EDNS @OPT@
-- record are also considered /sections/ in this context.
data DnsSection
    = DnsHeaderSection
      -- ^ While parsing the message header.
    | DnsQuestionSection
      -- ^ While parsing the question section.
    | DnsAnswerSection
      -- ^ While parsing the answer section.
    | DnsAuthoritySection
      -- ^ While parsing the authority section.
    | DnsAdditionalSection
      -- ^ While parsing the additional section.
    | DnsEDNSSection
      -- ^ While parsing the EDNS OPT record.
    | DnsNonSection
      -- ^ While parsing a wire-form message fragment.
  deriving (Eq, Show)

instance Presentable DnsSection where
    present DnsHeaderSection     = present @String "the message header"
    present DnsQuestionSection   = present @String "the question section"
    present DnsAnswerSection     = present @String "the answer section"
    present DnsAuthoritySection  = present @String "the authority section"
    present DnsAdditionalSection = present @String "the additional section"
    present DnsEDNSSection       = present @String "an EDNS OPT pseudo-RR"
    present DnsNonSection        = present @String "a wire-form fragment"

----------------------------------------------------------------

data NetworkContext =
    -- | The number of retries for the request was exceeded.
    RetryLimitExceeded
    -- | TCP fallback request timed out.
  | TimeoutExpired
    -- | Network failure.
  | NetworkFailure IOException
  | ServerFailure
  deriving (Eq, Show)

data ProtocolContext =
    -- ^ The sequence number of the answer doesn't match our query. This
    --   could indicate foul play.
    SequenceNumberMismatch
    -- ^ The question section of the response doesn't match our query. This
    --   could indicate foul play.
  | QuestionMismatch
  deriving (Eq, Show)

data UserContext =
    -- | The RRTYPE requested is invalid for queries.
    InvalidQueryType RRTYPE
    -- | The domain for query is illegal.
  | IllegalDomain String
    -- | The response message question count is not equal to 1.
  | BadResponseQuestionCount Int
  deriving (Eq, Show)

----------------------------------------------------------------

-- | Encoding error, polymorphic over the context type
data EncodeErr r where
    -- | message or field too long
    EncodeTooLong :: (Typeable r, Show r, Eq r) => r -> EncodeErr r
    -- | Invalid input
    CantEncode    :: (Typeable r, Show r, Eq r) => r -> EncodeErr r
    -- | Unencodable reserved type
    ReservedType  :: (Typeable r, Show r, Eq r) => RRTYPE -> r -> EncodeErr r
    -- | RCODE or flags require EDNS
    EDNSRequired  :: EncodeErr r

deriving instance (Eq r) => Eq (EncodeErr r)
deriving instance (Show r) => Show (EncodeErr r)

data EncodeContext = forall r. (Typeable r, Show r, Eq r) => EncodeContext (EncodeErr r)

instance Show EncodeContext where
    show (EncodeContext err) = show err

instance Eq EncodeContext where
    (EncodeContext (EncodeTooLong (_a :: a))) == (EncodeContext (EncodeTooLong (_b :: b))) =
        case teq a b of
            Just Refl -> _a == _b
            _         -> False
    (EncodeContext (CantEncode (_a :: a))) == (EncodeContext (CantEncode (_b :: b))) =
        case teq a b of
            Just Refl -> _a == _b
            _         -> False
    (EncodeContext EDNSRequired) == (EncodeContext EDNSRequired) = True
    _ == _ = False