1 module Network.DNS.Message
55 import Control.Exception
58 import Data.Binary.BitPut as BP
59 import Data.Binary.Get as G
60 import Data.Binary.Put as P'
61 import Data.Binary.Strict.BitGet as BG
62 import qualified Data.ByteString as BS
63 import qualified Data.ByteString.Char8 as C8 hiding (ByteString)
64 import qualified Data.ByteString.Lazy as LBS
66 import qualified Data.IntMap as IM
67 import Data.IntMap (IntMap)
68 import qualified Data.IntSet as IS
69 import Data.IntSet (IntSet)
70 import qualified Data.Map as M
73 import Network.DNS.Packer as P
74 import Network.DNS.Unpacker as U
81 , msgQuestions :: ![SomeQ]
82 , msgAnswers :: ![SomeRR]
83 , msgAuthorities :: ![SomeRR]
84 , msgAdditionals :: ![SomeRR]
90 hdMessageID :: !MessageID
91 , hdMessageType :: !MessageType
93 , hdIsAuthoritativeAnswer :: !Bool
94 , hdIsTruncated :: !Bool
95 , hdIsRecursionDesired :: !Bool
96 , hdIsRecursionAvailable :: !Bool
97 , hdResponseCode :: !ResponseCode
99 -- These fields are supressed in this data structure:
107 type MessageID = Word16
117 | ServerStatusRequest
129 data (QueryType qt, QueryClass qc) => Question qt qc
137 instance (QueryType qt, QueryClass qc) => Show (Question qt qc) where
138 show q = "Question { qName = " ++ show (qName q) ++
139 ", qType = " ++ show (qType q) ++
140 ", qClass = " ++ show (qClass q) ++ " }"
142 instance (QueryType qt, QueryClass qc) => Eq (Question qt qc) where
143 a == b = qName a == qName b &&
144 qType a == qType b &&
147 data SomeQ = forall qt qc. (QueryType qt, QueryClass qc) => SomeQ (Question qt qc)
149 instance Show SomeQ where
150 show (SomeQ q) = show q
152 instance Eq SomeQ where
153 (SomeQ a) == (SomeQ b) = Just a == cast b
155 data SomeQT = forall qt. QueryType qt => SomeQT qt
157 instance Show SomeQT where
158 show (SomeQT qt) = show qt
160 instance Eq SomeQT where
161 (SomeQT a) == (SomeQT b) = Just a == cast b
163 data SomeQC = forall qc. QueryClass qc => SomeQC qc
165 instance Show SomeQC where
166 show (SomeQC qc) = show qc
168 instance Eq SomeQC where
169 (SomeQC a) == (SomeQC b) = Just a == cast b
171 putSomeQ :: SomeQ -> Packer CompTable ()
173 = do putDomainName $ qName q
174 putQueryType $ qType q
175 putQueryClass $ qClass q
177 getSomeQ :: Unpacker DecompTable SomeQ
179 = do nm <- getDomainName
183 (SomeQT qt, SomeQC qc)
184 -> return $ SomeQ $ Question {
190 getSomeQT :: Unpacker s SomeQT
191 getSomeQT = do n <- liftM fromIntegral U.getWord16be
192 case IM.lookup n defaultQTTable of
196 -> fail ("Unknown query type: " ++ show n)
198 getSomeQC :: Unpacker s SomeQC
199 getSomeQC = do n <- liftM fromIntegral U.getWord16be
200 case IM.lookup n defaultQCTable of
204 -> fail ("Unknown query class: " ++ show n)
207 newtype DomainName = DN [DomainLabel] deriving (Eq, Show, Ord, Typeable)
208 type DomainLabel = BS.ByteString
210 rootName :: DomainName
211 rootName = DN [BS.empty]
213 isRootName :: DomainName -> Bool
214 isRootName (DN [_]) = True
217 consLabel :: DomainLabel -> DomainName -> DomainName
218 consLabel x (DN ys) = DN (x:ys)
220 unconsLabel :: DomainName -> (DomainLabel, DomainName)
221 unconsLabel (DN (x:xs)) = (x, DN xs)
222 unconsLabel x = error ("Illegal use of unconsLabel: " ++ show x)
224 mkDomainName :: String -> DomainName
225 mkDomainName = DN . mkLabels [] . notEmpty
227 notEmpty :: String -> String
228 notEmpty xs = assert (not $ null xs) xs
230 mkLabels :: [DomainLabel] -> String -> [DomainLabel]
231 mkLabels soFar [] = reverse (C8.empty : soFar)
232 mkLabels soFar xs = case break (== '.') xs of
234 -> mkLabels (C8.pack l : soFar) rest
235 _ -> error ("Illegal domain name: " ++ xs)
238 class (Show rc, Eq rc, Typeable rc) => RecordClass rc where
241 putRecordClass :: rc -> Packer s ()
242 putRecordClass = P.putWord16be . fromIntegral . rcToInt
245 data (RecordType rt dt, RecordClass rc) => ResourceRecord rt rc dt
247 rrName :: !DomainName
253 deriving (Show, Eq, Typeable)
256 data SomeRR = forall rt rc dt. (RecordType rt dt, RecordClass rc) => SomeRR (ResourceRecord rt rc dt)
258 instance Show SomeRR where
259 show (SomeRR rr) = show rr
261 instance Eq SomeRR where
262 (SomeRR a) == (SomeRR b) = Just a == cast b
265 putSomeRR :: SomeRR -> Packer CompTable ()
266 putSomeRR (SomeRR rr) = putResourceRecord rr
268 getSomeRR :: Unpacker DecompTable SomeRR
269 getSomeRR = do (srt, src) <- U.lookAhead $
270 do getDomainName -- skip
275 (SomeRT rt, SomeRC rc)
276 -> getResourceRecord rt rc >>= return . SomeRR
278 type CompTable = Map DomainName Int
279 type DecompTable = IntMap DomainName
282 getDomainName :: Unpacker DecompTable DomainName
283 getDomainName = worker
285 worker :: Unpacker DecompTable DomainName
287 = do offset <- U.bytesRead
288 hdr <- getLabelHeader
291 -> do dt <- U.getState
292 case IM.lookup n dt of
296 -> fail ("Illegal offset of label pointer: " ++ show (n, dt))
300 -> do label <- U.getByteString n
302 let name = consLabel label rest
303 U.modifyState $ IM.insert offset name
306 getLabelHeader :: Unpacker s LabelHeader
308 = do header <- U.lookAhead $ U.getByteString 1
313 n <- liftM fromIntegral (getAsWord8 6)
315 ( True, True) -> return $ Offset n
316 (False, False) -> return $ Length n
317 _ -> fail "Illegal label header"
320 -> do header' <- U.getByteString 2 -- Pointers have 2 octets.
322 = runBitGet header' $
324 n <- liftM fromIntegral (getAsWord16 14)
332 getCharString :: Unpacker s BS.ByteString
333 getCharString = do len <- U.getWord8
334 U.getByteString (fromIntegral len)
336 putCharString :: BS.ByteString -> Packer s ()
337 putCharString xs = do P.putWord8 $ fromIntegral $ BS.length xs
344 putDomainName :: DomainName -> Packer CompTable ()
346 = do ct <- P.getState
347 case M.lookup name ct of
349 -> do let ptr = runBitPut $
353 P.putLazyByteString ptr
355 -> do offset <- bytesWrote
356 P.modifyState $ M.insert name offset
358 let (label, rest) = unconsLabel name
362 if isRootName rest then
367 class (Show qt, Eq qt, Typeable qt) => QueryType qt where
370 putQueryType :: qt -> Packer s ()
371 putQueryType = P.putWord16be . fromIntegral . qtToInt
373 instance RecordType rt dt => QueryType rt where
376 class (Show qc, Eq qc, Typeable qc) => QueryClass qc where
379 putQueryClass :: qc -> Packer s ()
380 putQueryClass = P.putWord16be . fromIntegral . qcToInt
382 instance RecordClass rc => QueryClass rc where
386 class (Show rt, Show dt, Eq rt, Eq dt, Typeable rt, Typeable dt) => RecordType rt dt | rt -> dt where
388 putRecordData :: rt -> dt -> Packer CompTable ()
389 getRecordData :: rt -> Unpacker DecompTable dt
391 putRecordType :: rt -> Packer s ()
392 putRecordType = P.putWord16be . fromIntegral . rtToInt
394 putRecordDataWithLength :: rt -> dt -> Packer CompTable ()
395 putRecordDataWithLength rt dt
396 = do -- First, write a dummy data length.
400 -- Second, write data.
403 -- Third, rewrite the dummy length to an actual value.
404 offset' <- bytesWrote
405 let len = offset' - offset - 2
406 if len <= 0xFFFF then
408 $ P.putWord16be $ fromIntegral len
410 fail ("putRecordData " ++ show rt ++ " wrote " ++ show len
411 ++ " bytes, which is way too long")
413 putResourceRecord :: RecordClass rc => ResourceRecord rt rc dt -> Packer CompTable ()
415 = do putDomainName $ rrName rr
416 putRecordType $ rrType rr
417 putRecordClass $ rrClass rr
418 P.putWord32be $ rrTTL rr
419 putRecordDataWithLength (rrType rr) (rrData rr)
421 getRecordDataWithLength :: rt -> Unpacker DecompTable dt
422 getRecordDataWithLength rt
423 = do len <- U.getWord16be
424 offset <- U.bytesRead
425 dat <- getRecordData rt
426 offset' <- U.bytesRead
428 let consumed = offset' - offset
429 when (consumed /= len)
430 $ fail ("getRecordData " ++ show rt ++ " consumed " ++ show consumed ++
431 " bytes but it had to consume " ++ show len ++ " bytes")
435 getResourceRecord :: RecordClass rc => rt -> rc -> Unpacker DecompTable (ResourceRecord rt rc dt)
436 getResourceRecord rt rc
437 = do name <- getDomainName
438 U.skip 2 -- record type
439 U.skip 2 -- record class
441 dat <- getRecordDataWithLength rt
442 return $ ResourceRecord {
451 data SomeRT = forall rt dt. RecordType rt dt => SomeRT rt
453 instance Show SomeRT where
454 show (SomeRT rt) = show rt
456 instance Eq SomeRT where
457 (SomeRT a) == (SomeRT b) = Just a == cast b
459 getSomeRT :: Unpacker s SomeRT
460 getSomeRT = do n <- liftM fromIntegral U.getWord16be
461 case IM.lookup n defaultRTTable of
463 -> fail ("Unknown resource record type: " ++ show n)
467 data SomeRC = forall rc. RecordClass rc => SomeRC rc
469 instance Show SomeRC where
470 show (SomeRC rc) = show rc
472 instance Eq SomeRC where
473 (SomeRC a) == (SomeRC b) = Just a == cast b
475 getSomeRC :: Unpacker s SomeRC
476 getSomeRC = do n <- liftM fromIntegral U.getWord16be
477 case IM.lookup n defaultRCTable of
479 -> fail ("Unknown resource record class: " ++ show n)
486 soaMasterNameServer :: !DomainName
487 , soaResponsibleMailbox :: !DomainName
488 , soaSerialNumber :: !Word32
489 , soaRefreshInterval :: !Word32
490 , soaRetryInterval :: !Word32
491 , soaExpirationLimit :: !Word32
492 , soaMinimumTTL :: !Word32
494 deriving (Show, Eq, Typeable)
498 wksAddress :: !HostAddress
499 , wksProtocol :: !ProtocolNumber
500 , wksServices :: !IntSet
502 deriving (Show, Eq, Typeable)
505 data A = A deriving (Show, Eq, Typeable)
506 instance RecordType A HostAddress where
508 putRecordData _ = P.putWord32be
509 getRecordData _ = U.getWord32be
511 data NS = NS deriving (Show, Eq, Typeable)
512 instance RecordType NS DomainName where
514 putRecordData _ = putDomainName
515 getRecordData _ = getDomainName
517 data MD = MD deriving (Show, Eq, Typeable)
518 instance RecordType MD DomainName where
520 putRecordData _ = putDomainName
521 getRecordData _ = getDomainName
523 data MF = MF deriving (Show, Eq, Typeable)
524 instance RecordType MF DomainName where
526 putRecordData _ = putDomainName
527 getRecordData _ = getDomainName
529 data CNAME = CNAME deriving (Show, Eq, Typeable)
530 instance RecordType CNAME DomainName where
532 putRecordData _ = putDomainName
533 getRecordData _ = getDomainName
535 data SOA = SOA deriving (Show, Eq, Typeable)
536 instance RecordType SOA SOAFields where
538 putRecordData _ = \ soa ->
539 do putDomainName $ soaMasterNameServer soa
540 putDomainName $ soaResponsibleMailbox soa
541 P.putWord32be $ soaSerialNumber soa
542 P.putWord32be $ soaRefreshInterval soa
543 P.putWord32be $ soaRetryInterval soa
544 P.putWord32be $ soaExpirationLimit soa
545 P.putWord32be $ soaMinimumTTL soa
546 getRecordData _ = do master <- getDomainName
547 mail <- getDomainName
548 serial <- U.getWord32be
549 refresh <- U.getWord32be
550 retry <- U.getWord32be
551 expire <- U.getWord32be
554 soaMasterNameServer = master
555 , soaResponsibleMailbox = mail
556 , soaSerialNumber = serial
557 , soaRefreshInterval = refresh
558 , soaRetryInterval = retry
559 , soaExpirationLimit = expire
560 , soaMinimumTTL = ttl
563 data MB = MB deriving (Show, Eq, Typeable)
564 instance RecordType MB DomainName where
566 putRecordData _ = putDomainName
567 getRecordData _ = getDomainName
569 data MG = MG deriving (Show, Eq, Typeable)
570 instance RecordType MG DomainName where
572 putRecordData _ = putDomainName
573 getRecordData _ = getDomainName
575 data MR = MR deriving (Show, Eq, Typeable)
576 instance RecordType MR DomainName where
578 putRecordData _ = putDomainName
579 getRecordData _ = getDomainName
581 data NULL = NULL deriving (Show, Eq, Typeable)
582 instance RecordType NULL BS.ByteString where
584 putRecordData _ _ = fail "putRecordData NULL can't be defined"
585 getRecordData _ = fail "getRecordData NULL can't be defined"
586 putRecordDataWithLength _ = \ dat ->
587 do P.putWord16be $ fromIntegral $ BS.length dat
589 getRecordDataWithLength _ = do len <- U.getWord16be
590 U.getByteString $ fromIntegral len
592 data WKS = WKS deriving (Show, Eq, Typeable)
593 instance RecordType WKS WKSFields where
595 putRecordData _ = \ wks ->
596 do P.putWord32be $ wksAddress wks
597 P.putWord8 $ fromIntegral $ wksProtocol wks
598 P.putLazyByteString $ toBitmap $ wksServices wks
600 toBitmap :: IntSet -> LBS.ByteString
602 = let maxPort = IS.findMax is
603 range = [0 .. maxPort]
604 isAvail p = p `IS.member` is
606 runBitPut $ mapM_ putBit $ map isAvail range
607 getRecordData _ = fail "getRecordData WKS can't be defined"
609 getRecordDataWithLength _
610 = do len <- U.getWord16be
611 addr <- U.getWord32be
612 proto <- liftM fromIntegral U.getWord8
613 bits <- U.getByteString $ fromIntegral $ len - 4 - 1
616 , wksProtocol = proto
617 , wksServices = fromBitmap bits
620 fromBitmap :: BS.ByteString -> IntSet
622 = let Right is = runBitGet bs $ worker 0 IS.empty
626 worker :: Int -> IntSet -> BitGet IntSet
628 = do remain <- BG.remaining
634 worker (pos + 1) (IS.insert pos is)
639 data PTR = PTR deriving (Show, Eq, Typeable)
640 instance RecordType PTR DomainName where
642 putRecordData _ = putDomainName
643 getRecordData _ = getDomainName
645 data HINFO = HINFO deriving (Show, Eq, Typeable)
646 instance RecordType HINFO (BS.ByteString, BS.ByteString) where
648 putRecordData _ = \ (cpu, os) ->
651 getRecordData _ = do cpu <- getCharString
655 data MINFO = MINFO deriving (Show, Eq, Typeable)
656 instance RecordType MINFO (DomainName, DomainName) where
658 putRecordData _ = \ (r, e) ->
661 getRecordData _ = do r <- getDomainName
665 data MX = MX deriving (Show, Eq, Typeable)
666 instance RecordType MX (Word16, DomainName) where
668 putRecordData _ = \ (pref, exch) ->
669 do P.putWord16be pref
671 getRecordData _ = do pref <- U.getWord16be
672 exch <- getDomainName
675 data TXT = TXT deriving (Show, Eq, Typeable)
676 instance RecordType TXT [BS.ByteString] where
678 putRecordData _ = mapM_ putCharString
679 getRecordData _ = fail "getRecordData TXT can't be defined"
681 getRecordDataWithLength _ = U.getWord16be >>= worker [] . fromIntegral
683 worker :: [BS.ByteString] -> Int -> Unpacker s [BS.ByteString]
684 worker soFar 0 = return (reverse soFar)
685 worker soFar n = do str <- getCharString
686 worker (str : soFar) (0 `max` n - 1 - BS.length str)
688 data AXFR = AXFR deriving (Show, Eq, Typeable)
689 instance QueryType AXFR where
692 data MAILB = MAILB deriving (Show, Eq, Typeable)
693 instance QueryType MAILB where
696 data MAILA = MAILA deriving (Show, Eq, Typeable)
697 instance QueryType MAILA where
700 data ANY = ANY deriving (Show, Eq, Typeable)
701 instance QueryType ANY where
703 instance QueryClass ANY where
706 data IN = IN deriving (Show, Eq, Typeable)
707 instance RecordClass IN where
710 data CS = CS deriving (Show, Eq, Typeable)
711 instance RecordClass CS where
714 data CH = CH deriving (Show, Eq, Typeable)
715 instance RecordClass CH where
718 data HS = HS deriving (Show, Eq, Typeable)
719 instance RecordClass HS where
723 instance Binary Message where
724 put m = P.liftToBinary M.empty $
725 do putBinary $ msgHeader m
726 P.putWord16be $ fromIntegral $ length $ msgQuestions m
727 P.putWord16be $ fromIntegral $ length $ msgAnswers m
728 P.putWord16be $ fromIntegral $ length $ msgAuthorities m
729 P.putWord16be $ fromIntegral $ length $ msgAdditionals m
730 mapM_ putSomeQ $ msgQuestions m
731 mapM_ putSomeRR $ msgAnswers m
732 mapM_ putSomeRR $ msgAuthorities m
733 mapM_ putSomeRR $ msgAdditionals m
735 get = U.liftToBinary IM.empty $
737 nQ <- liftM fromIntegral U.getWord16be
738 nAns <- liftM fromIntegral U.getWord16be
739 nAth <- liftM fromIntegral U.getWord16be
740 nAdd <- liftM fromIntegral U.getWord16be
741 qs <- replicateM nQ getSomeQ
742 anss <- replicateM nAns getSomeRR
743 aths <- replicateM nAth getSomeRR
744 adds <- replicateM nAdd getSomeRR
749 , msgAuthorities = aths
750 , msgAdditionals = adds
753 instance Binary Header where
754 put h = do P'.putWord16be $ hdMessageID h
755 P'.putLazyByteString flags
758 do putNBits 1 $ fromEnum $ hdMessageType h
759 putNBits 4 $ fromEnum $ hdOpcode h
760 putBit $ hdIsAuthoritativeAnswer h
761 putBit $ hdIsTruncated h
762 putBit $ hdIsRecursionDesired h
763 putBit $ hdIsRecursionAvailable h
764 putNBits 3 (0 :: Int)
765 putNBits 4 $ fromEnum $ hdResponseCode h
767 get = do mID <- G.getWord16be
768 flags <- G.getByteString 2
771 do qr <- liftM (toEnum . fromIntegral) $ getAsWord8 1
772 op <- liftM (toEnum . fromIntegral) $ getAsWord8 4
778 rc <- liftM (toEnum . fromIntegral) $ getAsWord8 4
783 , hdIsAuthoritativeAnswer = aa
785 , hdIsRecursionDesired = rd
786 , hdIsRecursionAvailable = ra
787 , hdResponseCode = rc
791 instance Enum MessageType where
793 fromEnum Response = 1
799 instance Enum Opcode where
800 fromEnum StandardQuery = 0
801 fromEnum InverseQuery = 1
802 fromEnum ServerStatusRequest = 2
804 toEnum 0 = StandardQuery
805 toEnum 1 = InverseQuery
806 toEnum 2 = ServerStatusRequest
809 instance Enum ResponseCode where
811 fromEnum FormatError = 1
812 fromEnum ServerFailure = 2
813 fromEnum NameError = 3
814 fromEnum NotImplemented = 4
818 toEnum 1 = FormatError
819 toEnum 2 = ServerFailure
821 toEnum 4 = NotImplemented
826 defaultRTTable :: IntMap SomeRT
827 defaultRTTable = IM.fromList $ map toPair $
846 toPair :: SomeRT -> (Int, SomeRT)
847 toPair srt@(SomeRT rt) = (rtToInt rt, srt)
849 defaultQTTable :: IntMap SomeQT
850 defaultQTTable = mergeWithRTTable defaultRTTable $ IM.fromList $ map toPair $
857 toPair :: SomeQT -> (Int, SomeQT)
858 toPair sqt@(SomeQT qt) = (qtToInt qt, sqt)
860 mergeWithRTTable :: IntMap SomeRT -> IntMap SomeQT -> IntMap SomeQT
861 mergeWithRTTable rts qts
862 = IM.union (toQTTable rts) qts
864 toQTTable :: IntMap SomeRT -> IntMap SomeQT
865 toQTTable = IM.map toSomeQT
867 toSomeQT :: SomeRT -> SomeQT
868 toSomeQT (SomeRT rt) = SomeQT rt
870 defaultRCTable :: IntMap SomeRC
871 defaultRCTable = IM.fromList $ map toPair $
878 toPair :: SomeRC -> (Int, SomeRC)
879 toPair src@(SomeRC rc) = (rcToInt rc, src)
881 defaultQCTable :: IntMap SomeQC
882 defaultQCTable = mergeWithRCTable defaultRCTable $ IM.fromList $ map toPair $
886 toPair :: SomeQC -> (Int, SomeQC)
887 toPair sqc@(SomeQC qc) = (qcToInt qc, sqc)
889 mergeWithRCTable :: IntMap SomeRC -> IntMap SomeQC -> IntMap SomeQC
890 mergeWithRCTable rcs qcs
891 = IM.union (toQCTable rcs) qcs
893 toQCTable :: IntMap SomeRC -> IntMap SomeQC
894 toQCTable = IM.map toSomeQC
896 toSomeQC :: SomeRC -> SomeQC
897 toSomeQC (SomeRC rc) = SomeQC rc
900 wrapQuestion :: (QueryType qt, QueryClass qc) => Question qt qc -> SomeQ
903 wrapRecord :: (RecordType rt dt, RecordClass rc) => ResourceRecord rt rc dt -> SomeRR