1 module Network.DNS.Message
66 import Control.Exception
69 import Data.Binary.BitPut as BP
70 import Data.Binary.Get as G
71 import Data.Binary.Put as P'
72 import Data.Binary.Strict.BitGet as BG
73 import qualified Data.ByteString as BS
74 import qualified Data.ByteString.Char8 as C8 hiding (ByteString)
75 import qualified Data.ByteString.Lazy as LBS
77 import qualified Data.IntMap as IM
78 import Data.IntMap (IntMap)
79 import qualified Data.IntSet as IS
80 import Data.IntSet (IntSet)
82 import qualified Data.Map as M
85 import Network.DNS.Packer as P
86 import Network.DNS.Unpacker as U
93 , msgQuestions :: ![SomeQ]
94 , msgAnswers :: ![SomeRR]
95 , msgAuthorities :: ![SomeRR]
96 , msgAdditionals :: ![SomeRR]
102 hdMessageID :: !MessageID
103 , hdMessageType :: !MessageType
104 , hdOpcode :: !Opcode
105 , hdIsAuthoritativeAnswer :: !Bool
106 , hdIsTruncated :: !Bool
107 , hdIsRecursionDesired :: !Bool
108 , hdIsRecursionAvailable :: !Bool
109 , hdResponseCode :: !ResponseCode
111 -- These fields are supressed in this data structure:
119 type MessageID = Word16
129 | ServerStatusRequest
141 data (QueryType qt, QueryClass qc) => Question qt qc
149 instance (QueryType qt, QueryClass qc) => Show (Question qt qc) where
150 show q = "Question { qName = " ++ show (qName q) ++
151 ", qType = " ++ show (qType q) ++
152 ", qClass = " ++ show (qClass q) ++ " }"
154 instance (QueryType qt, QueryClass qc) => Eq (Question qt qc) where
155 a == b = qName a == qName b &&
156 qType a == qType b &&
159 data SomeQ = forall qt qc. (QueryType qt, QueryClass qc) => SomeQ (Question qt qc)
161 instance Show SomeQ where
162 show (SomeQ q) = show q
164 instance Eq SomeQ where
165 (SomeQ a) == (SomeQ b) = Just a == cast b
167 data SomeQT = forall qt. QueryType qt => SomeQT qt
169 instance Show SomeQT where
170 show (SomeQT qt) = show qt
172 instance Eq SomeQT where
173 (SomeQT a) == (SomeQT b) = Just a == cast b
175 data SomeQC = forall qc. QueryClass qc => SomeQC qc
177 instance Show SomeQC where
178 show (SomeQC qc) = show qc
180 instance Eq SomeQC where
181 (SomeQC a) == (SomeQC b) = Just a == cast b
183 putSomeQ :: SomeQ -> Packer CompTable ()
185 = do putDomainName $ qName q
186 putQueryType $ qType q
187 putQueryClass $ qClass q
189 getSomeQ :: Unpacker DecompTable SomeQ
191 = do nm <- getDomainName
195 (SomeQT qt, SomeQC qc)
196 -> return $ SomeQ $ Question {
202 getSomeQT :: Unpacker s SomeQT
203 getSomeQT = do n <- liftM fromIntegral U.getWord16be
204 case IM.lookup n defaultQTTable of
208 -> fail ("Unknown query type: " ++ show n)
210 getSomeQC :: Unpacker s SomeQC
211 getSomeQC = do n <- liftM fromIntegral U.getWord16be
212 case IM.lookup n defaultQCTable of
216 -> fail ("Unknown query class: " ++ show n)
219 newtype DomainName = DN [DomainLabel] deriving (Eq, Show, Ord, Typeable)
220 type DomainLabel = BS.ByteString
222 rootName :: DomainName
223 rootName = DN [BS.empty]
225 isRootName :: DomainName -> Bool
226 isRootName (DN [_]) = True
229 consLabel :: DomainLabel -> DomainName -> DomainName
230 consLabel x (DN ys) = DN (x:ys)
232 unconsLabel :: DomainName -> (DomainLabel, DomainName)
233 unconsLabel (DN (x:xs)) = (x, DN xs)
234 unconsLabel x = error ("Illegal use of unconsLabel: " ++ show x)
236 nameToLabels :: DomainName -> [DomainLabel]
237 nameToLabels (DN xs) = xs
239 isZoneOf :: DomainName -> DomainName -> Bool
240 isZoneOf (DN a) (DN b) = a `isSuffixOf` b
242 mkDomainName :: String -> DomainName
243 mkDomainName = DN . mkLabels [] . notEmpty
245 notEmpty :: String -> String
246 notEmpty xs = assert (not $ null xs) xs
248 mkLabels :: [DomainLabel] -> String -> [DomainLabel]
249 mkLabels soFar [] = reverse (C8.empty : soFar)
250 mkLabels soFar xs = case break (== '.') xs of
252 -> mkLabels (C8.pack l : soFar) rest
253 _ -> error ("Illegal domain name: " ++ xs)
255 mkDN :: String -> DomainName
259 class (Show rc, Eq rc, Typeable rc) => RecordClass rc where
262 putRecordClass :: rc -> Packer s ()
263 putRecordClass = P.putWord16be . fromIntegral . rcToInt
266 data (RecordType rt dt, RecordClass rc) => ResourceRecord rt rc dt
268 rrName :: !DomainName
274 deriving (Show, Eq, Typeable)
277 data SomeRR = forall rt rc dt. (RecordType rt dt, RecordClass rc) => SomeRR (ResourceRecord rt rc dt)
279 instance Show SomeRR where
280 show (SomeRR rr) = show rr
282 instance Eq SomeRR where
283 (SomeRR a) == (SomeRR b) = Just a == cast b
286 putSomeRR :: SomeRR -> Packer CompTable ()
287 putSomeRR (SomeRR rr) = putResourceRecord rr
289 getSomeRR :: Unpacker DecompTable SomeRR
290 getSomeRR = do (srt, src) <- U.lookAhead $
291 do getDomainName -- skip
296 (SomeRT rt, SomeRC rc)
297 -> getResourceRecord rt rc >>= return . SomeRR
299 type CompTable = Map DomainName Int
300 type DecompTable = IntMap DomainName
303 getDomainName :: Unpacker DecompTable DomainName
304 getDomainName = worker
306 worker :: Unpacker DecompTable DomainName
308 = do offset <- U.bytesRead
309 hdr <- getLabelHeader
312 -> do dt <- U.getState
313 case IM.lookup n dt of
317 -> fail ("Illegal offset of label pointer: " ++ show (n, dt))
321 -> do label <- U.getByteString n
323 let name = consLabel label rest
324 U.modifyState $ IM.insert offset name
327 getLabelHeader :: Unpacker s LabelHeader
329 = do header <- U.lookAhead $ U.getByteString 1
334 n <- liftM fromIntegral (getAsWord8 6)
336 ( True, True) -> return $ Offset n
337 (False, False) -> return $ Length n
338 _ -> fail "Illegal label header"
341 -> do header' <- U.getByteString 2 -- Pointers have 2 octets.
343 = runBitGet header' $
345 n <- liftM fromIntegral (getAsWord16 14)
353 getCharString :: Unpacker s BS.ByteString
354 getCharString = do len <- U.getWord8
355 U.getByteString (fromIntegral len)
357 putCharString :: BS.ByteString -> Packer s ()
358 putCharString xs = do P.putWord8 $ fromIntegral $ BS.length xs
365 putDomainName :: DomainName -> Packer CompTable ()
367 = do ct <- P.getState
368 case M.lookup name ct of
370 -> do let ptr = runBitPut $
374 P.putLazyByteString ptr
376 -> do offset <- bytesWrote
377 P.modifyState $ M.insert name offset
379 let (label, rest) = unconsLabel name
383 if isRootName rest then
388 class (Show qt, Eq qt, Typeable qt) => QueryType qt where
391 putQueryType :: qt -> Packer s ()
392 putQueryType = P.putWord16be . fromIntegral . qtToInt
394 instance RecordType rt dt => QueryType rt where
397 class (Show qc, Eq qc, Typeable qc) => QueryClass qc where
400 putQueryClass :: qc -> Packer s ()
401 putQueryClass = P.putWord16be . fromIntegral . qcToInt
403 instance RecordClass rc => QueryClass rc where
407 class (Show rt, Show dt, Eq rt, Eq dt, Typeable rt, Typeable dt) => RecordType rt dt | rt -> dt where
409 putRecordData :: rt -> dt -> Packer CompTable ()
410 getRecordData :: rt -> Unpacker DecompTable dt
412 putRecordType :: rt -> Packer s ()
413 putRecordType = P.putWord16be . fromIntegral . rtToInt
415 putRecordDataWithLength :: rt -> dt -> Packer CompTable ()
416 putRecordDataWithLength rt dt
417 = do -- First, write a dummy data length.
421 -- Second, write data.
424 -- Third, rewrite the dummy length to an actual value.
425 offset' <- bytesWrote
426 let len = offset' - offset - 2
427 if len <= 0xFFFF then
429 $ P.putWord16be $ fromIntegral len
431 fail ("putRecordData " ++ show rt ++ " wrote " ++ show len
432 ++ " bytes, which is way too long")
434 putResourceRecord :: RecordClass rc => ResourceRecord rt rc dt -> Packer CompTable ()
436 = do putDomainName $ rrName rr
437 putRecordType $ rrType rr
438 putRecordClass $ rrClass rr
439 P.putWord32be $ rrTTL rr
440 putRecordDataWithLength (rrType rr) (rrData rr)
442 getRecordDataWithLength :: rt -> Unpacker DecompTable dt
443 getRecordDataWithLength rt
444 = do len <- U.getWord16be
445 offset <- U.bytesRead
446 dat <- getRecordData rt
447 offset' <- U.bytesRead
449 let consumed = offset' - offset
450 when (consumed /= len)
451 $ fail ("getRecordData " ++ show rt ++ " consumed " ++ show consumed ++
452 " bytes but it had to consume " ++ show len ++ " bytes")
456 getResourceRecord :: RecordClass rc => rt -> rc -> Unpacker DecompTable (ResourceRecord rt rc dt)
457 getResourceRecord rt rc
458 = do name <- getDomainName
459 U.skip 2 -- record type
460 U.skip 2 -- record class
462 dat <- getRecordDataWithLength rt
463 return $ ResourceRecord {
472 data SomeRT = forall rt dt. RecordType rt dt => SomeRT rt
474 instance Show SomeRT where
475 show (SomeRT rt) = show rt
477 instance Eq SomeRT where
478 (SomeRT a) == (SomeRT b) = Just a == cast b
480 getSomeRT :: Unpacker s SomeRT
481 getSomeRT = do n <- liftM fromIntegral U.getWord16be
482 case IM.lookup n defaultRTTable of
484 -> fail ("Unknown resource record type: " ++ show n)
488 data SomeRC = forall rc. RecordClass rc => SomeRC rc
490 instance Show SomeRC where
491 show (SomeRC rc) = show rc
493 instance Eq SomeRC where
494 (SomeRC a) == (SomeRC b) = Just a == cast b
496 getSomeRC :: Unpacker s SomeRC
497 getSomeRC = do n <- liftM fromIntegral U.getWord16be
498 case IM.lookup n defaultRCTable of
500 -> fail ("Unknown resource record class: " ++ show n)
507 soaMasterNameServer :: !DomainName
508 , soaResponsibleMailbox :: !DomainName
509 , soaSerialNumber :: !Word32
510 , soaRefreshInterval :: !Word32
511 , soaRetryInterval :: !Word32
512 , soaExpirationLimit :: !Word32
513 , soaMinimumTTL :: !Word32
515 deriving (Show, Eq, Typeable)
519 wksAddress :: !HostAddress
520 , wksProtocol :: !ProtocolNumber
521 , wksServices :: !IntSet
523 deriving (Show, Eq, Typeable)
526 data A = A deriving (Show, Eq, Typeable)
527 instance RecordType A HostAddress where
529 putRecordData _ = P.putWord32be
530 getRecordData _ = U.getWord32be
532 data NS = NS deriving (Show, Eq, Typeable)
533 instance RecordType NS DomainName where
535 putRecordData _ = putDomainName
536 getRecordData _ = getDomainName
538 data MD = MD deriving (Show, Eq, Typeable)
539 instance RecordType MD DomainName where
541 putRecordData _ = putDomainName
542 getRecordData _ = getDomainName
544 data MF = MF deriving (Show, Eq, Typeable)
545 instance RecordType MF DomainName where
547 putRecordData _ = putDomainName
548 getRecordData _ = getDomainName
550 data CNAME = CNAME deriving (Show, Eq, Typeable)
551 instance RecordType CNAME DomainName where
553 putRecordData _ = putDomainName
554 getRecordData _ = getDomainName
556 data SOA = SOA deriving (Show, Eq, Typeable)
557 instance RecordType SOA SOAFields where
559 putRecordData _ = \ soa ->
560 do putDomainName $ soaMasterNameServer soa
561 putDomainName $ soaResponsibleMailbox soa
562 P.putWord32be $ soaSerialNumber soa
563 P.putWord32be $ soaRefreshInterval soa
564 P.putWord32be $ soaRetryInterval soa
565 P.putWord32be $ soaExpirationLimit soa
566 P.putWord32be $ soaMinimumTTL soa
567 getRecordData _ = do master <- getDomainName
568 mail <- getDomainName
569 serial <- U.getWord32be
570 refresh <- U.getWord32be
571 retry <- U.getWord32be
572 expire <- U.getWord32be
575 soaMasterNameServer = master
576 , soaResponsibleMailbox = mail
577 , soaSerialNumber = serial
578 , soaRefreshInterval = refresh
579 , soaRetryInterval = retry
580 , soaExpirationLimit = expire
581 , soaMinimumTTL = ttl
584 data MB = MB deriving (Show, Eq, Typeable)
585 instance RecordType MB DomainName where
587 putRecordData _ = putDomainName
588 getRecordData _ = getDomainName
590 data MG = MG deriving (Show, Eq, Typeable)
591 instance RecordType MG DomainName where
593 putRecordData _ = putDomainName
594 getRecordData _ = getDomainName
596 data MR = MR deriving (Show, Eq, Typeable)
597 instance RecordType MR DomainName where
599 putRecordData _ = putDomainName
600 getRecordData _ = getDomainName
602 data NULL = NULL deriving (Show, Eq, Typeable)
603 instance RecordType NULL BS.ByteString where
605 putRecordData _ _ = fail "putRecordData NULL can't be defined"
606 getRecordData _ = fail "getRecordData NULL can't be defined"
607 putRecordDataWithLength _ = \ dat ->
608 do P.putWord16be $ fromIntegral $ BS.length dat
610 getRecordDataWithLength _ = do len <- U.getWord16be
611 U.getByteString $ fromIntegral len
613 data WKS = WKS deriving (Show, Eq, Typeable)
614 instance RecordType WKS WKSFields where
616 putRecordData _ = \ wks ->
617 do P.putWord32be $ wksAddress wks
618 P.putWord8 $ fromIntegral $ wksProtocol wks
619 P.putLazyByteString $ toBitmap $ wksServices wks
621 toBitmap :: IntSet -> LBS.ByteString
623 = let maxPort = IS.findMax is
624 range = [0 .. maxPort]
625 isAvail p = p `IS.member` is
627 runBitPut $ mapM_ putBit $ map isAvail range
628 getRecordData _ = fail "getRecordData WKS can't be defined"
630 getRecordDataWithLength _
631 = do len <- U.getWord16be
632 addr <- U.getWord32be
633 proto <- liftM fromIntegral U.getWord8
634 bits <- U.getByteString $ fromIntegral $ len - 4 - 1
637 , wksProtocol = proto
638 , wksServices = fromBitmap bits
641 fromBitmap :: BS.ByteString -> IntSet
643 = let Right is = runBitGet bs $ worker 0 IS.empty
647 worker :: Int -> IntSet -> BitGet IntSet
649 = do remain <- BG.remaining
655 worker (pos + 1) (IS.insert pos is)
660 data PTR = PTR deriving (Show, Eq, Typeable)
661 instance RecordType PTR DomainName where
663 putRecordData _ = putDomainName
664 getRecordData _ = getDomainName
666 data HINFO = HINFO deriving (Show, Eq, Typeable)
667 instance RecordType HINFO (BS.ByteString, BS.ByteString) where
669 putRecordData _ = \ (cpu, os) ->
672 getRecordData _ = do cpu <- getCharString
676 data MINFO = MINFO deriving (Show, Eq, Typeable)
677 instance RecordType MINFO (DomainName, DomainName) where
679 putRecordData _ = \ (r, e) ->
682 getRecordData _ = do r <- getDomainName
686 data MX = MX deriving (Show, Eq, Typeable)
687 instance RecordType MX (Word16, DomainName) where
689 putRecordData _ = \ (pref, exch) ->
690 do P.putWord16be pref
692 getRecordData _ = do pref <- U.getWord16be
693 exch <- getDomainName
696 data TXT = TXT deriving (Show, Eq, Typeable)
697 instance RecordType TXT [BS.ByteString] where
699 putRecordData _ = mapM_ putCharString
700 getRecordData _ = fail "getRecordData TXT can't be defined"
702 getRecordDataWithLength _ = U.getWord16be >>= worker [] . fromIntegral
704 worker :: [BS.ByteString] -> Int -> Unpacker s [BS.ByteString]
705 worker soFar 0 = return (reverse soFar)
706 worker soFar n = do str <- getCharString
707 worker (str : soFar) (0 `max` n - 1 - BS.length str)
709 data AXFR = AXFR deriving (Show, Eq, Typeable)
710 instance QueryType AXFR where
713 data MAILB = MAILB deriving (Show, Eq, Typeable)
714 instance QueryType MAILB where
717 data MAILA = MAILA deriving (Show, Eq, Typeable)
718 instance QueryType MAILA where
721 data ANY = ANY deriving (Show, Eq, Typeable)
722 instance QueryType ANY where
724 instance QueryClass ANY where
727 data IN = IN deriving (Show, Eq, Typeable)
728 instance RecordClass IN where
731 data CS = CS deriving (Show, Eq, Typeable)
732 instance RecordClass CS where
735 data CH = CH deriving (Show, Eq, Typeable)
736 instance RecordClass CH where
739 data HS = HS deriving (Show, Eq, Typeable)
740 instance RecordClass HS where
744 instance Binary Message where
745 put m = P.liftToBinary M.empty $
746 do putBinary $ msgHeader m
747 P.putWord16be $ fromIntegral $ length $ msgQuestions m
748 P.putWord16be $ fromIntegral $ length $ msgAnswers m
749 P.putWord16be $ fromIntegral $ length $ msgAuthorities m
750 P.putWord16be $ fromIntegral $ length $ msgAdditionals m
751 mapM_ putSomeQ $ msgQuestions m
752 mapM_ putSomeRR $ msgAnswers m
753 mapM_ putSomeRR $ msgAuthorities m
754 mapM_ putSomeRR $ msgAdditionals m
756 get = U.liftToBinary IM.empty $
758 nQ <- liftM fromIntegral U.getWord16be
759 nAns <- liftM fromIntegral U.getWord16be
760 nAth <- liftM fromIntegral U.getWord16be
761 nAdd <- liftM fromIntegral U.getWord16be
762 qs <- replicateM nQ getSomeQ
763 anss <- replicateM nAns getSomeRR
764 aths <- replicateM nAth getSomeRR
765 adds <- replicateM nAdd getSomeRR
770 , msgAuthorities = aths
771 , msgAdditionals = adds
774 instance Binary Header where
775 put h = do P'.putWord16be $ hdMessageID h
776 P'.putLazyByteString flags
779 do putNBits 1 $ fromEnum $ hdMessageType h
780 putNBits 4 $ fromEnum $ hdOpcode h
781 putBit $ hdIsAuthoritativeAnswer h
782 putBit $ hdIsTruncated h
783 putBit $ hdIsRecursionDesired h
784 putBit $ hdIsRecursionAvailable h
785 putNBits 3 (0 :: Int)
786 putNBits 4 $ fromEnum $ hdResponseCode h
788 get = do mID <- G.getWord16be
789 flags <- G.getByteString 2
792 do qr <- liftM (toEnum . fromIntegral) $ getAsWord8 1
793 op <- liftM (toEnum . fromIntegral) $ getAsWord8 4
799 rc <- liftM (toEnum . fromIntegral) $ getAsWord8 4
804 , hdIsAuthoritativeAnswer = aa
806 , hdIsRecursionDesired = rd
807 , hdIsRecursionAvailable = ra
808 , hdResponseCode = rc
812 instance Enum MessageType where
814 fromEnum Response = 1
820 instance Enum Opcode where
821 fromEnum StandardQuery = 0
822 fromEnum InverseQuery = 1
823 fromEnum ServerStatusRequest = 2
825 toEnum 0 = StandardQuery
826 toEnum 1 = InverseQuery
827 toEnum 2 = ServerStatusRequest
830 instance Enum ResponseCode where
832 fromEnum FormatError = 1
833 fromEnum ServerFailure = 2
834 fromEnum NameError = 3
835 fromEnum NotImplemented = 4
839 toEnum 1 = FormatError
840 toEnum 2 = ServerFailure
842 toEnum 4 = NotImplemented
847 defaultRTTable :: IntMap SomeRT
848 defaultRTTable = IM.fromList $ map toPair $
867 toPair :: SomeRT -> (Int, SomeRT)
868 toPair srt@(SomeRT rt) = (rtToInt rt, srt)
870 defaultQTTable :: IntMap SomeQT
871 defaultQTTable = mergeWithRTTable defaultRTTable $ IM.fromList $ map toPair $
878 toPair :: SomeQT -> (Int, SomeQT)
879 toPair sqt@(SomeQT qt) = (qtToInt qt, sqt)
881 mergeWithRTTable :: IntMap SomeRT -> IntMap SomeQT -> IntMap SomeQT
882 mergeWithRTTable rts qts
883 = IM.union (toQTTable rts) qts
885 toQTTable :: IntMap SomeRT -> IntMap SomeQT
886 toQTTable = IM.map toSomeQT
888 toSomeQT :: SomeRT -> SomeQT
889 toSomeQT (SomeRT rt) = SomeQT rt
891 defaultRCTable :: IntMap SomeRC
892 defaultRCTable = IM.fromList $ map toPair $
899 toPair :: SomeRC -> (Int, SomeRC)
900 toPair src@(SomeRC rc) = (rcToInt rc, src)
902 defaultQCTable :: IntMap SomeQC
903 defaultQCTable = mergeWithRCTable defaultRCTable $ IM.fromList $ map toPair $
907 toPair :: SomeQC -> (Int, SomeQC)
908 toPair sqc@(SomeQC qc) = (qcToInt qc, sqc)
910 mergeWithRCTable :: IntMap SomeRC -> IntMap SomeQC -> IntMap SomeQC
911 mergeWithRCTable rcs qcs
912 = IM.union (toQCTable rcs) qcs
914 toQCTable :: IntMap SomeRC -> IntMap SomeQC
915 toQCTable = IM.map toSomeQC
917 toSomeQC :: SomeRC -> SomeQC
918 toSomeQC (SomeRC rc) = SomeQC rc
921 wrapQuestion :: (QueryType qt, QueryClass qc) => Question qt qc -> SomeQ
924 wrapRecord :: (RecordType rt dt, RecordClass rc) => ResourceRecord rt rc dt -> SomeRR