--- /dev/null
+module Network.DNS.Message
+ ( Header(..)
+ , Opcode(..)
+ , ResponseCode(..)
+ )
+ where
+
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.Bits
+import Data.Word
+
+
+data Header
+ = QueryHeader {
+ hdMessageID :: !Word16
+ , hdOpcode :: !Opcode
+ , hdIsTruncated :: !Bool
+ , hdIsRecursionDesired :: !Bool
+ }
+ | ResponseHeader {
+ hdMessageID :: !Word16
+ , hdOpcode :: !Opcode
+ , hdIsAuthoritativeAnswer :: !Bool
+ , hdIsTruncated :: !Bool
+ , hdIsRecursionDesired :: !Bool
+ , hdIsRecursionAvailable :: !Bool
+ , hdResponseCode :: !ResponseCode
+ }
+
+data Opcode
+ = StandardQuery
+ | InverseQuery
+ | ServerStatusRequest
+
+data ResponseCode
+ = NoError
+ | FormatError
+ | ServerFailure
+ | NameError
+ | NotImplemented
+ | Refused
+ deriving (Show, Eq)
+
+hdIsResponse :: Header -> Bool
+hdIsResponse (QueryHeader _ _ _ _ ) = False
+hdIsResponse (ResponseHeader _ _ _ _ _ _ _) = True
+
+instance Binary Header where
+ put h = do putWord16be $ hdMessageID h
+ let qr = boolToNum $ hdIsResponse h
+ op = fromIntegral $ fromEnum $ hdOpcode h
+ aa = if hdIsResponse h then
+ boolToNum $ hdIsAuthoritativeAnswer h
+ else
+ 0
+ tc = boolToNum $ hdIsTruncated h
+ rd = boolToNum $ hdIsRecursionDesired h
+ ra = if hdIsResponse h then
+ boolToNum $ hdIsRecursionAvailable h
+ else
+ 0
+ rc = if hdIsResponse h then
+ fromIntegral $ fromEnum $ hdResponseCode h
+ else
+ 0
+ flags = ((qr `shiftL` 15) .&. 0x01) .|.
+ ((op `shiftL` 11) .&. 0x0F) .|.
+ ((aa `shiftL` 10) .&. 0x01) .|.
+ ((tc `shiftL` 9) .&. 0x01) .|.
+ ((rd `shiftL` 8) .&. 0x01) .|.
+ ((ra `shiftL` 7) .&. 0x01) .|.
+ ((rc `shiftL` 0) .&. 0x0F)
+ putWord16be flags
+ where
+ boolToNum :: Num a => Bool -> a
+ boolToNum True = 1
+ boolToNum False = 0
+
+ get = do mID <- getWord16be
+ flags <- getWord16be
+ let qr = testBit flags 15
+ op = toEnum $ fromIntegral ((flags `shiftR` 11) .&. 0x0F)
+ aa = testBit flags 10
+ tc = testBit flags 9
+ rd = testBit flags 8
+ ra = testBit flags 7
+ rc = toEnum $ fromIntegral (flags .&. 0x0F)
+ hd = if qr then
+ ResponseHeader {
+ hdMessageID = mID
+ , hdOpcode = op
+ , hdIsAuthoritativeAnswer = aa
+ , hdIsTruncated = tc
+ , hdIsRecursionDesired = rd
+ , hdIsRecursionAvailable = ra
+ , hdResponseCode = rc
+ }
+ else
+ QueryHeader {
+ hdMessageID = mID
+ , hdOpcode = op
+ , hdIsTruncated = tc
+ , hdIsRecursionDesired = rd
+ }
+ return hd
+
+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 Bounded Opcode where
+ minBound = StandardQuery
+ maxBound = ServerStatusRequest
+
+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 Bounded ResponseCode where
+ minBound = NoError
+ maxBound = Refused