module Network.Whois (
WhoisServer (..)
, serverFor
, whois
) where
import Control.Monad (liftM2)
import Data.Char (toLower)
import Data.List (isInfixOf)
import Data.List.Split (splitOn)
import Network
import Network.URI (isIPv6address, isIPv4address)
import System.IO
data WhoisServer = WhoisServer {
hostname :: String
, port :: Int
, query :: String
} deriving (Show, Eq)
isIpAddress :: String -> Bool
isIpAddress = liftM2 (||) isIPv4address isIPv6address
serverFor :: String -> Maybe WhoisServer
serverFor a
| isIpAddress a = Just $ WhoisServer "whois.arin.net" 43 "n + "
| "." `isInfixOf` a = Just findServer
| otherwise = Nothing
where
tld = reverse . takeWhile (/= '.') $ reverse a
findServer = case tld of
"ly" -> WhoisServer "whois.nic.ly" 43 ""
"gd" -> WhoisServer "gd.whois-servers.net" 43 ""
"io" -> WhoisServer "io.whois-servers.net" 43 ""
"de" -> WhoisServer "whois.denic.de" 43 "-T dn,ace "
"so" -> WhoisServer "whois.nic.so" 43 ""
_ -> WhoisServer (tld ++ ".whois-servers.net") 43 "domain "
whois :: String -> IO (Maybe String, Maybe String)
whois a = withSocketsDo $ do
m <- fetchWhois a $ serverFor a
n <- case m of
Just n -> fetchWhois a $ referralServer n
_ -> return Nothing
return (m, n)
fetchWhois :: String -> Maybe WhoisServer -> IO (Maybe String)
fetchWhois a (Just server) = do
sock <- connectTo (hostname server) (PortNumber $ fromIntegral $ port server)
hPutStr sock $ query server ++ a ++ "\r\n"
contents <- hGetContents sock
return $ Just contents
fetchWhois a Nothing = return Nothing
getReferralServer :: String -> Maybe String
getReferralServer x = if null r
then Nothing
else Just (p $ head r)
where
l = lines $ map toLower x
f y = filter (map toLower y `isInfixOf`) l
r = concatMap f ["referralserver: ", "whois server: "]
p y = splitOn ": " y !! 1
parseReferralServer :: Maybe String -> Maybe WhoisServer
parseReferralServer Nothing = Nothing
parseReferralServer (Just s) = Just whoisServer
where
noPrefix = reverse $ takeWhile (/= '/') $ reverse s
splitPort = splitOn ":" noPrefix
whoisServer = if length splitPort > 1
then WhoisServer (head splitPort) (read (splitPort !! 1) :: Int) ""
else WhoisServer (head splitPort) 43 ""
referralServer :: String -> Maybe WhoisServer
referralServer a = parseReferralServer $ getReferralServer a