{-# LINE 1 "Network/Socket/Info.hsc" #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
#include "HsNetDef.h"
module Network.Socket.Info where
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Marshal.Utils (maybeWith, with)
import GHC.IO.Exception (IOErrorType(NoSuchThing))
import System.IO.Error (ioeSetErrorString, mkIOError)
import Network.Socket.Imports
import Network.Socket.Internal
import Network.Socket.Syscall
import Network.Socket.Types
type HostName = String
type ServiceName = String
data AddrInfoFlag =
AI_ADDRCONFIG
| AI_ALL
| AI_CANONNAME
| AI_NUMERICHOST
| AI_NUMERICSERV
| AI_PASSIVE
| AI_V4MAPPED
deriving (Eq, Read, Show)
aiFlagMapping :: [(AddrInfoFlag, CInt)]
aiFlagMapping =
[
{-# LINE 72 "Network/Socket/Info.hsc" #-}
(AI_ADDRCONFIG, 1024),
{-# LINE 73 "Network/Socket/Info.hsc" #-}
{-# LINE 76 "Network/Socket/Info.hsc" #-}
{-# LINE 77 "Network/Socket/Info.hsc" #-}
(AI_ALL, 256),
{-# LINE 78 "Network/Socket/Info.hsc" #-}
{-# LINE 81 "Network/Socket/Info.hsc" #-}
(AI_CANONNAME, 2),
{-# LINE 82 "Network/Socket/Info.hsc" #-}
(AI_NUMERICHOST, 4),
{-# LINE 83 "Network/Socket/Info.hsc" #-}
{-# LINE 84 "Network/Socket/Info.hsc" #-}
(AI_NUMERICSERV, 4096),
{-# LINE 85 "Network/Socket/Info.hsc" #-}
{-# LINE 88 "Network/Socket/Info.hsc" #-}
(AI_PASSIVE, 1),
{-# LINE 89 "Network/Socket/Info.hsc" #-}
{-# LINE 90 "Network/Socket/Info.hsc" #-}
(AI_V4MAPPED, 2048)
{-# LINE 91 "Network/Socket/Info.hsc" #-}
{-# LINE 94 "Network/Socket/Info.hsc" #-}
]
addrInfoFlagImplemented :: AddrInfoFlag -> Bool
addrInfoFlagImplemented f = packBits aiFlagMapping [f] /= 0
data AddrInfo = AddrInfo {
addrFlags :: [AddrInfoFlag]
, addrFamily :: Family
, addrSocketType :: SocketType
, addrProtocol :: ProtocolNumber
, addrAddress :: SockAddr
, addrCanonName :: Maybe String
} deriving (Eq, Show)
instance Storable AddrInfo where
sizeOf _ = 48
{-# LINE 112 "Network/Socket/Info.hsc" #-}
alignment _ = alignment (0 :: CInt)
peek p = do
ai_flags <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
{-# LINE 116 "Network/Socket/Info.hsc" #-}
ai_family <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) p
{-# LINE 117 "Network/Socket/Info.hsc" #-}
ai_socktype <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
{-# LINE 118 "Network/Socket/Info.hsc" #-}
ai_protocol <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) p
{-# LINE 119 "Network/Socket/Info.hsc" #-}
ai_addr <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) p >>= peekSockAddr
{-# LINE 120 "Network/Socket/Info.hsc" #-}
ai_canonname_ptr <- ((\hsc_ptr -> peekByteOff hsc_ptr 24)) p
{-# LINE 121 "Network/Socket/Info.hsc" #-}
ai_canonname <- if ai_canonname_ptr == nullPtr
then return Nothing
else Just <$> peekCString ai_canonname_ptr
return $ AddrInfo {
addrFlags = unpackBits aiFlagMapping ai_flags
, addrFamily = unpackFamily ai_family
, addrSocketType = unpackSocketType ai_socktype
, addrProtocol = ai_protocol
, addrAddress = ai_addr
, addrCanonName = ai_canonname
}
poke p (AddrInfo flags family sockType protocol _ _) = do
let c_stype = packSocketType sockType
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) p (packBits aiFlagMapping flags)
{-# LINE 139 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) p (packFamily family)
{-# LINE 140 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p c_stype
{-# LINE 141 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p protocol
{-# LINE 142 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 16)) p (0::CSize)
{-# LINE 146 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 32)) p nullPtr
{-# LINE 147 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 24)) p nullPtr
{-# LINE 148 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 40)) p nullPtr
{-# LINE 149 "Network/Socket/Info.hsc" #-}
data NameInfoFlag =
NI_DGRAM
| NI_NAMEREQD
| NI_NOFQDN
| NI_NUMERICHOST
| NI_NUMERICSERV
deriving (Eq, Read, Show)
niFlagMapping :: [(NameInfoFlag, CInt)]
niFlagMapping = [(NI_DGRAM, 16),
{-# LINE 177 "Network/Socket/Info.hsc" #-}
(NI_NAMEREQD, 4),
{-# LINE 178 "Network/Socket/Info.hsc" #-}
(NI_NOFQDN, 1),
{-# LINE 179 "Network/Socket/Info.hsc" #-}
(NI_NUMERICHOST, 2),
{-# LINE 180 "Network/Socket/Info.hsc" #-}
(NI_NUMERICSERV, 8)]
{-# LINE 181 "Network/Socket/Info.hsc" #-}
defaultHints :: AddrInfo
defaultHints = AddrInfo {
addrFlags = []
, addrFamily = AF_UNSPEC
, addrSocketType = NoSocketType
, addrProtocol = defaultProtocol
, addrAddress = SockAddrInet 0 0
, addrCanonName = Nothing
}
getAddrInfo
:: Maybe AddrInfo
-> Maybe HostName
-> Maybe ServiceName
-> IO [AddrInfo]
getAddrInfo hints node service = alloc getaddrinfo
where
alloc body = withSocketsDo $ maybeWith withCString node $ \c_node ->
maybeWith withCString service $ \c_service ->
maybeWith with filteredHints $ \c_hints ->
alloca $ \ptr_ptr_addrs ->
body c_node c_service c_hints ptr_ptr_addrs
getaddrinfo c_node c_service c_hints ptr_ptr_addrs = do
ret <- c_getaddrinfo c_node c_service c_hints ptr_ptr_addrs
if ret == 0 then do
ptr_addrs <- peek ptr_ptr_addrs
ais <- followAddrInfo ptr_addrs
c_freeaddrinfo ptr_addrs
case ais of
[] -> ioError $ mkIOError NoSuchThing message Nothing Nothing
_ -> return ais
else do
err <- gai_strerror ret
ioError $ ioeSetErrorString
(mkIOError NoSuchThing message Nothing Nothing)
err
message = concat [
"Network.Socket.getAddrInfo (called with preferred socket type/protocol: "
, maybe "Nothing" show hints
, ", host name: "
, show node
, ", service name: "
, show service
, ")"
]
{-# LINE 282 "Network/Socket/Info.hsc" #-}
toHints h = h { addrFlags = delete AI_NUMERICSERV (addrFlags h) }
filteredHints = case service of
Nothing -> toHints <$> hints
_ -> hints
{-# LINE 292 "Network/Socket/Info.hsc" #-}
followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo]
followAddrInfo ptr_ai
| ptr_ai == nullPtr = return []
| otherwise = do
a <- peek ptr_ai
as <- ((\hsc_ptr -> peekByteOff hsc_ptr 40)) ptr_ai >>= followAddrInfo
{-# LINE 299 "Network/Socket/Info.hsc" #-}
return (a : as)
foreign import ccall safe "hsnet_getaddrinfo"
c_getaddrinfo :: CString -> CString -> Ptr AddrInfo -> Ptr (Ptr AddrInfo)
-> IO CInt
foreign import ccall safe "hsnet_freeaddrinfo"
c_freeaddrinfo :: Ptr AddrInfo -> IO ()
gai_strerror :: CInt -> IO String
{-# LINE 311 "Network/Socket/Info.hsc" #-}
gai_strerror n = c_gai_strerror n >>= peekCString
foreign import ccall safe "gai_strerror"
c_gai_strerror :: CInt -> IO CString
{-# LINE 318 "Network/Socket/Info.hsc" #-}
withCStringIf :: Bool -> Int -> (CSize -> CString -> IO a) -> IO a
withCStringIf False _ f = f 0 nullPtr
withCStringIf True n f = allocaBytes n (f (fromIntegral n))
getNameInfo
:: [NameInfoFlag]
-> Bool
-> Bool
-> SockAddr
-> IO (Maybe HostName, Maybe ServiceName)
getNameInfo flags doHost doService addr = alloc getnameinfo
where
alloc body = withSocketsDo $
withCStringIf doHost (1025) $ \c_hostlen c_host ->
{-# LINE 351 "Network/Socket/Info.hsc" #-}
withCStringIf doService (32) $ \c_servlen c_serv ->
{-# LINE 352 "Network/Socket/Info.hsc" #-}
withSockAddr addr $ \ptr_addr sz ->
body c_hostlen c_host c_servlen c_serv ptr_addr sz
getnameinfo c_hostlen c_host c_servlen c_serv ptr_addr sz = do
ret <- c_getnameinfo ptr_addr
(fromIntegral sz)
c_host
c_hostlen
c_serv
c_servlen
(packBits niFlagMapping flags)
if ret == 0 then do
let peekIf doIf c_val =
if doIf then Just <$> peekCString c_val else return Nothing
host <- peekIf doHost c_host
serv <- peekIf doService c_serv
return (host, serv)
else do
err <- gai_strerror ret
ioError $ ioeSetErrorString
(mkIOError NoSuchThing message Nothing Nothing)
err
message = concat [
"Network.Socket.getNameInfo (called with flags: "
, show flags
, ", hostname lookup: "
, show doHost
, ", service name lookup: "
, show doService
, ", socket address: "
, show addr
, ")"
]
foreign import ccall safe "hsnet_getnameinfo"
c_getnameinfo :: Ptr SockAddr -> CInt -> CString -> CSize -> CString
-> CSize -> CInt -> IO CInt
packBits :: (Eq a, Num b, Bits b) => [(a, b)] -> [a] -> b
packBits mapping xs = foldl' pack 0 mapping
where
pack acc (k, v) | k `elem` xs = acc .|. v
| otherwise = acc
unpackBits :: (Num b, Bits b) => [(a, b)] -> b -> [a]
unpackBits [] _ = []
unpackBits ((k,v):xs) r
| r .&. v /= 0 = k : unpackBits xs (r .&. complement v)
| otherwise = unpackBits xs r
instance Show SockAddr where
{-# LINE 417 "Network/Socket/Info.hsc" #-}
showsPrec _ (SockAddrUnix str) = showString str
{-# LINE 421 "Network/Socket/Info.hsc" #-}
showsPrec _ (SockAddrInet port ha)
= showHostAddress ha
. showString ":"
. shows port
showsPrec _ (SockAddrInet6 port _ ha6 _)
= showChar '['
. showHostAddress6 ha6
. showString "]:"
. shows port
showHostAddress :: HostAddress -> ShowS
showHostAddress ip =
let (u3, u2, u1, u0) = hostAddressToTuple ip in
foldr1 (.) . intersperse (showChar '.') $ map showInt [u3, u2, u1, u0]
showHostAddress6 :: HostAddress6 -> ShowS
showHostAddress6 ha6@(a1, a2, a3, a4)
| a1 == 0 && a2 == 0 && a3 == 0xffff =
showString "::ffff:" . showHostAddress a4
| a1 == 0 && a2 == 0 && a3 == 0 && a4 >= 0x10000 =
showString "::" . showHostAddress a4
| end - begin > 1 =
showFields prefix . showString "::" . showFields suffix
| otherwise =
showFields fields
where
fields =
let (u7, u6, u5, u4, u3, u2, u1, u0) = hostAddress6ToTuple ha6 in
[u7, u6, u5, u4, u3, u2, u1, u0]
showFields = foldr (.) id . intersperse (showChar ':') . map showHex
prefix = take begin fields
suffix = drop end fields
begin = end + diff
(diff, end) = minimum $
scanl (\c i -> if i == 0 then c - 1 else 0) 0 fields `zip` [0..]
openSocket :: AddrInfo -> IO Socket
openSocket addr = socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)