module Resolve.DNS.LiveTCP where
import qualified Resolve.DNS.TCP as TCP
import Resolve.Types
import qualified Resolve.DNS.Channel as C
import Resolve.DNS.Types
import Control.Monad
import Control.Monad.STM
import Control.Monad.Trans.Except
import Control.Concurrent.STM.TMVar
import Control.Concurrent
import Data.Maybe
import Data.Unique
import Control.Exception
import Network.Socket hiding (Closed)
import System.Log.Logger
nameM = "Resolve.DNS.LiveTCP"
data Config = Config { family :: Family
, protocol :: ProtocolNumber
, server :: SockAddr
}
deriving (Show)
data Record = Record { resolver :: Resolver Message Message
, unique :: Unique
, sock :: Socket
}
new :: Config -> IO (Resolver Message Message)
new c = do
res <- newEmptyTMVarIO
l <- newEmptyTMVarIO
let del r = do
delete (resolver r)
close (sock r)
x <- atomically $ do
x <- tryReadTMVar res
case x of
Nothing -> return False
Just r' -> if ((unique r) == (unique r')) then (void $ takeTMVar res) >> return True
else return False
when x $ debugM nameM $ (show c) ++ " connection closed, deleted"
let loop a = do
let nameF = nameM ++ ".resolve"
bracket
(atomically $ do
x <- tryReadTMVar res
when (isNothing x) $ putTMVar l ()
return x)
(\x -> atomically $ when (isNothing x) $ takeTMVar l)
(\x -> case x of
Just r -> do
b <- try (resolve (resolver r) a)
case b of
Left C.Dead -> do
del r
loop a
Right b' -> return b'
Nothing -> do
debugM nameF $ (show c) ++ " trying to reconnect"
bracketOnError
(socket (family c) (Stream) (protocol c))
(\s -> close s)
(\s -> do
connect s (server c)
debugM nameF $ (show c ) ++ " reconnected"
bracketOnError
(TCP.new $ TCP.Config { TCP.socket = s})
delete
(\r -> do
u <- newUnique
atomically $ do
putTMVar res $ Record { resolver = r
, unique = u
, sock = s}
)
)
loop a)
return $ Resolver { resolve = loop
, delete = do
x <- atomically $ tryReadTMVar res
case x of
Nothing -> return ()
Just r -> del r
}