1 module Network.DNS.Message
24 import Data.Binary.BitPut as BP
25 import Data.Binary.Get as G
26 import Data.Binary.Put as P
27 import Data.Binary.Strict.BitGet as BG
28 import qualified Data.ByteString as BS
29 import qualified Data.ByteString.Lazy as LBS
31 import qualified Data.IntMap as IM
32 import Data.IntMap (IntMap)
36 replicateM' :: Monad m => Int -> (a -> m (b, a)) -> a -> m ([b], a)
37 replicateM' = worker []
39 worker :: Monad m => [b] -> Int -> (a -> m (b, a)) -> a -> m ([b], a)
40 worker soFar 0 _ a = return (reverse soFar, a)
41 worker soFar n f a = do (b, a') <- f a
42 worker (b : soFar) (n - 1) f a'
48 , msgQuestions :: ![Question]
49 , msgAnswers :: ![SomeRR]
50 , msgAuthorities :: ![SomeRR]
51 , msgAdditionals :: ![SomeRR]
56 hdMessageID :: !MessageID
57 , hdMessageType :: !MessageType
59 , hdIsAuthoritativeAnswer :: !Bool
60 , hdIsTruncated :: !Bool
61 , hdIsRecursionDesired :: !Bool
62 , hdIsRecursionAvailable :: !Bool
63 , hdResponseCode :: !ResponseCode
65 -- These fields are supressed in this data structure:
72 type MessageID = Word16
97 , qType :: !RecordType
98 , qClass :: !RecordClass
102 putQ :: Question -> Put
104 = do putDomainName $ qName q
108 getQ :: DecompTable -> Get (Question, DecompTable)
110 = do (nm, dt') <- getDomainName dt
120 type DomainName = [DomainLabel]
121 type DomainLabel = BS.ByteString
128 | AnyClass -- Only for queries
131 class (Typeable rr, Show rr, Eq rr) => ResourceRecord rr where
132 rrName :: rr -> DomainName
133 rrType :: rr -> RecordType
134 rrClass :: rr -> RecordClass
136 rrPutData :: rr -> Put
137 rrGetData :: DecompTable -> DomainName -> RecordClass -> TTL -> Get (rr, DecompTable)
139 fromRR :: SomeRR -> Maybe rr
142 fromRR (SomeRR rr') = cast rr'
144 putRR :: ResourceRecord rr => rr -> Put
145 putRR rr = do putDomainName $ rrName rr
148 putWord32be $ rrTTL rr
150 let dat = runPut $ rrPutData rr
151 putWord16be $ fromIntegral $ LBS.length dat
152 putLazyByteString dat
154 getRR :: DecompTable -> Get (SomeRR, DecompTable)
156 = do (nm, dt') <- getDomainName dt
161 CNAME -> do (rr, dt'') <- rrGetData dt' nm cl ttl
162 return (toRR (rr :: CNAME), dt'')
163 HINFO -> do (rr, dt'') <- rrGetData dt' nm cl ttl
164 return (toRR (rr :: HINFO), dt'')
165 AXFR -> onlyForQuestions "AXFR"
166 MAILB -> onlyForQuestions "MAILB"
167 MAILA -> onlyForQuestions "MAILA"
168 AnyType -> onlyForQuestions "ANY"
170 onlyForQuestions name
171 = fail (name ++ " is only for questions, not an actual resource record.")
173 data SomeRR = forall rr. ResourceRecord rr => SomeRR rr
175 instance ResourceRecord SomeRR where
176 rrName (SomeRR rr) = rrName rr
177 rrType (SomeRR rr) = rrType rr
178 rrClass (SomeRR rr) = rrClass rr
179 rrTTL (SomeRR rr) = rrTTL rr
180 rrPutData (SomeRR rr) = rrPutData rr
181 rrGetData _ _ _ _ = fail "SomeRR can't directly be constructed."
184 instance Eq SomeRR where
185 (SomeRR a) == (SomeRR b) = Just a == cast b
186 instance Show SomeRR where
187 show (SomeRR rr) = show rr
189 type DecompTable = IntMap BS.ByteString
192 data CNAME = CNAME' !DomainName !RecordClass !TTL !DomainName
193 deriving (Eq, Show, Typeable)
194 instance ResourceRecord CNAME where
195 rrName (CNAME' n _ _ _) = n
197 rrClass (CNAME' _ c _ _) = c
198 rrTTL (CNAME' _ _ t _) = t
199 rrGetData dt n c t = do (d, dt') <- getDomainName dt
200 return (CNAME' n c t d, dt')
201 rrPutData (CNAME' _ _ _ d) = putDomainName d
203 data HINFO = HINFO' !DomainName !RecordClass !TTL !BS.ByteString !BS.ByteString
204 deriving (Eq, Show, Typeable)
205 instance ResourceRecord HINFO where
206 rrName (HINFO' n _ _ _ _) = n
208 rrClass (HINFO' _ c _ _ _) = c
209 rrTTL (HINFO' _ _ t _ _) = t
210 rrGetData dt n c t = do cpu <- getCharString
212 return (HINFO' n c t cpu os, dt)
213 rrPutData (HINFO' _ _ _ c o) = do putCharString c
216 getDomainName :: DecompTable -> Get (DomainName, DecompTable)
217 getDomainName = flip worker []
219 worker :: DecompTable -> [DomainLabel] -> Get ([DomainLabel], DecompTable)
221 = do (l, dt') <- getDomainLabel dt
223 True -> return (reverse (l : soFar), dt')
224 False -> worker dt' (l : soFar)
226 getDomainLabel :: DecompTable -> Get (DomainLabel, DecompTable)
228 = do header <- getByteString 1
233 n <- liftM fromIntegral (getAsWord8 6)
235 ( True, True) -> return $ Offset n
236 (False, False) -> return $ Length n
237 _ -> fail "Illegal label header"
240 -> do let Just l = IM.lookup n dt
243 -> do offset <- liftM fromIntegral bytesRead
244 label <- getByteString n
245 let dt' = IM.insert offset label dt
248 getCharString :: Get BS.ByteString
249 getCharString = do len <- G.getWord8
250 getByteString (fromIntegral len)
252 putCharString :: BS.ByteString -> Put
253 putCharString = putDomainLabel
259 putDomainName :: DomainName -> Put
260 putDomainName = mapM_ putDomainLabel
262 putDomainLabel :: DomainLabel -> Put
264 = do putWord8 $ fromIntegral $ BS.length l
292 instance Binary Message where
293 put m = do put $ msgHeader m
294 putWord16be $ fromIntegral $ length $ msgQuestions m
295 putWord16be $ fromIntegral $ length $ msgAnswers m
296 putWord16be $ fromIntegral $ length $ msgAuthorities m
297 putWord16be $ fromIntegral $ length $ msgAdditionals m
298 mapM_ putQ $ msgQuestions m
299 mapM_ putRR $ msgAnswers m
300 mapM_ putRR $ msgAuthorities m
301 mapM_ putRR $ msgAdditionals m
304 nQ <- liftM fromIntegral G.getWord16be
305 nAns <- liftM fromIntegral G.getWord16be
306 nAth <- liftM fromIntegral G.getWord16be
307 nAdd <- liftM fromIntegral G.getWord16be
308 (qs , dt1) <- replicateM' nQ getQ IM.empty
309 (anss, dt2) <- replicateM' nAns getRR dt1
310 (aths, dt3) <- replicateM' nAth getRR dt2
311 (adds, _ ) <- replicateM' nAdd getRR dt3
316 , msgAuthorities = aths
317 , msgAdditionals = adds
320 instance Binary Header where
321 put h = do putWord16be $ hdMessageID h
322 putLazyByteString flags
325 do putNBits 1 $ fromEnum $ hdMessageType h
326 putNBits 4 $ fromEnum $ hdOpcode h
327 putBit $ hdIsAuthoritativeAnswer h
328 putBit $ hdIsTruncated h
329 putBit $ hdIsRecursionDesired h
330 putBit $ hdIsRecursionAvailable h
331 putNBits 3 (0 :: Int)
332 putNBits 4 $ fromEnum $ hdResponseCode h
334 get = do mID <- G.getWord16be
335 flags <- getByteString 2
338 do qr <- liftM (toEnum . fromIntegral) $ getAsWord8 1
339 op <- liftM (toEnum . fromIntegral) $ getAsWord8 4
345 rc <- liftM (toEnum . fromIntegral) $ getAsWord8 4
350 , hdIsAuthoritativeAnswer = aa
352 , hdIsRecursionDesired = rd
353 , hdIsRecursionAvailable = ra
354 , hdResponseCode = rc
358 instance Enum MessageType where
360 fromEnum Response = 1
366 instance Enum Opcode where
367 fromEnum StandardQuery = 0
368 fromEnum InverseQuery = 1
369 fromEnum ServerStatusRequest = 2
371 toEnum 0 = StandardQuery
372 toEnum 1 = InverseQuery
373 toEnum 2 = ServerStatusRequest
376 instance Enum ResponseCode where
378 fromEnum FormatError = 1
379 fromEnum ServerFailure = 2
380 fromEnum NameError = 3
381 fromEnum NotImplemented = 4
385 toEnum 1 = FormatError
386 toEnum 2 = ServerFailure
388 toEnum 4 = NotImplemented
392 instance Enum RecordType where
412 fromEnum AnyType = 255
436 instance Enum RecordClass where
441 fromEnum AnyClass = 255
447 toEnum 255 = AnyClass
450 instance Binary RecordType where
451 get = liftM (toEnum . fromIntegral) G.getWord16be
452 put = putWord16be . fromIntegral . fromEnum
454 instance Binary RecordClass where
455 get = liftM (toEnum . fromIntegral) G.getWord16be
456 put = putWord16be . fromIntegral . fromEnum