Commit 9b18586538ec6fcc682a7fb7737b03d6baec2831

  • avatar
  • Peter Simons (Committer)
  • Fri Feb 26 00:00:00 CET 2010
  • avatar
  • Lutz Donnerhacke <lutz @iks-…na.de> (Author)
  • Fri Jan 11 10:55:02 CET 2008
Allow retrieving generic RRTypes.
Add support for CNAME queries.
Canonify RRType before parsing responses.
Added NSEC processing and zone walking example.
  
2222import Control.Exception ( assert, bracket )
2323import Network ( HostName )
2424import Network.Socket ( HostAddress )
25import Data.Bits ()
2526import Foreign
2627import Foreign.C
2728import ADNS.Endian
111111
112112-- |The record types we support.
113113
114data RRType = A | MX | NS | PTR
115 deriving (Eq, Bounded, Show)
114data RRType = A | CNAME | MX | NS | PTR
115 | NSEC
116 | RRType Int
117 deriving (Read)
116118
119instance Eq RRType where
120 a == b = fromEnum a == fromEnum b
121
122instance Show RRType where
123 showsPrec _ x = case toEnum $ fromEnum x of -- canonify
124 A -> showString "A"
125 CNAME -> showString "CNAME"
126 MX -> showString "MX"
127 NS -> showString "NS"
128 PTR -> showString "PTR"
129 NSEC -> showString "NSEC"
130 (RRType i) -> showString "TYPE" . shows i
131
117132instance Enum RRType where
118133 toEnum #{const adns_r_a} = A
134 toEnum #{const adns_r_cname} = CNAME
119135 toEnum #{const adns_r_mx} = MX
120136 toEnum #{const adns_r_ns} = NS
121137 toEnum #{const adns_r_ptr} = PTR
122 toEnum i = error ("Network.DNS.ADNS.RRType cannot be mapped to value " ++ show i)
138 toEnum x = case x .&. #{const adns_rrt_typemask} of
139 47 -> NSEC
140 i -> RRType i
123141
124142 fromEnum A = #{const adns_r_a}
143 fromEnum CNAME = #{const adns_r_cname}
125144 fromEnum MX = #{const adns_r_mx}
126145 fromEnum NS = #{const adns_r_ns}
127146 fromEnum PTR = #{const adns_r_ptr}
147 fromEnum x = #{const adns_r_unknown} .|. case x of
148 NSEC -> 47
149 (RRType i) -> i
150 _ -> error "Missing case in fromEnum ADNS.Base.RRType"
128151
129152instance Storable RRType where
130153 sizeOf _ = #{size adns_rrtype}
292292 a <- #{peek adns_rr_inthostaddr, ha} ptr
293293 return (RRIntHostAddr (fromEnum i) a)
294294
295-- |Original definition:
296--
297-- > typedef struct {
298-- > int len;
299-- > unsigned char *data;
300-- > } adns_rr_byteblock;
301
302data RRByteblock = RRByteblock Int (Ptr CChar)
303
304instance Storable RRByteblock where
305 sizeOf _ = #{size adns_rr_byteblock}
306 alignment _ = alignment (undefined :: CInt)
307 poke _ _ = fail "poke is undefined for Network.DNS.ADNS.RRByteblock"
308 peek ptr = do
309 l <- #{peek adns_rr_byteblock, len } ptr :: IO CInt
310 p <- #{peek adns_rr_byteblock, data} ptr
311 return (RRByteblock (fromEnum l) p)
312
295313data Answer = Answer
296314 { status :: Status
297315 -- ^ Status code for this query.
298316 , cname :: Maybe String
299 -- ^ Always 'Nothing' for @CNAME@ queries (which are not supported yet anyway).
317 -- ^ Always 'Nothing' for @CNAME@ queries
300318 , owner :: Maybe String
301319 -- ^ Only set if 'Owner' was requested for query.
302320 , expires :: CTime
326326
327327data Response
328328 = RRA RRAddr
329 | RRCNAME String
329330 | RRMX Int RRHostAddr
330331 | RRNS RRHostAddr
331332 | RRPTR String
333 | RRNSEC String
334 | RRUNKNOWN String
332335 deriving (Show)
333336
334337instance Storable Answer where
365365peekResp :: RRType -> Ptr b -> Int -> Int -> IO [Response]
366366peekResp _ _ _ 0 = return []
367367peekResp rt ptr off n = do
368 r <- parseByType rt
368 r <- parseByType (toEnum $ fromEnum rt)
369369 rs <- peekResp rt (ptr `plusPtr` off) off (n-1)
370370 return (r:rs)
371371
375375 parseByType PTR = peek (castPtr ptr) >>= peekCString >>= return . RRPTR
376376 parseByType MX = do (RRIntHostAddr i addr) <- peek (castPtr ptr)
377377 return (RRMX i addr)
378 parseByType CNAME = peek (castPtr ptr) >>= peekCString >>= return . RRCNAME
379 parseByType NSEC = do RRByteblock len rptr <- peek (castPtr ptr)
380 (name, _) <- peekFQDNAndAdvance rptr len
381 return $ RRNSEC name
382 parseByType (RRType _) = do RRByteblock len rptr <- peek (castPtr ptr)
383 str <- peekCStringLen (rptr, len)
384 return $ RRUNKNOWN str
385
386
387-- |This function parses a FQDN in uncompressed wire format and advances
388-- the pointer to the next byte after the parsed name.
389
390peekFQDNAndAdvance :: Ptr a -> Int -> IO (String, Ptr a)
391peekFQDNAndAdvance ptr _ = do
392 cc <- peek (castPtr ptr :: Ptr CChar)
393 let ptr1 = ptr `plusPtr` 1
394 case fromEnum cc of
395 c | c == 0 -> return ("", ptr1)
396 | c < 64 -> do name <- peekCStringLen (castPtr ptr1, c)
397 (zone, ptr2) <- peekFQDNAndAdvance (ptr1 `plusPtr` c) 0
398 return (name ++ "." ++ zone, ptr2)
399 | otherwise -> error "Compressed FQDN must not occur here."
400
401
378402
379403-- * ADNS Library Functions
380404
  
1module Main where
2
3import ADNS
4import ADNS.Base
5import Control.Concurrent.MVar
6import System.Environment
7
8main :: IO ()
9main = initResolver [NoErrPrint, NoServerWarn] $ \resolver -> do
10 args <- getArgs
11 case args of
12 [name] -> traverse resolver name
13 [t,name] -> work resolver (read t) name
14 _ -> putStrLn "Usage: t [typeid] fqdn"
15
16-- | Test function to see the raw results of a given query type
17work :: Resolver -> RRType -> String -> IO ()
18work resolver t n = do
19 putStrLn $ showString "Querying " . shows t $ showString " for " n
20 print =<< takeMVar =<< resolver n t [QuoteOk_Query]
21
22-- | Example implementation to traverse a DNSSEC signed zone.
23--
24-- This implementation is clearly wrong, because any real zone traversal
25-- is done using the NSEC records in the authority section of a NXDOMAIN
26-- response.
27--
28-- Unfortunly the adns library does not provide access to other sections
29-- than the answer section, so this walk is done by querying NSEC directly.
30--
31-- If there are signed subzones, the traversal switches to the subzone
32-- and stops if this subzone is traversed. You may continue the traversal
33-- by providing the next entry after the subzone.
34--
35-- You may try this mechanism on "dnssec.iks-jena.de"
36traverse :: Resolver -> String -> IO ()
37traverse resolver x = do
38 putStrLn x
39 answer <- takeMVar =<< resolver x NSEC [QuoteOk_Query]
40 case rrs answer of
41 [RRNSEC y] | not (x `endsWith` ('.':y)) -> traverse resolver y
42 _ -> return ()
43
44endsWith :: String -> String -> Bool
45endsWith x y = startsWith (reverse x) (reverse y)
46
47startsWith :: String -> String -> Bool
48startsWith (x:xs) (y:ys) = x == y && startsWith xs ys
49startsWith _ ys = null ys
  
2323Main-Is: adns-reverse-lookup.hs
2424Extra-Libraries: adns
2525GHC-Options: -O -Wall -threaded
26
27Executable: adns-test-and-traverse
28Hs-Source-Dirs: example, .
29Main-Is: adns-test-and-traverse.hs
30Extra-Libraries: adns
31GHC-Options: -O -Wall -threaded