packages feed

dns 1.4.5 → 2.0.0

raw patch · 11 files changed

+352/−323 lines, 11 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Network.DNS.Decode: receive' :: Socket -> IO (DNSMessage (RD ByteString))
- Network.DNS.Resolver: lookupRaw' :: Resolver -> Domain -> TYPE -> IO (Either DNSError (DNSMessage (RD ByteString)))
- Network.DNS.Types: DNSFormat :: DNSHeader -> [Question] -> [RR a] -> [RR a] -> [RR a] -> DNSMessage a
- Network.DNS.Types: anCount :: DNSHeader -> Int
- Network.DNS.Types: arCount :: DNSHeader -> Int
- Network.DNS.Types: data RD a
- Network.DNS.Types: data RR a
- Network.DNS.Types: dnsMapWithType :: (TYPE -> a -> b) -> DNSMessage a -> DNSMessage b
- Network.DNS.Types: dnsTraverseWithType :: Applicative f => (TYPE -> a -> f b) -> DNSMessage a -> f (DNSMessage b)
- Network.DNS.Types: nsCount :: DNSHeader -> Int
- Network.DNS.Types: qdCount :: DNSHeader -> Int
- Network.DNS.Types: rdlen :: RR a -> Int
- Network.DNS.Types: rrMapWithType :: (TYPE -> a -> b) -> RR a -> RR b
- Network.DNS.Types: toType :: String -> TYPE
- Network.DNS.Types: type RDATA = RD [Int]
- Network.DNS.Types: type ResourceRecord = RR RDATA
+ Network.DNS.Resolver: fromDNSMessage :: DNSMessage -> (DNSMessage -> a) -> Either DNSError a
+ Network.DNS.Types: BadOpt :: RCODE
+ Network.DNS.Types: BadOptRecord :: DNSError
+ Network.DNS.Types: ClientSubnet :: OPTTYPE
+ Network.DNS.Types: DNSMessage :: DNSHeader -> [Question] -> [ResourceRecord] -> [ResourceRecord] -> [ResourceRecord] -> DNSMessage
+ Network.DNS.Types: OD_ClientSubnet :: Int -> Int -> IP -> OData
+ Network.DNS.Types: OD_Unknown :: Int -> ByteString -> OData
+ Network.DNS.Types: OPT :: TYPE
+ Network.DNS.Types: OUNKNOWN :: Int -> OPTTYPE
+ Network.DNS.Types: OptRecord :: Int -> Bool -> Int -> RData -> ResourceRecord
+ Network.DNS.Types: RD_OPT :: [OData] -> RData
+ Network.DNS.Types: data OData
+ Network.DNS.Types: data OPTTYPE
+ Network.DNS.Types: data RData
+ Network.DNS.Types: data ResourceRecord
+ Network.DNS.Types: intToOptType :: Int -> OPTTYPE
+ Network.DNS.Types: optTypeToInt :: OPTTYPE -> Int
+ Network.DNS.Types: ordnssecok :: ResourceRecord -> Bool
+ Network.DNS.Types: orudpsize :: ResourceRecord -> Int
+ Network.DNS.Types: orversion :: ResourceRecord -> Int
- Network.DNS.Decode: decode :: ByteString -> Either String DNSFormat
+ Network.DNS.Decode: decode :: ByteString -> Either String DNSMessage
- Network.DNS.Decode: receive :: Socket -> IO DNSFormat
+ Network.DNS.Decode: receive :: Socket -> IO DNSMessage
- Network.DNS.Encode: encode :: DNSFormat -> ByteString
+ Network.DNS.Encode: encode :: DNSMessage -> ByteString
- Network.DNS.Resolver: fromDNSFormat :: DNSFormat -> (DNSFormat -> a) -> Either DNSError a
+ Network.DNS.Resolver: fromDNSFormat :: DNSMessage -> (DNSMessage -> a) -> Either DNSError a
- Network.DNS.Resolver: lookup :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RDATA])
+ Network.DNS.Resolver: lookup :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RData])
- Network.DNS.Resolver: lookupAuth :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RDATA])
+ Network.DNS.Resolver: lookupAuth :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RData])
- Network.DNS.Resolver: lookupRaw :: Resolver -> Domain -> TYPE -> IO (Either DNSError DNSFormat)
+ Network.DNS.Resolver: lookupRaw :: Resolver -> Domain -> TYPE -> IO (Either DNSError DNSMessage)
- Network.DNS.Types: DNSHeader :: Int -> DNSFlags -> Int -> Int -> Int -> Int -> DNSHeader
+ Network.DNS.Types: DNSHeader :: Int -> DNSFlags -> DNSHeader
- Network.DNS.Types: RD_A :: IPv4 -> RD a
+ Network.DNS.Types: RD_A :: IPv4 -> RData
- Network.DNS.Types: RD_AAAA :: IPv6 -> RD a
+ Network.DNS.Types: RD_AAAA :: IPv6 -> RData
- Network.DNS.Types: RD_CNAME :: Domain -> RD a
+ Network.DNS.Types: RD_CNAME :: Domain -> RData
- Network.DNS.Types: RD_DNAME :: Domain -> RD a
+ Network.DNS.Types: RD_DNAME :: Domain -> RData
- Network.DNS.Types: RD_MX :: Int -> Domain -> RD a
+ Network.DNS.Types: RD_MX :: Int -> Domain -> RData
- Network.DNS.Types: RD_NS :: Domain -> RD a
+ Network.DNS.Types: RD_NS :: Domain -> RData
- Network.DNS.Types: RD_OTH :: a -> RD a
+ Network.DNS.Types: RD_OTH :: ByteString -> RData
- Network.DNS.Types: RD_PTR :: Domain -> RD a
+ Network.DNS.Types: RD_PTR :: Domain -> RData
- Network.DNS.Types: RD_SOA :: Domain -> Domain -> Int -> Int -> Int -> Int -> Int -> RD a
+ Network.DNS.Types: RD_SOA :: Domain -> Domain -> Int -> Int -> Int -> Int -> Int -> RData
- Network.DNS.Types: RD_SRV :: Int -> Int -> Int -> Domain -> RD a
+ Network.DNS.Types: RD_SRV :: Int -> Int -> Int -> Domain -> RData
- Network.DNS.Types: RD_TXT :: ByteString -> RD a
+ Network.DNS.Types: RD_TXT :: ByteString -> RData
- Network.DNS.Types: ResourceRecord :: Domain -> TYPE -> Int -> Int -> a -> RR a
+ Network.DNS.Types: ResourceRecord :: Domain -> TYPE -> Int -> RData -> ResourceRecord
- Network.DNS.Types: additional :: DNSMessage a -> [RR a]
+ Network.DNS.Types: additional :: DNSMessage -> [ResourceRecord]
- Network.DNS.Types: answer :: DNSMessage a -> [RR a]
+ Network.DNS.Types: answer :: DNSMessage -> [ResourceRecord]
- Network.DNS.Types: authority :: DNSMessage a -> [RR a]
+ Network.DNS.Types: authority :: DNSMessage -> [ResourceRecord]
- Network.DNS.Types: data DNSMessage a
+ Network.DNS.Types: data DNSMessage
- Network.DNS.Types: header :: DNSMessage a -> DNSHeader
+ Network.DNS.Types: header :: DNSMessage -> DNSHeader
- Network.DNS.Types: question :: DNSMessage a -> [Question]
+ Network.DNS.Types: question :: DNSMessage -> [Question]
- Network.DNS.Types: rdata :: RR a -> a
+ Network.DNS.Types: rdata :: ResourceRecord -> RData
- Network.DNS.Types: responseA :: Int -> Question -> IPv4 -> DNSFormat
+ Network.DNS.Types: responseA :: Int -> Question -> [IPv4] -> DNSMessage
- Network.DNS.Types: responseAAAA :: Int -> Question -> IPv6 -> DNSFormat
+ Network.DNS.Types: responseAAAA :: Int -> Question -> [IPv6] -> DNSMessage
- Network.DNS.Types: rrname :: RR a -> Domain
+ Network.DNS.Types: rrname :: ResourceRecord -> Domain
- Network.DNS.Types: rrttl :: RR a -> Int
+ Network.DNS.Types: rrttl :: ResourceRecord -> Int
- Network.DNS.Types: rrtype :: RR a -> TYPE
+ Network.DNS.Types: rrtype :: ResourceRecord -> TYPE
- Network.DNS.Types: type DNSFormat = DNSMessage RDATA
+ Network.DNS.Types: type DNSFormat = DNSMessage

