module Main where
-import Control.Applicative
import Control.Monad
-import Data.ConfigFile
-import Data.Either.Utils
+import qualified Data.ByteString.Char8 as C (pack, unpack)
+import qualified Data.ByteString.Lazy.Char8 as L (pack, unpack)
+import Data.ByteString.Lazy as BS hiding (reverse, putStrLn)
import Data.List.Split
-import Data.Aeson (decode)
-import Network.JsonRpc.Client
+import Data.Aeson (encode, decode, Value(..))
+import Network.HTTP.Types
+import Data.Conduit
+import Network.HTTP.Conduit
+
+import JsonRpcClient
+import Config
+import PowerDns
import NmcJson
confFile = "/etc/namecoin.conf"
--- Config file handling
-
-data Config = Config { rpcuser :: String
- , rpcpassword :: String
- , rpchost :: String
- , rpcport :: String
- } deriving (Show)
-
-readConfig :: String -> IO Config
-readConfig f = do
- cp <- return . forceEither =<< readfile emptyCP f
- return (Config { rpcuser = getSetting cp "rpcuser" ""
- , rpcpassword = getSetting cp "rpcpassword" ""
- , rpchost = getSetting cp "rpchost" "localhost"
- , rpcport = getSetting cp "rpcport" "8336"
- })
- where
- getSetting cp x dfl = case get cp "DEFAULT" x of
- Left _ -> dfl
- Right x -> x
+-- HTTP/JsonRpc interface
+
+qReq :: Config -> ByteString -> ByteString -> Request m
+qReq cf q id = applyBasicAuth (C.pack (rpcuser cf)) (C.pack (rpcpassword cf))
+ $ def { host = (C.pack (rpchost cf))
+ , port = (rpcport cf)
+ , method = "PUT"
+ , requestHeaders = [ (hAccept, "application/json")
+ , (hContentType, "application/json")
+ , (hConnection, "Keep-Alive")
+ ]
+ , requestBody = RequestBodyLBS $ encode $
+ JsonRpcRequest JsonRpcV1
+ "name_show"
+ [q]
+ (String "pdns-nmc")
+ , checkStatus = \_ _ _ -> Nothing
+ }
+
+qRsp :: Response ByteString -> Either String NmcDom
+qRsp rsp =
+ case parseJsonRpc (responseBody rsp) :: Either JsonRpcError NmcRes of
+ Left jerr ->
+ case (jrpcErrCode jerr) of
+ -4 -> Right emptyNmcDom
+ _ -> Left $ "JsonRpc error response: " ++ (show jerr)
+ Right jrsp ->
+ case resValue jrsp of
+ "" -> Right emptyNmcDom
+ vstr ->
+ case decode vstr :: Maybe NmcDom of
+ Nothing -> Left $ "Unparseable value: " ++ (show vstr)
+ Just dom -> Right dom
-- NMC interface
-queryNmc :: String -> String -> RRType -> String -> IO (Either String NmcDom)
-queryNmc uri fqdn qtype qid = do
- case reverse (splitOn "." fqdn) of
+queryNmc :: Manager -> Config -> String -> String
+ -> IO (Either String NmcDom)
+queryNmc mgr cfg fqdn qid = do
+ case reverse (splitOn "." fqdn) of
"bit":dn:xs -> do
- ans <- detailledRemote Version1 [] uri "name_show" $ "d/" ++ dn
- let mdom = decode (resValue ans) :: Maybe NmcDom
- case mdom of
- Nothing -> return $ Left ("Unparseable: " ++ (show (resValue ans)))
- Just dom -> return $ Right dom
+ rsp <- runResourceT $
+ httpLbs (qReq cfg (L.pack ("d/" ++ dn)) (L.pack qid)) mgr
+ return $ qRsp rsp
_ ->
return $ Left "Only \".bit\" domain is supported"
--- PowerDNS ABI
-
-data RRType = RRTypeSRV | RRTypeA | RRTypeAAAA | RRTypeCNAME
- | RRTypeDNAME | RRTypeSOA | RRTypeRP | RRTypeLOC
- | RRTypeNS | RRTypeDS
- | RRTypeANY | RRTypeError String
- deriving (Show)
-
-data PdnsRequest = PdnsRequestQ
- { qName :: String
- , qType :: RRType
- , iD :: String
- , remoteIpAddress :: String
- , localIpAddress :: Maybe String
- , ednsSubnetAddress :: Maybe String
- }
- | PdnsRequestAXFR String
- | PdnsRequestPing
- deriving (Show)
-
-pdnsParse ver s =
- let
- getQt qt = case qt of
- "SRV" -> RRTypeSRV
- "A" -> RRTypeA
- "AAAA" -> RRTypeAAAA
- "CNAME" -> RRTypeCNAME
- "DNAME" -> RRTypeDNAME
- "SOA" -> RRTypeSOA
- "RP" -> RRTypeRP
- "LOC" -> RRTypeLOC
- "NS" -> RRTypeNS
- "DS" -> RRTypeDS
- "ANY" -> RRTypeANY
- _ -> RRTypeError qt
- getLIp ver xs
- | ver >= 2 = case xs of
- x:_ -> Just x
- _ -> Nothing
- | otherwise = Nothing
- getRIp ver xs
- | ver >= 3 = case xs of
- _:x:_ -> Just x
- _ -> Nothing
- | otherwise = Nothing
- in
- case words s of
- "PING":[] -> Right PdnsRequestPing
- "AXFR":x:[] -> Right (PdnsRequestAXFR x)
- "Q":qn:"IN":qt:id:rip:xs -> Right (PdnsRequestQ
- { qName = qn
- , qType = getQt qt
- , iD = id
- , remoteIpAddress = rip
- , localIpAddress = getLIp ver xs
- , ednsSubnetAddress = getRIp ver xs
- })
- _ -> Left s
-
-pdnsOut :: String -> Either String PdnsRequest -> IO ()
-pdnsOut _ (Left e) = putStrLn ("ERROR\tUnparseable request: " ++ e)
-pdnsOut uri (Right rq) = case rq of
- PdnsRequestQ qn qt id lip rip eip -> do
- dom <- queryNmc uri qn qt id
- case dom of
- Left e -> putStrLn ("ERROR\tNmc query error: " ++ e)
- Right result -> print result
- PdnsRequestAXFR xfrreq ->
- putStrLn ("ERROR\t No support for AXFR " ++ xfrreq)
- PdnsRequestPing -> putStrLn "OK"
-
-- Main entry
main = do
+
cfg <- readConfig confFile
- let uri = "http://" ++ rpcuser cfg ++ ":" ++ rpcpassword cfg ++
- "@" ++ rpchost cfg ++ ":" ++ rpcport cfg ++ "/"
+
ver <- do
let
loopErr e = forever $ do
_ -> loopErr $ "bad HELO " ++ (show s)
putStrLn $ "OK\tDnsNmc ready to serve, protocol v." ++ (show ver)
- forever $ getLine >>= (pdnsOut uri) . (pdnsParse ver)
+
+ mgr <- newManager def
+ forever $ do
+ l <- getLine
+ case pdnsParse ver l of
+ Left e -> putStrLn $ "ERROR\t" ++ e
+ Right preq -> do
+ case preq of
+ PdnsRequestQ qname qtype id _ _ _ -> do
+ ncres <- queryNmc mgr cfg qname id
+ case ncres of
+ Left e -> putStrLn $ "ERROR\t" ++ e
+ Right dom -> putStrLn $ pdnsOut qtype dom
+ PdnsRequestAXFR xfrreq ->
+ putStrLn ("ERROR\tNo support for AXFR " ++ xfrreq)
+ PdnsRequestPing -> putStrLn "OK"
+
+-- for testing
+
+ask str = do
+ cfg <- readConfig confFile
+ mgr <- newManager def
+ ncres <- queryNmc mgr cfg str "test-req-id"
+ case ncres of
+ Left e -> putStrLn $ "ERROR\t" ++ e
+ Right dom -> putStrLn $ pdnsOut RRTypeANY dom