dns/SimpleServer.hs
Kazu Yamamoto 71aae4e574 adding "." to "localhost" and using "8.8.8.8".
The last dot is suggested by Ben Clifford <benc>.
2012-10-17 10:29:33 +09:00

96 lines
3.0 KiB
Haskell

{-# LANGUAGE RecordWildCards, OverloadedStrings #-}
import Control.Applicative
import Control.Concurrent
import Control.Monad
import qualified Data.ByteString as S
import Data.ByteString.Lazy hiding (putStrLn, filter, length)
import Data.Default
import Data.IP
import Data.Maybe
import Data.Monoid
import Network.BSD
import Network.DNS hiding (lookup)
import Network.Socket hiding (recvFrom)
import Network.Socket.ByteString
import System.Environment
import System.Timeout
data Conf = Conf {
bufSize :: Int
, timeOut :: Int
, realDNS :: HostName
, hosts :: [(Domain, IPv4)]
}
instance Default Conf where
def = Conf {
bufSize = 512
, timeOut = 3 * 1000 * 1000
, realDNS = "8.8.8.8"
, hosts = [("localhost.", "127.0.0.1")]
}
timeout' :: String -> Int -> IO a -> IO (Maybe a)
timeout' msg tm io = do
result <- timeout tm io
maybe (putStrLn msg) (const $ return ()) result
return result
proxyRequest :: Conf -> ResolvConf -> DNSFormat -> IO (Maybe DNSFormat)
proxyRequest Conf{..} rc req = do
let worker Resolver{..} = do
let packet = mconcat . toChunks $ encode req
sendAll dnsSock packet
receive dnsSock
rs <- makeResolvSeed rc
withResolver rs $ \r ->
(>>= check) <$> timeout' "proxy timeout" timeOut (worker r)
where
ident = identifier . header $ req
check :: DNSFormat -> Maybe DNSFormat
check rsp = let hdr = header rsp
in if identifier hdr == ident
then Just rsp
else Nothing
handleRequest :: Conf -> ResolvConf -> DNSFormat -> IO (Maybe DNSFormat)
handleRequest conf@Conf{hosts=hosts} rc req =
maybe
(proxyRequest conf rc req)
(return . Just)
lookupHosts
where
filterA = filter ((==A) . qtype)
ident = identifier . header $ req
lookupHosts = do
q <- listToMaybe . filterA . question $ req
ip <- lookup (qname q) hosts
return $ responseA ident q ip
handlePacket :: Conf -> Socket -> SockAddr -> S.ByteString -> IO ()
handlePacket conf@Conf{..} sock addr bs = case decode (fromChunks [bs]) of
Right req -> do
let rc = defaultResolvConf { resolvInfo = RCHostName realDNS }
mrsp <- handleRequest conf rc req
case mrsp of
Just rsp ->
let packet = mconcat . toChunks $ encode rsp
in void $ timeout' "send timeout" timeOut (sendAllTo sock packet addr)
Nothing -> return ()
Left msg -> putStrLn msg
main :: IO ()
main = withSocketsDo $ do
dns <- fromMaybe (realDNS def) . listToMaybe <$> getArgs
let conf = def { realDNS=dns }
addrinfos <- getAddrInfo
(Just (defaultHints {addrFlags = [AI_PASSIVE]}))
Nothing (Just "domain")
addrinfo <- maybe (fail "no addr info") return (listToMaybe addrinfos)
sock <- socket (addrFamily addrinfo) Datagram defaultProtocol
bindSocket sock (addrAddress addrinfo)
forever $ do
(bs, addr) <- recvFrom sock (bufSize conf)
forkIO $ handlePacket conf sock addr bs