Files

+ Changelog view
@@ -0,0 +1,8 @@+2.0.0+	- DNSMessage is now monomorphic+	- RDATA is now monomorphic+	- Removed traversal instance for DNSMessage+	- EDNS0 encoding/decoding is now supported+	- Removed dnsMapWithType and dnsTraverseWithType functions+	- responseA and responseAAAA now take lists of IP addresses as their arguments+	- DNSHeader type no longer has qdCount, anCount, nsCount, and arCount fields
Network/DNS/Decode.hs view
@@ -1,28 +1,30 @@-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, CPP #-}  module Network.DNS.Decode (     decode   , receive-  , receive'   ) where -import Control.Applicative ((<$), (<$>), (<*), (<*>)) import Control.Monad (replicateM) import Control.Monad.Trans.Resource (ResourceT, runResourceT) import qualified Control.Exception as ControlException import Data.Bits ((.&.), shiftR, testBit) import Data.ByteString (ByteString)+import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy as BL import Data.Conduit (($$), Source) import Data.Conduit.Network (sourceSocket)-import Data.IP (toIPv4, toIPv6b)-import Data.Traversable (traverse)+import Data.IP (IP(..), toIPv4, toIPv6b) import Data.Typeable (Typeable) import Network (Socket) import Network.DNS.Internal import Network.DNS.StateBinary +#if __GLASGOW_HASKELL__ < 709+import Control.Applicative+#endif+ ----------------------------------------------------------------  @@ -34,50 +36,31 @@  -- | Receiving DNS data from 'Socket' and parse it. -receive :: Socket -> IO DNSFormat-receive sock = do-    dns <- receiveDNSFormat $ sourceSocket sock-    case traverse unpackBytes dns of-        Left e  -> ControlException.throwIO (RDATAParseError e)-        Right d -> return d---- | Receiving DNS data from 'Socket' and partially parse it.---   Unknown RDATA sections will be left as 'ByteString'-receive' :: Socket -> IO (DNSMessage (RD ByteString))-receive' sock = receiveDNSFormat $ sourceSocket sock-+receive :: Socket -> IO DNSMessage+receive = receiveDNSFormat . sourceSocket  ----------------------------------------------------------------  -- | Parsing DNS data. -decode :: BL.ByteString -> Either String DNSFormat+decode :: BL.ByteString -> Either String DNSMessage decode bs = fst <$> runSGet decodeResponse bs-            >>= traverse unpackBytes -unpackBytes :: RD ByteString -> Either String (RD [Int])-unpackBytes (RD_OTH dta) = RD_OTH . fst <$> unpack dta'- where-    len = fromIntegral $ BS.length dta-    dta' = BL.fromChunks [dta]-    unpack = runSGet (getNBytes len)-unpackBytes rd = Right $ error "unhandled case in decode" <$> rd- -----------------------------------------------------------------receiveDNSFormat :: Source (ResourceT IO) ByteString -> IO (DNSMessage (RD ByteString))+receiveDNSFormat :: Source (ResourceT IO) ByteString -> IO DNSMessage receiveDNSFormat src = fst <$> runResourceT (src $$ sink)   where     sink = sinkSGet decodeResponse  ---------------------------------------------------------------- -decodeResponse :: SGet (DNSMessage (RD ByteString))+decodeResponse :: SGet DNSMessage decodeResponse = do-    hd <- decodeHeader-    DNSFormat hd <$> decodeQueries (qdCount hd)-                 <*> decodeRRs (anCount hd)-                 <*> decodeRRs (nsCount hd)-                 <*> decodeRRs (arCount hd)+    (hd,qdCount,anCount,nsCount,arCount) <- decodeHeader+    DNSMessage hd <$> decodeQueries qdCount+                  <*> decodeRRs anCount+                  <*> decodeRRs nsCount+                  <*> decodeRRs arCount  ---------------------------------------------------------------- @@ -101,13 +84,20 @@  ---------------------------------------------------------------- -decodeHeader :: SGet DNSHeader-decodeHeader = DNSHeader <$> decodeIdentifier-                         <*> decodeFlags-                         <*> decodeQdCount-                         <*> decodeAnCount-                         <*> decodeNsCount-                         <*> decodeArCount+decodeHeader :: SGet (DNSHeader,Int,Int,Int,Int)+decodeHeader = do+        hd <- DNSHeader <$> decodeIdentifier+                        <*> decodeFlags+        qdCount <- decodeQdCount+        anCount <- decodeAnCount+        nsCount <- decodeNsCount+        arCount <- decodeArCount+        pure (hd+             ,qdCount+             ,anCount+             ,nsCount+             ,arCount+             )   where     decodeIdentifier = getInt16     decodeQdCount = getInt16@@ -123,30 +113,54 @@ decodeType :: SGet TYPE decodeType = intToType <$> getInt16 +decodeOptType :: SGet OPTTYPE+decodeOptType = intToOptType <$> getInt16+ decodeQuery :: SGet Question decodeQuery = Question <$> decodeDomain-                       <*> (decodeType <* ignoreClass)+                       <*> decodeType+                       <*  ignoreClass -decodeRRs :: Int -> SGet [RR (RD ByteString)]+decodeRRs :: Int -> SGet [ResourceRecord] decodeRRs n = replicateM n decodeRR -decodeRR :: SGet (RR (RD ByteString))+decodeRR :: SGet ResourceRecord decodeRR = do-    Question dom typ <- decodeQuery-    ttl <- decodeTTL-    len <- decodeRLen-    dat <- decodeRData typ len-    return ResourceRecord { rrname = dom-                          , rrtype = typ-                          , rrttl  = ttl-                          , rdlen  = len-                          , rdata  = dat-                          }+    dom <- decodeDomain+    typ <- decodeType+    decodeRR' dom typ   where+    decodeRR' _ OPT = do+        udps <- decodeUDPSize+        _ <- decodeERCode+        ver <- decodeOPTVer+        dok <- decodeDNSOK+        len <- decodeRLen+        dat <- decodeRData OPT len+        return OptRecord { orudpsize = udps+                         , ordnssecok = dok+                         , orversion = ver+                         , rdata = dat+                         }++    decodeRR' dom t = do+        ignoreClass+        ttl <- decodeTTL+        len <- decodeRLen+        dat <- decodeRData t len+        return ResourceRecord { rrname = dom+                              , rrtype = t+                              , rrttl  = ttl+                              , rdata  = dat+                              }+    decodeUDPSize = fromIntegral <$> getInt16+    decodeERCode = getInt8+    decodeOPTVer = fromIntegral <$> getInt8+    decodeDNSOK = flip testBit 15 <$> getInt16     decodeTTL = fromIntegral <$> get32     decodeRLen = getInt16 -decodeRData :: TYPE -> Int -> SGet (RD ByteString)+decodeRData :: TYPE -> Int -> SGet RData decodeRData NS _ = RD_NS <$> decodeDomain decodeRData MX _ = RD_MX <$> decodePreference <*> decodeDomain   where@@ -180,31 +194,55 @@     decodePriority = getInt16     decodeWeight   = getInt16     decodePort     = getInt16-+decodeRData OPT ol = RD_OPT <$> decode' ol+  where+    decode' :: Int -> SGet [OData]+    decode' l+        | l  < 0 = fail "decodeOPTData: length inconsistency"+        | l == 0 = pure []+        | otherwise = do+            optCode <- decodeOptType+            optLen <- getInt16+            dat <- decodeOData optCode optLen+            (dat:) <$> decode' (l - optLen - 4) decodeRData _  len = RD_OTH <$> getNByteString len +decodeOData :: OPTTYPE -> Int -> SGet OData+decodeOData ClientSubnet len = do+        fam <- getInt16+        srcMask <- getInt8+        scpMask <- getInt8+        rawip <- fmap fromIntegral . B.unpack <$> getNByteString (len - 4) -- 4 = 2 + 1 + 1+        ip <- case fam of+                    1 -> pure . IPv4 . toIPv4 $ take 4 (rawip ++ repeat 0)+                    2 -> pure . IPv6 . toIPv6b $ take 16 (rawip ++ repeat 0)+                    _ -> fail "Unsupported address family"+        pure $ OD_ClientSubnet srcMask scpMask ip+decodeOData (OUNKNOWN i) len = OD_Unknown i <$> getNByteString len+ ----------------------------------------------------------------  decodeDomain :: SGet Domain decodeDomain = do     pos <- getPosition     c <- getInt8-    if c == 0 then-        return ""-      else do-        let n = getValue c-        if isPointer c then do+    let n = getValue c+    -- Syntax hack to avoid using MultiWayIf+    case () of+        _ | c == 0 -> return ""+        _ | isPointer c -> do             d <- getInt8             let offset = n * 256 + d             mo <- pop offset             case mo of                 Nothing -> fail $ "decodeDomain: " ++ show offset-                Just o -> do-                    -- A pointer may refer to another pointer.-                    -- So, register this position for the domain.-                    push pos o-                    return o-          else do+                -- A pointer may refer to another pointer.+                -- So, register this position for the domain.+                Just o -> push pos o >> return o+        -- As for now, extended labels have no use.+        -- This may change some time in the future.+        _ | isExtLabel c -> return ""+        _ | otherwise -> do             hs <- getNByteString n             ds <- decodeDomain             let dom = hs `BS.append` "." `BS.append` ds@@ -213,6 +251,7 @@   where     getValue c = c .&. 0x3f     isPointer c = testBit c 7 && testBit c 6+    isExtLabel c = (not $ testBit c 7) && testBit c 6  ignoreClass :: SGet () ignoreClass = () <$ get16
Network/DNS/Encode.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RecordWildCards, CPP #-}  module Network.DNS.Encode (     encode@@ -9,16 +9,18 @@ import Control.Monad (when) import Control.Monad.State (State, modify, execState, gets) import Data.Binary (Word16)-import Data.Bits ((.|.), bit, shiftL)+import Data.Bits ((.|.), bit, shiftL, setBit) import qualified Data.ByteString.Char8 as BS import Data.ByteString.Lazy.Char8 (ByteString)-import Data.IP (fromIPv4, fromIPv6b)-import Data.Monoid (Monoid, mappend, mconcat)+import Data.IP (IP(..),fromIPv4, fromIPv6b)+import Data.List (dropWhileEnd)+import Data.Monoid ((<>)) import Network.DNS.Internal import Network.DNS.StateBinary -(+++) :: Monoid a => a -> a -> a-(+++) = mappend+#if __GLASGOW_HASKELL__ < 709+import Data.Monoid (mconcat)+#endif  ---------------------------------------------------------------- @@ -31,7 +33,6 @@     qry = defaultQuery {         header = hdr {            identifier = idt-         , qdCount = length qs          }       , question = qs       }@@ -40,37 +41,35 @@  -- | Composing DNS data. -encode :: DNSFormat -> ByteString-encode fmt = runSPut (encodeDNSFormat fmt)+encode :: DNSMessage -> ByteString+encode msg = runSPut (encodeDNSMessage msg)  ---------------------------------------------------------------- -encodeDNSFormat :: DNSFormat -> SPut-encodeDNSFormat fmt = encodeHeader hdr-                  +++ mconcat (map encodeQuestion qs)-                  +++ mconcat (map encodeRR an)-                  +++ mconcat (map encodeRR au)-                  +++ mconcat (map encodeRR ad)+encodeDNSMessage :: DNSMessage -> SPut+encodeDNSMessage msg = encodeHeader hdr+                    <> encodeNums+                    <> mconcat (map encodeQuestion qs)+                    <> mconcat (map encodeRR an)+                    <> mconcat (map encodeRR au)+                    <> mconcat (map encodeRR ad)   where-    hdr = header fmt-    qs = question fmt-    an = answer fmt-    au = authority fmt-    ad = additional fmt+    encodeNums = mconcat $ fmap putInt16 [length qs+                                         ,length an+                                         ,length au+                                         ,length ad+                                         ]+    hdr = header msg+    qs = question msg+    an = answer msg+    au = authority msg+    ad = additional msg  encodeHeader :: DNSHeader -> SPut encodeHeader hdr = encodeIdentifier (identifier hdr)-               +++ encodeFlags (flags hdr)-               +++ decodeQdCount (qdCount hdr)-               +++ decodeAnCount (anCount hdr)-               +++ decodeNsCount (nsCount hdr)-               +++ decodeArCount (arCount hdr)+                <> encodeFlags (flags hdr)   where     encodeIdentifier = putInt16-    decodeQdCount = putInt16-    decodeAnCount = putInt16-    decodeNsCount = putInt16-    decodeArCount = putInt16  encodeFlags :: DNSFlags -> SPut encodeFlags DNSFlags{..} = put16 word@@ -95,30 +94,36 @@     word = execState st 0  encodeQuestion :: Question -> SPut-encodeQuestion Question{..} =-        encodeDomain qname-    +++ putInt16 (typeToInt qtype)-    +++ put16 1+encodeQuestion Question{..} = encodeDomain qname+                           <> putInt16 (typeToInt qtype)+                           <> put16 1 +putRData :: RData -> SPut+putRData rd = do+    addPositionW 2 -- "simulate" putInt16+    rDataWrite <- encodeRDATA rd+    let rdataLength = fromIntegral . BS.length . BB.toByteString . BB.fromWrite $ rDataWrite+    let rlenWrite = BB.writeInt16be rdataLength+    return rlenWrite <> return rDataWrite+ encodeRR :: ResourceRecord -> SPut-encodeRR ResourceRecord{..} =-    mconcat-      [ encodeDomain rrname-      , putInt16 (typeToInt rrtype)-      , put16 1-      , putInt32 rrttl-      , rlenRDATA-      ]-  where-    -- Encoding rdata without using rdlen-    rlenRDATA = do-        addPositionW 2 -- "simulate" putInt16-        rDataWrite <- encodeRDATA rdata-        let rdataLength = fromIntegral . BS.length . BB.toByteString . BB.fromWrite $ rDataWrite-        let rlenWrite = BB.writeInt16be rdataLength-        return rlenWrite +++ return rDataWrite+encodeRR ResourceRecord{..} = mconcat [ encodeDomain rrname+                                      , putInt16 (typeToInt rrtype)+                                      , put16 1+                                      , putInt32 rrttl+                                      , putRData rdata+                                      ] -encodeRDATA :: RDATA -> SPut+encodeRR OptRecord{..} = mconcat [ encodeDomain BS.empty+                                 , putInt16 (typeToInt OPT)+                                 , putInt16 orudpsize+                                 , putInt32 $ if ordnssecok+                                              then setBit 0 15+                                              else 0+                                 , putRData rdata+                                 ]++encodeRDATA :: RData -> SPut encodeRDATA rd = case rd of     (RD_A ip)          -> mconcat $ map putInt8 (fromIPv4 ip)     (RD_AAAA ip)       -> mconcat $ map putInt8 (fromIPv6b ip)@@ -128,7 +133,8 @@     (RD_PTR dom)       -> encodeDomain dom     (RD_MX prf dom)    -> mconcat [putInt16 prf, encodeDomain dom]     (RD_TXT txt)       -> putByteStringWithLength txt-    (RD_OTH bytes)     -> mconcat $ map putInt8 bytes+    (RD_OTH bytes)     -> putByteString bytes+    (RD_OPT opts)      -> mconcat $ fmap encodeOData opts     (RD_SOA d1 d2 serial refresh retry expire min') -> mconcat         [ encodeDomain d1         , encodeDomain d2@@ -145,32 +151,49 @@         , encodeDomain dom         ] +encodeOData :: OData -> SPut+encodeOData (OD_ClientSubnet srcNet scpNet ip) = let dropZeroes = dropWhileEnd (==0)+                                                     (fam,raw) = case ip of+                                                                    IPv4 ip4 -> (1,dropZeroes $ fromIPv4 ip4)+                                                                    IPv6 ip6 -> (2,dropZeroes $ fromIPv6b ip6)+                                                     dataLen = 2 + 2 + length raw+                                                 in mconcat [putInt16 (optTypeToInt ClientSubnet)+                                                            ,putInt16 dataLen+                                                            ,putInt16 fam+                                                            ,putInt8 srcNet+                                                            ,putInt8 scpNet+                                                            ,mconcat $ fmap putInt8 raw+                                                            ]+encodeOData (OD_Unknown code bs) = mconcat [putInt16 code+                                           ,putInt16 $ BS.length bs+                                           ,putByteString bs+                                           ]+ -- In the case of the TXT record, we need to put the string length putByteStringWithLength :: BS.ByteString -> SPut-putByteStringWithLength bs =-       putInt8 (fromIntegral $ BS.length bs) -- put the length of the given string-   +++ putByteString bs+putByteStringWithLength bs = putInt8 (fromIntegral $ BS.length bs) -- put the length of the given string+                          <> putByteString bs  ----------------------------------------------------------------  encodeDomain :: Domain -> SPut-encodeDomain dom | BS.null dom = put8 0-encodeDomain dom = do-    mpos <- wsPop dom-    cur <- gets wsPosition-    case mpos of-        Just pos -> encodePointer pos-        Nothing  -> wsPush dom cur >>-                    mconcat [ encodePartialDomain hd-                            , encodeDomain tl-                            ]+encodeDomain dom+    | BS.null dom = put8 0+    | otherwise = do+        mpos <- wsPop dom+        cur <- gets wsPosition+        case mpos of+            Just pos -> encodePointer pos+            Nothing  -> wsPush dom cur >>+                        mconcat [ encodePartialDomain hd+                                , encodeDomain tl+                                ]   where     (hd, tl') = BS.break (=='.') dom     tl = if BS.null tl' then tl' else BS.drop 1 tl'  encodePointer :: Int -> SPut-encodePointer pos = let w = (pos .|. 0xc000) in putInt16 w+encodePointer pos = putInt16 (pos .|. 0xc000)  encodePartialDomain :: Domain -> SPut-encodePartialDomain sub = putInt8 (BS.length sub)-                      +++ putByteString sub+encodePartialDomain = putByteStringWithLength
Network/DNS/Internal.hs view
@@ -1,17 +1,13 @@-{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, DeriveFoldable #-}+{-# LANGUAGE DeriveDataTypeable #-}  module Network.DNS.Internal where  import Control.Exception (Exception)-import Control.Applicative import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS-import Data.Char (toUpper)-import Data.IP (IPv4, IPv6)+import Data.IP (IP, IPv4, IPv6) import Data.Maybe (fromMaybe) import Data.Typeable (Typeable)-import Data.Foldable (Foldable)-import Data.Traversable  ---------------------------------------------------------------- @@ -21,7 +17,17 @@ ----------------------------------------------------------------  -- | Types for resource records.-data TYPE = A | AAAA | NS | TXT | MX | CNAME | SOA | PTR | SRV | DNAME+data TYPE = A+          | AAAA+          | NS+          | TXT+          | MX+          | CNAME+          | SOA+          | PTR+          | SRV+          | DNAME+          | OPT           | UNKNOWN Int deriving (Eq, Show, Read)  rrDB :: [(TYPE, Int)]@@ -36,8 +42,18 @@   , (AAAA,  28)   , (SRV,   33)   , (DNAME, 39) -- RFC 2672+  , (OPT,   41) -- RFC 6891   ] +data OPTTYPE = ClientSubnet+             | OUNKNOWN Int+    deriving (Eq)++orDB :: [(OPTTYPE, Int)]+orDB = [+        (ClientSubnet, 8)+       ]+ rookup                  :: (Eq b) => b -> [(a,b)] -> Maybe a rookup _    []          =  Nothing rookup  key ((x,y):xys)@@ -48,10 +64,13 @@ intToType n = fromMaybe (UNKNOWN n) $ rookup n rrDB typeToInt :: TYPE -> Int typeToInt (UNKNOWN x)  = x-typeToInt t = fromMaybe 0 $ lookup t rrDB+typeToInt t = fromMaybe (error "typeToInt") $ lookup t rrDB -toType :: String -> TYPE-toType = read . map toUpper+intToOptType :: Int -> OPTTYPE+intToOptType n = fromMaybe (OUNKNOWN n) $ rookup n orDB+optTypeToInt :: OPTTYPE -> Int+optTypeToInt (OUNKNOWN x)  = x+optTypeToInt t = fromMaybe (error "optTypeToInt") $ lookup t orDB  ---------------------------------------------------------------- @@ -85,61 +104,28 @@     --   or a name server may not wish to perform     --   a particular operation (e.g., zone transfer) for particular data.   | OperationRefused+    -- | The server detected a malformed OPT RR.+  | BadOptRecord   deriving (Eq, Show, Typeable)  instance Exception DNSError  -- | Raw data format for DNS Query and Response.-data DNSMessage a = DNSFormat {+data DNSMessage = DNSMessage {     header     :: DNSHeader   , question   :: [Question]-  , answer     :: [RR a]-  , authority  :: [RR a]-  , additional :: [RR a]-  } deriving (Eq, Show, Functor, Foldable)--type DNSFormat = DNSMessage RDATA--instance Traversable DNSMessage where-  sequenceA dns = liftA3 build answer' authority' additional'-    where-      answer'     = traverse sequenceA $ answer dns-      authority'  = traverse sequenceA $ authority dns-      additional' = traverse sequenceA $ additional dns-      build ans auth add = cast { answer     = ans-                                , authority  = auth-                                , additional = add }-        where-          cast = error "unhandled case in sequenceA (DNSMessage)" <$> dns---- | Like 'fmap' except that RR 'TYPE' context is available---   within the map.-dnsMapWithType :: (TYPE -> a -> b) -> DNSMessage a -> DNSMessage b-dnsMapWithType parse dns =-    cast { answer     = mapParse $ answer dns-         , authority  = mapParse $ authority dns-         , additional = mapParse $ additional dns-         }-  where-    cast = error "unhandled case in dnsMapWithType" <$> dns-    mapParse = map (rrMapWithType parse)---- | Behaves exactly like a regular 'traverse' except that the traversing---   function also has access to the RR 'TYPE' associated with a value.-dnsTraverseWithType ::-    Applicative f =>-    (TYPE -> a -> f b) -> DNSMessage a -> f (DNSMessage b)-dnsTraverseWithType parse = sequenceA . dnsMapWithType parse+  , answer     :: [ResourceRecord]+  , authority  :: [ResourceRecord]+  , additional :: [ResourceRecord]+  } deriving (Eq, Show) +-- | For backward compatibility.+type DNSFormat = DNSMessage  -- | Raw data format for the header of DNS Query and Response. data DNSHeader = DNSHeader {     identifier :: Int   , flags      :: DNSFlags-  , qdCount    :: Int-  , anCount    :: Int-  , nsCount    :: Int-  , arCount    :: Int   } deriving (Eq, Show)  -- | Raw data format for the flags of DNS Query and Response.@@ -159,7 +145,7 @@  data OPCODE = OP_STD | OP_INV | OP_SSR deriving (Eq, Show, Enum) -data RCODE = NoErr | FormatErr | ServFail | NameErr | NotImpl | Refused deriving (Eq, Show, Enum)+data RCODE = NoErr | FormatErr | ServFail | NameErr | NotImpl | Refused | BadOpt deriving (Eq, Show, Enum)  ---------------------------------------------------------------- @@ -176,33 +162,36 @@ ----------------------------------------------------------------  -- | Raw data format for resource records.-data RR a = ResourceRecord {-    rrname :: Domain-  , rrtype :: TYPE-  , rrttl  :: Int-  , rdlen  :: Int-  , rdata  :: a-  } deriving (Eq, Show, Functor, Foldable)--type ResourceRecord = RR RDATA+data ResourceRecord = ResourceRecord {+                            rrname :: Domain+                          , rrtype :: TYPE+                          , rrttl  :: Int+                          , rdata  :: RData+                          }+                    | OptRecord {+                            orudpsize   :: Int+                          , ordnssecok  :: Bool+                          , orversion   :: Int+                          , rdata       :: RData+                          }+                    deriving (Eq,Show)  -- | Raw data format for each type.-data RD a = RD_NS Domain | RD_CNAME Domain | RD_DNAME Domain-           | RD_MX Int Domain | RD_PTR Domain+data RData = RD_NS Domain+           | RD_CNAME Domain+           | RD_DNAME Domain+           | RD_MX Int Domain+           | RD_PTR Domain            | RD_SOA Domain Domain Int Int Int Int Int-           | RD_A IPv4 | RD_AAAA IPv6 | RD_TXT ByteString+           | RD_A IPv4+           | RD_AAAA IPv6+           | RD_TXT ByteString            | RD_SRV Int Int Int Domain-           | RD_OTH a deriving (Eq, Functor, Foldable)--type RDATA = RD [Int]--instance Traversable RD where-  sequenceA (RD_OTH a) = RD_OTH <$> a-  sequenceA rd         = pure cast-    where-        cast = error "unhandled case in squenceA (RD)" <$> rd+           | RD_OPT [OData]+           | RD_OTH ByteString+    deriving (Eq) -instance Show a => Show (RD a) where+instance Show RData where   show (RD_NS dom) = BS.unpack dom   show (RD_MX prf dom) = BS.unpack dom ++ " " ++ show prf   show (RD_CNAME dom) = BS.unpack dom@@ -213,20 +202,18 @@   show (RD_SOA mn _ _ _ _ _ mi) = BS.unpack mn ++ " " ++ show mi   show (RD_PTR dom) = BS.unpack dom   show (RD_SRV pri wei prt dom) = show pri ++ " " ++ show wei ++ " " ++ show prt ++ BS.unpack dom+  show (RD_OPT od) = show od   show (RD_OTH is) = show is -instance Traversable RR where-  sequenceA rr = (\x -> fmap (const x) rr) <$> rdata rr --- | Like 'fmap' except that RR 'TYPE' context is available---   within the map.-rrMapWithType :: (TYPE -> a -> b) -> RR a -> RR b-rrMapWithType parse rr = parse (rrtype rr) <$> rr+data OData = OD_ClientSubnet Int Int IP+           | OD_Unknown Int ByteString+    deriving (Eq,Show)  ---------------------------------------------------------------- -defaultQuery :: DNSFormat-defaultQuery = DNSFormat {+defaultQuery :: DNSMessage+defaultQuery = DNSMessage {     header = DNSHeader {        identifier = 0      , flags = DNSFlags {@@ -238,10 +225,6 @@          , recAvailable = False          , rcode        = NoErr          }-     , qdCount = 0-     , anCount = 0-     , nsCount = 0-     , arCount = 0      }   , question   = []   , answer     = []@@ -249,7 +232,7 @@   , additional = []   } -defaultResponse :: DNSFormat+defaultResponse :: DNSMessage defaultResponse =   let hd = header defaultQuery       flg = flags hd@@ -263,24 +246,24 @@         }       } -responseA :: Int -> Question -> IPv4 -> DNSFormat-responseA ident q ip =+responseA :: Int -> Question -> [IPv4] -> DNSMessage+responseA ident q ips =   let hd = header defaultResponse       dom = qname q-      an = ResourceRecord dom A 300 4 (RD_A ip)+      an = fmap (ResourceRecord dom A 300 . RD_A) ips   in  defaultResponse {-          header = hd { identifier=ident, qdCount = 1, anCount = 1 }+          header = hd { identifier=ident }         , question = [q]-        , answer = [an]+        , answer = an       } -responseAAAA :: Int -> Question -> IPv6 -> DNSFormat-responseAAAA ident q ip =+responseAAAA :: Int -> Question -> [IPv6] -> DNSMessage+responseAAAA ident q ips =   let hd = header defaultResponse       dom = qname q-      an = ResourceRecord dom AAAA 300 16 (RD_AAAA ip)+      an = fmap (ResourceRecord dom AAAA 300 . RD_AAAA) ips   in  defaultResponse {-          header = hd { identifier=ident, qdCount = 1, anCount = 1 }+          header = hd { identifier=ident }         , question = [q]-        , answer = [an]+        , answer = an       }
Network/DNS/Lookup.hs view
@@ -35,7 +35,7 @@ --   >>> --   >>> rs <- makeResolvSeed defaultResolvConf --   >>> withResolver rs $ \resolver -> lookupA resolver hostname---   Right [93.184.216.119]+--   Right [93.184.216.34] -- --   The only error that we can easily cause is a timeout. We do this --   by creating and utilizing a 'ResolvConf' which has a timeout of@@ -103,7 +103,7 @@     Left err  -> return (Left err)     Right rds -> return $ mapM unTag rds   where-    unTag :: RDATA -> Either DNSError IPv4+    unTag :: RData -> Either DNSError IPv4     unTag (RD_A x) = Right x     unTag _ = Left UnexpectedRDATA @@ -126,7 +126,7 @@     Left err  -> return (Left err)     Right rds -> return $ mapM unTag rds   where-    unTag :: RDATA -> Either DNSError IPv6+    unTag :: RData -> Either DNSError IPv6     unTag (RD_AAAA x) = Right x     unTag _ = Left UnexpectedRDATA @@ -166,7 +166,7 @@     Left err  -> return (Left err)     Right rds -> return $ mapM unTag rds   where-    unTag :: RDATA -> Either DNSError (Domain,Int)+    unTag :: RData -> Either DNSError (Domain,Int)     unTag (RD_MX pr dm) = Right (dm,pr)     unTag _ = Left UnexpectedRDATA @@ -227,7 +227,7 @@ --   'lookupNSAuth'. The only difference between those two is which --   function, 'lookup' or 'lookupAuth', is used to perform the --   lookup. We take either of those as our first parameter.-lookupNSImpl :: (Resolver -> Domain -> TYPE -> IO (Either DNSError [RDATA]))+lookupNSImpl :: (Resolver -> Domain -> TYPE -> IO (Either DNSError [RData]))              -> Resolver              -> Domain              -> IO (Either DNSError [Domain])@@ -238,7 +238,7 @@     Left err  -> return (Left err)     Right rds -> return $ mapM unTag rds   where-    unTag :: RDATA -> Either DNSError Domain+    unTag :: RData -> Either DNSError Domain     unTag (RD_NS dm) = Right dm     unTag _ = Left UnexpectedRDATA @@ -315,7 +315,7 @@     Left err  -> return (Left err)     Right rds -> return $ mapM unTag rds   where-    unTag :: RDATA -> Either DNSError ByteString+    unTag :: RData -> Either DNSError ByteString     unTag (RD_TXT x) = Right x     unTag _ = Left UnexpectedRDATA @@ -344,7 +344,7 @@     Left err  -> return (Left err)     Right rds -> return $ mapM unTag rds   where-    unTag :: RDATA -> Either DNSError Domain+    unTag :: RData -> Either DNSError Domain     unTag (RD_PTR dm) = Right dm     unTag _ = Left UnexpectedRDATA @@ -406,6 +406,6 @@     Left err  -> return (Left err)     Right rds -> return $ mapM unTag rds   where-    unTag :: RDATA -> Either DNSError (Int,Int,Int,Domain)+    unTag :: RData -> Either DNSError (Int,Int,Int,Domain)     unTag (RD_SRV pri wei prt dm) = Right (pri,wei,prt,dm)     unTag _ = Left UnexpectedRDATA
Network/DNS/Resolver.hs view
@@ -14,11 +14,10 @@   , lookupAuth   -- ** Raw looking up function   , lookupRaw-  , lookupRaw'+  , fromDNSMessage   , fromDNSFormat   ) where -import Control.Applicative ((<$>), (<*>), pure) import Control.Exception (bracket) import Data.Char (isSpace) import Data.List (isPrefixOf)@@ -34,6 +33,10 @@ import System.Random (getStdRandom, randomR) import System.Timeout (timeout) +#if __GLASGOW_HASKELL__ < 709+import Control.Applicative ((<$>), (<*>), pure)+#endif+ #if mingw32_HOST_OS == 1 import Network.Socket (send) import qualified Data.ByteString.Lazy.Char8 as LB@@ -199,40 +202,45 @@ ----------------------------------------------------------------  -- | Looking up resource records of a domain. The first parameter is one of---   the field accessors of the 'DNSFormat' type -- this allows you to+--   the field accessors of the 'DNSMessage' type -- this allows you to --   choose which section (answer, authority, or additional) you would like --   to inspect for the result. -lookupSection :: (DNSFormat -> [ResourceRecord])+lookupSection :: (DNSMessage -> [ResourceRecord])               -> Resolver               -> Domain               -> TYPE-              -> IO (Either DNSError [RDATA])+              -> IO (Either DNSError [RData]) lookupSection section rlv dom typ = do     eans <- lookupRaw rlv dom typ     case eans of         Left  err -> return $ Left err-        Right ans -> return $ fromDNSFormat ans toRDATA+        Right ans -> return $ fromDNSMessage ans toRData   where     {- CNAME hack     dom' = if "." `isSuffixOf` dom then dom else dom ++ "."     correct r = rrname r == dom' && rrtype r == typ     -}     correct r = rrtype r == typ-    toRDATA = map rdata . filter correct . section+    toRData = map rdata . filter correct . section --- | Extract necessary information from 'DNSFormat'-fromDNSFormat :: DNSFormat -> (DNSFormat -> a) -> Either DNSError a-fromDNSFormat ans conv = case errcode ans of+-- | Extract necessary information from 'DNSMessage'+fromDNSMessage :: DNSMessage -> (DNSMessage -> a) -> Either DNSError a+fromDNSMessage ans conv = case errcode ans of     NoErr     -> Right $ conv ans     FormatErr -> Left FormatError     ServFail  -> Left ServerFailure     NameErr   -> Left NameError     NotImpl   -> Left NotImplemented     Refused   -> Left OperationRefused+    BadOpt    -> Left BadOptRecord   where     errcode = rcode . flags . header +-- | For backward compatibility.+fromDNSFormat :: DNSMessage -> (DNSMessage -> a) -> Either DNSError a+fromDNSFormat = fromDNSMessage+ -- | Look up resource records for a domain, collecting the results --   from the ANSWER section of the response. --@@ -241,14 +249,14 @@ --   >>> let hostname = Data.ByteString.Char8.pack "www.example.com" --   >>> rs <- makeResolvSeed defaultResolvConf --   >>> withResolver rs $ \resolver -> lookup resolver hostname A---   Right [93.184.216.119]+--   Right [93.184.216.34] ---lookup :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RDATA])+lookup :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RData]) lookup = lookupSection answer  -- | Look up resource records for a domain, collecting the results --   from the AUTHORITY section of the response.-lookupAuth :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RDATA])+lookupAuth :: Resolver -> Domain -> TYPE -> IO (Either DNSError [RData]) lookupAuth = lookupSection authority  @@ -267,7 +275,7 @@ --   And the (formatted) expected output: -- --   @---   Right (DNSFormat+--   Right (DNSMessage --           { header = DNSHeader --                        { identifier = 1, --                          flags = DNSFlags@@ -278,10 +286,7 @@ --                                      recDesired = True, --                                      recAvailable = True, --                                      rcode = NoErr },---                          qdCount = 1,---                          anCount = 1,---                          nsCount = 0,---                          arCount = 0},+--                        }, --             question = [Question { qname = \"www.example.com.\", --                                    qtype = A}], --             answer = [ResourceRecord {rrname = \"www.example.com.\",@@ -293,20 +298,15 @@ --             additional = []}) --  @ ---lookupRaw :: Resolver -> Domain -> TYPE -> IO (Either DNSError DNSFormat)+lookupRaw :: Resolver -> Domain -> TYPE -> IO (Either DNSError DNSMessage) lookupRaw = lookupRawInternal receive --- | Like 'lookupRaw' except that no unknown RDATA records are not split up---   into 'Int's.-lookupRaw' :: Resolver -> Domain -> TYPE -> IO (Either DNSError (DNSMessage (RD BS.ByteString)))-lookupRaw' = lookupRawInternal receive'- lookupRawInternal ::-    (Socket -> IO (DNSMessage a))+    (Socket -> IO DNSMessage)     -> Resolver     -> Domain     -> TYPE-    -> IO (Either DNSError (DNSMessage a))+    -> IO (Either DNSError DNSMessage) lookupRawInternal _ _   dom _   | isIllegal dom     = return $ Left IllegalDomain lookupRawInternal rcv rlv dom typ = do
Network/DNS/StateBinary.hs view
@@ -1,9 +1,8 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, CPP #-} module Network.DNS.StateBinary where  import Blaze.ByteString.Builder (Write) import qualified Blaze.ByteString.Builder as BB-import Control.Applicative ((<$>), (<*)) import Control.Monad.State (State, StateT) import qualified Control.Monad.State as ST import Control.Monad.Trans.Resource (ResourceT)@@ -19,9 +18,13 @@ import qualified Data.IntMap as IM import Data.Map (Map) import qualified Data.Map as M-import Data.Monoid (Monoid, mconcat, mappend, mempty) import Data.Word (Word8, Word16, Word32) import Network.DNS.Types++#if __GLASGOW_HASKELL__ < 709+import Control.Applicative ((<$>), (<*))+import Data.Monoid (Monoid, mconcat, mappend, mempty)+#endif  ---------------------------------------------------------------- 
Network/DNS/Types.hs view
@@ -5,20 +5,16 @@   -- * Domain     Domain   -- * Resource Records-  , ResourceRecord-  , RR (..)-  , RDATA-  , RD (..)-  , rrMapWithType+  , ResourceRecord (..)+  , RData (..), OData (..)   -- ** Resource Record Type-  , TYPE (..), intToType, typeToInt, toType+  , TYPE (..), intToType, typeToInt+  , OPTTYPE (..), intToOptType, optTypeToInt   -- * DNS Error   , DNSError (..)-  -- * DNS Format-  , DNSFormat+  -- * DNS Message   , DNSMessage (..)-  , dnsMapWithType-  , dnsTraverseWithType+  , DNSFormat   -- * DNS Header   , DNSHeader (..)   -- * DNS Flags
dns.cabal view
@@ -1,5 +1,5 @@ Name:                   dns-Version:                1.4.5+Version:                2.0.0 Author:                 Kazu Yamamoto <kazu@iij.ad.jp> Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp> License:                BSD3@@ -11,6 +11,7 @@ Category:               Network Cabal-Version:          >= 1.10 Build-Type:             Simple+Extra-Source-Files:     Changelog  Library   Default-Language:     Haskell2010
test/DecodeSpec.hs view
@@ -1,8 +1,11 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, CPP #-}  module DecodeSpec where  import Data.ByteString.Internal (ByteString(..), unsafeCreate)+#if !MIN_VERSION_bytestring(0,10,0)+import qualified Data.ByteString as BS+#endif import qualified Data.ByteString.Lazy as BL import Data.Word8 import Foreign.ForeignPtr (withForeignPtr)@@ -49,7 +52,11 @@ ----------------------------------------------------------------  fromHexString :: BL.ByteString -> BL.ByteString+#if MIN_VERSION_bytestring(0,10,0) fromHexString = BL.fromStrict . fromHexString' . BL.toStrict+#else+fromHexString = BL.pack . BS.unpack . fromHexString' . BS.pack . BL.unpack+#endif  fromHexString' :: ByteString -> ByteString fromHexString' (PS fptr off len) = unsafeCreate size $ \dst ->
test/EncodeSpec.hs view
@@ -10,26 +10,26 @@ spec :: Spec spec = do     describe "encode" $ do-        it "encodes DNSFormat correctly" $ do+        it "encodes DNSMessage correctly" $ do             check1 testQueryA             check1 testQueryAAAA             check1 testResponseA             check1 testResponseTXT      describe "decode" $ do-        it "decodes DNSFormat correctly" $ do+        it "decodes DNSMessage correctly" $ do             check2 testQueryA             check2 testQueryAAAA             check2 testResponseA             check2 testResponseTXT -check1 :: DNSFormat -> Expectation+check1 :: DNSMessage -> Expectation check1 inp = out `shouldBe` Right inp   where     bs = encode inp     out = decode bs -check2 :: DNSFormat -> Expectation+check2 :: DNSMessage -> Expectation check2 inp = bs' `shouldBe` bs   where     bs = encode inp@@ -39,26 +39,24 @@ defaultHeader :: DNSHeader defaultHeader = header defaultQuery -testQueryA :: DNSFormat+testQueryA :: DNSMessage testQueryA = defaultQuery {     header = defaultHeader {          identifier = 1000-       , qdCount = 1        }   , question = [makeQuestion "www.mew.org." A]   } -testQueryAAAA :: DNSFormat+testQueryAAAA :: DNSMessage testQueryAAAA = defaultQuery {     header = defaultHeader {          identifier = 1000-       , qdCount = 1        }   , question = [makeQuestion "www.mew.org." AAAA]   } -testResponseA :: DNSFormat-testResponseA = DNSFormat {+testResponseA :: DNSMessage+testResponseA = DNSMessage {     header = DNSHeader {          identifier = 61046        , flags = DNSFlags {@@ -70,10 +68,6 @@          , recAvailable = True          , rcode = NoErr          }-       , qdCount = 1-       , anCount = 8-       , nsCount = 2-       , arCount = 4        }   , question = [Question {                      qname = "492056364.qzone.qq.com."@@ -84,56 +78,48 @@                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [119, 147, 15, 122]                  }             , ResourceRecord {                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [119, 147, 79, 106]                  }             , ResourceRecord {                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [183, 60, 55, 43]                  }             , ResourceRecord {                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [183, 60, 55, 107]                  }             , ResourceRecord {                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [113, 108, 7, 172]                  }             , ResourceRecord {                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [113, 108, 7, 174]                  }             , ResourceRecord {                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [113, 108, 7, 175]                  }             , ResourceRecord {                    rrname = "492056364.qzone.qq.com."                  , rrtype = A                  , rrttl = 568-                 , rdlen = 4                  , rdata = RD_A $ toIPv4 [119, 147, 15, 100]                  }             ]@@ -141,14 +127,12 @@                        rrname = "qzone.qq.com."                      , rrtype = NS                      , rrttl = 45919-                     , rdlen = 10                      , rdata = RD_NS "ns-tel2.qq.com."                      }                 , ResourceRecord {                        rrname = "qzone.qq.com."                      , rrtype = NS                      , rrttl = 45919-                     , rdlen = 10                      , rdata = RD_NS "ns-tel1.qq.com."                      }                 ]@@ -156,35 +140,31 @@                         rrname = "ns-tel1.qq.com."                       , rrtype = A                       , rrttl = 46520-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [121, 14, 73, 115]                       }                  , ResourceRecord {                         rrname = "ns-tel2.qq.com."                       , rrtype = A                       , rrttl = 2890-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [222, 73, 76, 226]                       }                  , ResourceRecord {                         rrname = "ns-tel2.qq.com."                       , rrtype = A                       , rrttl = 2890-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [183, 60, 3, 202]                       }                  , ResourceRecord {                         rrname = "ns-tel2.qq.com."                       , rrtype = A                       , rrttl = 2890-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [218, 30, 72, 180]                       }                  ]   } -testResponseTXT :: DNSFormat-testResponseTXT = DNSFormat {+testResponseTXT :: DNSMessage+testResponseTXT = DNSMessage {     header = DNSHeader {          identifier = 48724        , flags = DNSFlags {@@ -196,10 +176,6 @@          , recAvailable = True          , rcode = NoErr          }-       , qdCount = 1-       , anCount = 1-       , nsCount = 2-       , arCount = 4        }   , question = [Question {                      qname = "492056364.qzone.qq.com."@@ -210,7 +186,6 @@                    rrname = "492056364.qzone.qq.com."                  , rrtype = TXT                  , rrttl = 0-                 , rdlen = 16                  , rdata = RD_TXT "simple txt line"                  }             ]@@ -218,14 +193,12 @@                        rrname = "qzone.qq.com."                      , rrtype = NS                      , rrttl = 45919-                     , rdlen = 10                      , rdata = RD_NS "ns-tel2.qq.com."                      }                 , ResourceRecord {                        rrname = "qzone.qq.com."                      , rrtype = NS                      , rrttl = 45919-                     , rdlen = 10                      , rdata = RD_NS "ns-tel1.qq.com."                      }                 ]@@ -233,28 +206,24 @@                         rrname = "ns-tel1.qq.com."                       , rrtype = A                       , rrttl = 46520-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [121, 14, 73, 115]                       }                  , ResourceRecord {                         rrname = "ns-tel2.qq.com."                       , rrtype = A                       , rrttl = 2890-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [222, 73, 76, 226]                       }                  , ResourceRecord {                         rrname = "ns-tel2.qq.com."                       , rrtype = A                       , rrttl = 2890-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [183, 60, 3, 202]                       }                  , ResourceRecord {                         rrname = "ns-tel2.qq.com."                       , rrtype = A                       , rrttl = 2890-                      , rdlen = 4                       , rdata = RD_A $ toIPv4 [218, 30, 72, 180]                       }                  ]