module Network.DNS.Message ( Message(..) , MessageType(..) , Header(..) , Opcode(..) , ResponseCode(..) , Question(..) , ResourceRecord(..) , RecordType(..) , RecordClass(..) ) where import Control.Monad import Data.Binary import Data.Binary.BitPut import Data.Binary.Get as G import Data.Binary.Put import Data.Binary.Strict.BitGet as BG import Data.Word data Message = Message { msgHeader :: !Header , msgQuestions :: ![Question] , msgAnswers :: ![ResourceRecord] , msgAuthorities :: ![ResourceRecord] , msgAdditionals :: ![ResourceRecord] } data Header = Header { hdMessageID :: !Word16 , hdMessageType :: !MessageType , hdOpcode :: !Opcode , hdIsAuthoritativeAnswer :: !Bool , hdIsTruncated :: !Bool , hdIsRecursionDesired :: !Bool , hdIsRecursionAvailable :: !Bool , hdResponseCode :: !ResponseCode -- These fields are supressed in this data structure: -- + QDCOUNT -- + ANCOUNT -- + NSCOUNT -- + ARCOUNT } data MessageType = Query | Response deriving (Show, Eq) data Opcode = StandardQuery | InverseQuery | ServerStatusRequest deriving (Show, Eq) data ResponseCode = NoError | FormatError | ServerFailure | NameError | NotImplemented | Refused deriving (Show, Eq) data Question = Question { qName :: !DomainName , qType :: !RecordType , qClass :: !RecordClass } deriving (Show, Eq) type DomainName = [[Word8]] data RecordClass = IN | CS -- Obsolete | CH | HS | AnyClass -- Only for queries deriving (Show, Eq) data ResourceRecord = ResourceRecord { rrName :: !DomainName , rrType :: !RecordType , rrClass :: !RecordClass , rrTTL :: !Word32 , rrData :: ![Word8] } deriving (Show, Eq) data RecordType = A | NS | MD | MF | CNAME | SOA | MB | MG | MR | NULL | WKS | PTR | HINFO | MINFO | MX | TXT -- Only for queries: | AXFR | MAILB -- Obsolete | MAILA -- Obsolete | AnyType deriving (Show, Eq) instance Binary Header where put h = do putWord16be $ hdMessageID h putLazyByteString flags where flags = runBitPut $ do putNBits 1 $ fromEnum $ hdMessageType h putNBits 4 $ fromEnum $ hdOpcode h putBit $ hdIsAuthoritativeAnswer h putBit $ hdIsTruncated h putBit $ hdIsRecursionDesired h putBit $ hdIsRecursionAvailable h putNBits 3 (0 :: Int) putNBits 4 $ fromEnum $ hdResponseCode h get = do mID <- G.getWord16be flags <- getByteString 2 let Right hd = runBitGet flags $ do qr <- liftM (toEnum . fromIntegral) $ getAsWord8 1 op <- liftM (toEnum . fromIntegral) $ getAsWord8 4 aa <- getBit tc <- getBit rd <- getBit ra <- getBit BG.skip 3 rc <- liftM (toEnum . fromIntegral) $ getAsWord8 4 return Header { hdMessageID = mID , hdMessageType = qr , hdOpcode = op , hdIsAuthoritativeAnswer = aa , hdIsTruncated = tc , hdIsRecursionDesired = rd , hdIsRecursionAvailable = ra , hdResponseCode = rc } return hd instance Enum MessageType where fromEnum Query = 0 fromEnum Response = 1 toEnum 0 = Query toEnum 1 = Response toEnum _ = undefined instance Enum Opcode where fromEnum StandardQuery = 0 fromEnum InverseQuery = 1 fromEnum ServerStatusRequest = 2 toEnum 0 = StandardQuery toEnum 1 = InverseQuery toEnum 2 = ServerStatusRequest toEnum _ = undefined instance Enum ResponseCode where fromEnum NoError = 0 fromEnum FormatError = 1 fromEnum ServerFailure = 2 fromEnum NameError = 3 fromEnum NotImplemented = 4 fromEnum Refused = 5 toEnum 0 = NoError toEnum 1 = FormatError toEnum 2 = ServerFailure toEnum 3 = NameError toEnum 4 = NotImplemented toEnum 5 = Refused toEnum _ = undefined instance Enum RecordType where fromEnum A = 1 fromEnum NS = 2 fromEnum MD = 3 fromEnum MF = 4 fromEnum CNAME = 5 fromEnum SOA = 6 fromEnum MB = 7 fromEnum MG = 8 fromEnum MR = 9 fromEnum NULL = 10 fromEnum WKS = 11 fromEnum PTR = 12 fromEnum HINFO = 13 fromEnum MINFO = 14 fromEnum MX = 15 fromEnum TXT = 16 fromEnum AXFR = 252 fromEnum MAILB = 253 fromEnum MAILA = 254 fromEnum AnyType = 255 toEnum 1 = A toEnum 2 = NS toEnum 3 = MD toEnum 4 = MF toEnum 5 = CNAME toEnum 6 = SOA toEnum 7 = MB toEnum 8 = MG toEnum 9 = MR toEnum 10 = NULL toEnum 11 = WKS toEnum 12 = PTR toEnum 13 = HINFO toEnum 14 = MINFO toEnum 15 = MX toEnum 16 = TXT toEnum 252 = AXFR toEnum 253 = MAILB toEnum 254 = MAILA toEnum 255 = AnyType toEnum _ = undefined instance Enum RecordClass where fromEnum IN = 1 fromEnum CS = 2 fromEnum CH = 3 fromEnum HS = 4 fromEnum AnyClass = 255 toEnum 1 = IN toEnum 2 = CS toEnum 3 = CH toEnum 4 = HS toEnum 255 = AnyClass toEnum _ = undefined