module System.Socket.Internal.AddrInfo (
AddrInfo (..)
, GetAddrInfo (..)
, GetNameInfo (..)
, AddrInfoException (..)
, gaiStrerror
, eaiAGAIN
, eaiBADFLAGS
, eaiFAIL
, eaiFAMILY
, eaiMEMORY
, eaiNONAME
, eaiSOCKTYPE
, eaiSERVICE
, eaiSYSTEM
, AddrInfoFlags (..)
, aiADDRCONFIG
, aiALL
, aiCANONNAME
, aiNUMERICHOST
, aiNUMERICSERV
, aiPASSIVE
, aiV4MAPPED
, NameInfoFlags (..)
, niNAMEREQD
, niDGRAM
, niNOFQDN
, niNUMERICHOST
, niNUMERICSERV
) where
import Control.Exception
import Control.Monad
import Data.Bits
import Data.Monoid
import Data.Typeable
import qualified Data.ByteString as BS
import Foreign.Ptr
import Foreign.Storable
import Foreign.C.Types
import Foreign.C.String
import Foreign.Marshal.Alloc
import System.IO.Unsafe
import System.Socket.Family
import System.Socket.Family.INET
import System.Socket.Family.INET6
import System.Socket.Type
import System.Socket.Protocol
import System.Socket.Internal.FFI
data AddrInfo f t p
= AddrInfo
{ addrInfoFlags :: AddrInfoFlags
, addrAddress :: SockAddr f
, addrCanonName :: Maybe BS.ByteString
}
deriving instance (Eq (SockAddr f)) => Eq (AddrInfo f t p)
deriving instance (Show (SockAddr f)) => Show (AddrInfo f t p)
newtype AddrInfoException
= AddrInfoException CInt
deriving (Eq, Typeable)
instance Show AddrInfoException where
show e = "AddrInfoException \"" ++ gaiStrerror e ++ "\""
instance Exception AddrInfoException
gaiStrerror :: AddrInfoException -> String
gaiStrerror (AddrInfoException e) =
unsafePerformIO $ do
msgPtr <- c_gai_strerror e
peekCString msgPtr
eaiAGAIN :: AddrInfoException
eaiAGAIN = AddrInfoException (3)
eaiBADFLAGS :: AddrInfoException
eaiBADFLAGS = AddrInfoException (1)
eaiFAIL :: AddrInfoException
eaiFAIL = AddrInfoException (4)
eaiFAMILY :: AddrInfoException
eaiFAMILY = AddrInfoException (6)
eaiMEMORY :: AddrInfoException
eaiMEMORY = AddrInfoException (10)
eaiNONAME :: AddrInfoException
eaiNONAME = AddrInfoException (2)
eaiSERVICE :: AddrInfoException
eaiSERVICE = AddrInfoException (8)
eaiSOCKTYPE :: AddrInfoException
eaiSOCKTYPE = AddrInfoException (7)
eaiSYSTEM :: AddrInfoException
eaiSYSTEM = AddrInfoException (11)
newtype AddrInfoFlags
= AddrInfoFlags CInt
deriving (Eq, Show, Bits)
instance Monoid AddrInfoFlags where
mempty
= AddrInfoFlags 0
mappend (AddrInfoFlags a) (AddrInfoFlags b)
= AddrInfoFlags (a .|. b)
aiADDRCONFIG :: AddrInfoFlags
aiADDRCONFIG = AddrInfoFlags (32)
aiALL :: AddrInfoFlags
aiALL = AddrInfoFlags (16)
aiCANONNAME :: AddrInfoFlags
aiCANONNAME = AddrInfoFlags (2)
aiNUMERICHOST :: AddrInfoFlags
aiNUMERICHOST = AddrInfoFlags (4)
aiNUMERICSERV :: AddrInfoFlags
aiNUMERICSERV = AddrInfoFlags (1024)
aiPASSIVE :: AddrInfoFlags
aiPASSIVE = AddrInfoFlags (1)
aiV4MAPPED :: AddrInfoFlags
aiV4MAPPED = AddrInfoFlags (8)
newtype NameInfoFlags
= NameInfoFlags CInt
deriving (Eq, Show, Bits)
instance Monoid NameInfoFlags where
mempty
= NameInfoFlags 0
mappend (NameInfoFlags a) (NameInfoFlags b)
= NameInfoFlags (a .|. b)
niNAMEREQD :: NameInfoFlags
niNAMEREQD = NameInfoFlags (8)
niDGRAM :: NameInfoFlags
niDGRAM = NameInfoFlags (16)
niNOFQDN :: NameInfoFlags
niNOFQDN = NameInfoFlags (4)
niNUMERICHOST :: NameInfoFlags
niNUMERICHOST = NameInfoFlags (1)
niNUMERICSERV :: NameInfoFlags
niNUMERICSERV = NameInfoFlags (2)
class (Family f) => GetAddrInfo f where
getAddrInfo :: (Type t, Protocol p) => Maybe BS.ByteString -> Maybe BS.ByteString -> AddrInfoFlags -> IO [AddrInfo f t p]
instance GetAddrInfo INET where
getAddrInfo = getAddrInfo'
instance GetAddrInfo INET6 where
getAddrInfo = getAddrInfo'
getAddrInfo' :: forall f t p. (Family f, Type t, Protocol p) => Maybe BS.ByteString -> Maybe BS.ByteString -> AddrInfoFlags -> IO [AddrInfo f t p]
getAddrInfo' mnode mservice (AddrInfoFlags flags) = do
alloca $ \resultPtrPtr-> do
poke resultPtrPtr nullPtr
allocaBytes ((48)) $ \addrInfoPtr-> do
c_memset addrInfoPtr 0 (48)
poke (ai_flags addrInfoPtr) flags
poke (ai_family addrInfoPtr) (familyNumber (undefined :: f))
poke (ai_socktype addrInfoPtr) (typeNumber (undefined :: t))
poke (ai_protocol addrInfoPtr) (protocolNumber (undefined :: p))
fnode $ \nodePtr-> do
fservice $ \servicePtr->
bracket
(c_getaddrinfo nodePtr servicePtr addrInfoPtr resultPtrPtr)
(\_-> do resultPtr <- peek resultPtrPtr
when (resultPtr /= nullPtr) (c_freeaddrinfo resultPtr)
)
(\e-> if e == 0 then do
resultPtr <- peek resultPtrPtr
peekAddrInfos resultPtr
else do
throwIO (AddrInfoException e)
)
where
ai_flags = ((\hsc_ptr -> hsc_ptr `plusPtr` 0)) :: Ptr (AddrInfo a t p) -> Ptr CInt
ai_family = ((\hsc_ptr -> hsc_ptr `plusPtr` 4)) :: Ptr (AddrInfo a t p) -> Ptr CInt
ai_socktype = ((\hsc_ptr -> hsc_ptr `plusPtr` 8)) :: Ptr (AddrInfo a t p) -> Ptr CInt
ai_protocol = ((\hsc_ptr -> hsc_ptr `plusPtr` 12)) :: Ptr (AddrInfo a t p) -> Ptr CInt
ai_addr = ((\hsc_ptr -> hsc_ptr `plusPtr` 24)) :: Ptr (AddrInfo a t p) -> Ptr (Ptr a)
ai_canonname = ((\hsc_ptr -> hsc_ptr `plusPtr` 32)) :: Ptr (AddrInfo a t p) -> Ptr CString
ai_next = ((\hsc_ptr -> hsc_ptr `plusPtr` 40)) :: Ptr (AddrInfo a t p) -> Ptr (Ptr (AddrInfo a t p))
fnode = case mnode of
Just node -> BS.useAsCString node
Nothing -> \f-> f nullPtr
fservice = case mservice of
Just service -> BS.useAsCString service
Nothing -> \f-> f nullPtr
peekAddrInfos ptr =
if ptr == nullPtr
then return []
else do
flag <- peek (ai_flags ptr)
addr <- peek (ai_addr ptr) >>= peek
cname <- do cnPtr <- peek (ai_canonname ptr)
if cnPtr == nullPtr
then return Nothing
else BS.packCString cnPtr >>= return . Just
as <- peek (ai_next ptr) >>= peekAddrInfos
return ((AddrInfo (AddrInfoFlags flag) addr cname):as)
class (Family f) => GetNameInfo f where
getNameInfo :: SockAddr f -> NameInfoFlags -> IO (BS.ByteString, BS.ByteString)
instance GetNameInfo INET where
getNameInfo = getNameInfo'
instance GetNameInfo INET6 where
getNameInfo = getNameInfo'
getNameInfo' :: Storable a => a -> NameInfoFlags -> IO (BS.ByteString, BS.ByteString)
getNameInfo' addr (NameInfoFlags flags) =
alloca $ \addrPtr->
allocaBytes (1025) $ \hostPtr->
allocaBytes (32) $ \servPtr-> do
poke addrPtr addr
e <- c_getnameinfo addrPtr (fromIntegral $ sizeOf addr)
hostPtr (1025)
servPtr (32)
flags
if e == 0 then do
host <- BS.packCString hostPtr
serv <- BS.packCString servPtr
return (host,serv)
else do
throwIO (AddrInfoException e)
foreign import ccall FFI_GETADDRINFO_SAFETY FFI_GETADDRINFO
c_getaddrinfo :: CString -> CString -> Ptr (AddrInfo a t p) -> Ptr (Ptr (AddrInfo a t p)) -> IO CInt
foreign import ccall FFI_FREEADDRINFO_SAFETY FFI_FREEADDRINFO
c_freeaddrinfo :: Ptr (AddrInfo a t p) -> IO ()
foreign import ccall FFI_GETNAMEINFO_SAFETY FFI_GETNAMEINFO
c_getnameinfo :: Ptr a -> CInt -> CString -> CInt -> CString -> CInt -> CInt -> IO CInt
foreign import ccall FFI_GAI_STRERROR_SAFETY FFI_GAI_STRERROR
c_gai_strerror :: CInt -> IO CString