dns 1.2.3 → 1.3.0
raw patch · 6 files changed
+148/−41 lines, 6 filesdep −attoparsec-conduitdep ~conduit-extraPVP ok
version bump matches the API change (PVP)
Dependencies removed: attoparsec-conduit
Dependency ranges changed: conduit-extra
API changes (from Hackage documentation)
- Network.DNS.Types: data DNSFormat
- Network.DNS.Types: data RDATA
- Network.DNS.Types: data ResourceRecord
+ Network.DNS.Decode: instance Exception RDATAParseError
+ Network.DNS.Decode: instance Show RDATAParseError
+ Network.DNS.Decode: instance Typeable RDATAParseError
+ 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: data DNSMessage a
+ 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: rrMapWithType :: (TYPE -> a -> b) -> RR a -> RR b
+ Network.DNS.Types: type DNSFormat = DNSMessage RDATA
+ Network.DNS.Types: type RDATA = RD [Int]
+ Network.DNS.Types: type ResourceRecord = RR RDATA
- Network.DNS.Types: DNSFormat :: DNSHeader -> [Question] -> [ResourceRecord] -> [ResourceRecord] -> [ResourceRecord] -> DNSFormat
+ Network.DNS.Types: DNSFormat :: DNSHeader -> [Question] -> [RR a] -> [RR a] -> [RR a] -> DNSMessage a
- Network.DNS.Types: RD_A :: IPv4 -> RDATA
+ Network.DNS.Types: RD_A :: IPv4 -> RD a
- Network.DNS.Types: RD_AAAA :: IPv6 -> RDATA
+ Network.DNS.Types: RD_AAAA :: IPv6 -> RD a
- Network.DNS.Types: RD_CNAME :: Domain -> RDATA
+ Network.DNS.Types: RD_CNAME :: Domain -> RD a
- Network.DNS.Types: RD_MX :: Int -> Domain -> RDATA
+ Network.DNS.Types: RD_MX :: Int -> Domain -> RD a
- Network.DNS.Types: RD_NS :: Domain -> RDATA
+ Network.DNS.Types: RD_NS :: Domain -> RD a
- Network.DNS.Types: RD_OTH :: [Int] -> RDATA
+ Network.DNS.Types: RD_OTH :: a -> RD a
- Network.DNS.Types: RD_PTR :: Domain -> RDATA
+ Network.DNS.Types: RD_PTR :: Domain -> RD a
- Network.DNS.Types: RD_SOA :: Domain -> Domain -> Int -> Int -> Int -> Int -> Int -> RDATA
+ Network.DNS.Types: RD_SOA :: Domain -> Domain -> Int -> Int -> Int -> Int -> Int -> RD a
- Network.DNS.Types: RD_SRV :: Int -> Int -> Int -> Domain -> RDATA
+ Network.DNS.Types: RD_SRV :: Int -> Int -> Int -> Domain -> RD a
- Network.DNS.Types: RD_TXT :: ByteString -> RDATA
+ Network.DNS.Types: RD_TXT :: ByteString -> RD a
- Network.DNS.Types: ResourceRecord :: Domain -> TYPE -> Int -> Int -> RDATA -> ResourceRecord
+ Network.DNS.Types: ResourceRecord :: Domain -> TYPE -> Int -> Int -> a -> RR a
- Network.DNS.Types: additional :: DNSFormat -> [ResourceRecord]
+ Network.DNS.Types: additional :: DNSMessage a -> [RR a]
- Network.DNS.Types: answer :: DNSFormat -> [ResourceRecord]
+ Network.DNS.Types: answer :: DNSMessage a -> [RR a]
- Network.DNS.Types: authority :: DNSFormat -> [ResourceRecord]
+ Network.DNS.Types: authority :: DNSMessage a -> [RR a]
- Network.DNS.Types: header :: DNSFormat -> DNSHeader
+ Network.DNS.Types: header :: DNSMessage a -> DNSHeader
- Network.DNS.Types: question :: DNSFormat -> [Question]
+ Network.DNS.Types: question :: DNSMessage a -> [Question]
- Network.DNS.Types: rdata :: ResourceRecord -> RDATA
+ Network.DNS.Types: rdata :: RR a -> a
- Network.DNS.Types: rdlen :: ResourceRecord -> Int
+ Network.DNS.Types: rdlen :: RR a -> Int
- Network.DNS.Types: rrname :: ResourceRecord -> Domain
+ Network.DNS.Types: rrname :: RR a -> Domain
- Network.DNS.Types: rrttl :: ResourceRecord -> Int
+ Network.DNS.Types: rrttl :: RR a -> Int
- Network.DNS.Types: rrtype :: ResourceRecord -> TYPE
+ Network.DNS.Types: rrtype :: RR a -> TYPE
Files
- Network/DNS/Decode.hs +42/−9
- Network/DNS/Internal.hs +68/−12
- Network/DNS/Lookup.hs +6/−6
- Network/DNS/Resolver.hs +17/−3
- Network/DNS/Types.hs +11/−4
- dns.cabal +4/−7
Network/DNS/Decode.hs view
@@ -1,13 +1,15 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, ViewPatterns, DeriveDataTypeable #-} 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.Char8 as BS@@ -15,34 +17,65 @@ import Data.Conduit (($$), Source) import Data.Conduit.Network (sourceSocket) import Data.IP (toIPv4, toIPv6)-import Data.Maybe (fromMaybe)+import Data.Traversable (traverse)+import Data.Typeable (Typeable) import Network (Socket) import Network.DNS.Internal import Network.DNS.StateBinary ---------------------------------------------------------------- ++data RDATAParseError = RDATAParseError String+ deriving (Show, Typeable)++instance ControlException.Exception RDATAParseError++ -- | Receiving DNS data from 'Socket' and parse it. receive :: Socket -> IO DNSFormat-receive sock = receiveDNSFormat $ sourceSocket sock+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++ ---------------------------------------------------------------- -- | Parsing DNS data. decode :: BL.ByteString -> Either String DNSFormat decode bs = fst <$> runSGet decodeResponse bs+ >>= traverse unpackBytes +unpackBytes :: RD ByteString -> Either String (RD [Int])+unpackBytes (RD_OTH dta) = fmap (RD_OTH . fst) $ unpack $ dta'+ where+ len = fromIntegral $ BS.length dta+ dta' = BL.fromChunks [dta]+ unpack = runSGet (getNBytes len)+unpackBytes rd = Right $ cast rd+ where+ cast = fmap (error "unhandled case in decode")+++ -----------------------------------------------------------------receiveDNSFormat :: Source (ResourceT IO) ByteString -> IO DNSFormat+receiveDNSFormat :: Source (ResourceT IO) ByteString -> IO (DNSMessage (RD ByteString)) receiveDNSFormat src = fst <$> runResourceT (src $$ sink) where sink = sinkSGet decodeResponse ---------------------------------------------------------------- -decodeResponse :: SGet DNSFormat+decodeResponse :: SGet (DNSMessage (RD ByteString)) decodeResponse = do hd <- decodeHeader DNSFormat hd <$> decodeQueries (qdCount hd)@@ -98,10 +131,10 @@ decodeQuery = Question <$> decodeDomain <*> (decodeType <* ignoreClass) -decodeRRs :: Int -> SGet [ResourceRecord]+decodeRRs :: Int -> SGet [RR (RD ByteString)] decodeRRs n = replicateM n decodeRR -decodeRR :: SGet ResourceRecord+decodeRR :: SGet (RR (RD ByteString)) decodeRR = do Question dom typ <- decodeQuery ttl <- decodeTTL@@ -117,7 +150,7 @@ decodeTTL = fromIntegral <$> get32 decodeRLen = getInt16 -decodeRData :: TYPE -> Int -> SGet RDATA+decodeRData :: TYPE -> Int -> SGet (RD ByteString) decodeRData NS _ = RD_NS <$> decodeDomain decodeRData MX _ = RD_MX <$> decodePreference <*> decodeDomain where@@ -155,7 +188,7 @@ decodeWeight = getInt16 decodePort = getInt16 -decodeRData _ len = RD_OTH <$> getNBytes len+decodeRData _ len = RD_OTH <$> getNByteString len ----------------------------------------------------------------
Network/DNS/Internal.hs view
@@ -1,14 +1,17 @@-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, DeriveFoldable #-} 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.Maybe (fromMaybe) import Data.Typeable (Typeable)+import Data.Foldable (Foldable)+import Data.Traversable ---------------------------------------------------------------- @@ -86,14 +89,49 @@ instance Exception DNSError -- | Raw data format for DNS Query and Response.-data DNSFormat = DNSFormat {+data DNSMessage a = DNSFormat { header :: DNSHeader , question :: [Question]- , answer :: [ResourceRecord]- , authority :: [ResourceRecord]- , additional :: [ResourceRecord]- } deriving (Eq, Show)+ , 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 = fmap (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 = fmap (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++ -- | Raw data format for the header of DNS Query and Response. data DNSHeader = DNSHeader { identifier :: Int@@ -138,22 +176,32 @@ ---------------------------------------------------------------- -- | Raw data format for resource records.-data ResourceRecord = ResourceRecord {+data RR a = ResourceRecord { rrname :: Domain , rrtype :: TYPE , rrttl :: Int , rdlen :: Int- , rdata :: RDATA- } deriving (Eq, Show)+ , rdata :: a+ } deriving (Eq, Show, Functor, Foldable) +type ResourceRecord = RR RDATA+ -- | Raw data format for each type.-data RDATA = RD_NS Domain | RD_CNAME Domain | RD_MX Int Domain | RD_PTR Domain+data RD a = RD_NS Domain | RD_CNAME 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_SRV Int Int Int Domain- | RD_OTH [Int] deriving (Eq)+ | RD_OTH a deriving (Eq, Functor, Foldable) -instance Show RDATA where+type RDATA = RD [Int]++instance Traversable RD where+ sequenceA (RD_OTH a) = fmap RD_OTH a+ sequenceA rd = pure cast+ where+ cast = fmap (error "unhandled case in squenceA (RD)") rd++instance Show a => Show (RD a) 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@@ -164,6 +212,14 @@ 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_OTH is) = show is++instance Traversable RR where+ sequenceA rr = fmap (\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) `fmap` rr ----------------------------------------------------------------
Network/DNS/Lookup.hs view
@@ -177,12 +177,12 @@ -- Examples: -- -- >>> import Data.List (sort)--- >>> let hostname = Data.ByteString.Char8.pack "mixi.jp"+-- >>> let hostname = Data.ByteString.Char8.pack "iij.ad.jp" -- >>> -- >>> rs <- makeResolvSeed defaultResolvConf -- >>> ips <- withResolver rs $ \resolver -> lookupAviaMX resolver hostname -- >>> fmap sort ips--- Right [202.32.29.4,202.32.29.5]+-- Right [202.232.30.70,202.232.30.144] -- -- Since there is more than one result, it is necessary to sort the -- list in order to check for equality.@@ -328,11 +328,11 @@ -- We look up the PTR associated with the IP address -- 210.130.137.80, i.e., 80.137.130.210.in-addr.arpa: ----- >>> let hostname = Data.ByteString.Char8.pack "80.137.130.210.in-addr.arpa"+-- >>> let hostname = Data.ByteString.Char8.pack "164.2.232.202.in-addr.arpa" -- >>> -- >>> rs <- makeResolvSeed defaultResolvConf -- >>> withResolver rs $ \resolver -> lookupPTR resolver hostname--- Right ["www-v4.iij.ad.jp."]+-- Right ["www.iij.ad.jp."] -- -- The 'lookupRDNS' function is more suited to this particular task. --@@ -355,11 +355,11 @@ -- We repeat the example from 'lookupPTR', except now we pass the IP -- address directly: ----- >>> let hostname = Data.ByteString.Char8.pack "210.130.137.80"+-- >>> let hostname = Data.ByteString.Char8.pack "202.232.2.164" -- >>> -- >>> rs <- makeResolvSeed defaultResolvConf -- >>> withResolver rs $ \resolver -> lookupRDNS resolver hostname--- Right ["www-v4.iij.ad.jp."]+-- Right ["www.iij.ad.jp."] -- lookupRDNS :: Resolver -> Domain -> IO (Either DNSError [Domain]) lookupRDNS rlv ip = lookupPTR rlv dom
Network/DNS/Resolver.hs view
@@ -14,6 +14,7 @@ , lookupAuth -- ** Raw looking up function , lookupRaw+ , lookupRaw' , fromDNSFormat ) where @@ -286,9 +287,22 @@ -- @ -- lookupRaw :: Resolver -> Domain -> TYPE -> IO (Either DNSError DNSFormat)-lookupRaw _ dom _+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))+ -> Resolver+ -> Domain+ -> TYPE+ -> IO (Either DNSError (DNSMessage a))+lookupRawInternal _ _ dom _ | isIllegal dom = return $ Left IllegalDomain-lookupRaw rlv dom typ = do+lookupRawInternal rcv rlv dom typ = do seqno <- genId rlv let query = composeQuery seqno [q] checkSeqno = check seqno@@ -301,7 +315,7 @@ return $ Left ret | otherwise = do sendAll sock query- response <- timeout tm (receive sock)+ response <- timeout tm (rcv sock) case response of Nothing -> loop query checkSeqno (cnt + 1) False Just res -> do
Network/DNS/Types.hs view
@@ -4,12 +4,21 @@ module Network.DNS.Types ( -- * Domain Domain- -- * TYPE+ -- * Resource Records+ , ResourceRecord+ , RR (..)+ , RDATA+ , RD (..)+ , rrMapWithType+ -- ** Resource Record Type , TYPE (..), intToType, typeToInt, toType -- * DNS Error , DNSError (..) -- * DNS Format- , DNSFormat (..)+ , DNSFormat+ , DNSMessage (..)+ , dnsMapWithType+ , dnsTraverseWithType -- * DNS Header , DNSHeader (..) -- * DNS Flags@@ -18,9 +27,7 @@ , QorR (..) , OPCODE (..) , RCODE (..)- , ResourceRecord (..) , Question (..)- , RDATA (..) , responseA, responseAAAA ) where
dns.cabal view
@@ -1,5 +1,5 @@ Name: dns-Version: 1.2.3+Version: 1.3.0 Author: Kazu Yamamoto <kazu@iij.ad.jp> Maintainer: Kazu Yamamoto <kazu@iij.ad.jp> License: BSD3@@ -26,12 +26,11 @@ if impl(ghc >= 7) Build-Depends: base >= 4 && < 5 , attoparsec- , attoparsec-conduit , binary , blaze-builder , bytestring , conduit >= 1.1- , conduit-extra+ , conduit-extra >= 1.1 , containers , iproute >= 1.2.4 , mtl@@ -41,12 +40,11 @@ else Build-Depends: base >= 4 && < 5 , attoparsec- , attoparsec-conduit , binary , blaze-builder , bytestring , conduit >= 1.1- , conduit-extra+ , conduit-extra >= 1.1 , containers , iproute >= 1.2.4 , mtl@@ -76,12 +74,11 @@ Other-Modules: EncodeSpec Build-Depends: base , attoparsec- , attoparsec-conduit , binary , blaze-builder , bytestring , conduit >= 1.1- , conduit-extra+ , conduit-extra >= 1.1 , containers , dns , hspec