{-# LINE 1 "Network/Socket/Info.hsc" #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
#include "HsNetDef.h"
module Network.Socket.Info where
import Control.Exception (mask, onException)
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE
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 75 "Network/Socket/Info.hsc" #-}
(AI_ADDRCONFIG, 32),
{-# LINE 76 "Network/Socket/Info.hsc" #-}
{-# LINE 79 "Network/Socket/Info.hsc" #-}
{-# LINE 80 "Network/Socket/Info.hsc" #-}
(AI_ALL, 16),
{-# LINE 81 "Network/Socket/Info.hsc" #-}
{-# LINE 84 "Network/Socket/Info.hsc" #-}
(AI_CANONNAME, 2),
{-# LINE 85 "Network/Socket/Info.hsc" #-}
(AI_NUMERICHOST, 4),
{-# LINE 86 "Network/Socket/Info.hsc" #-}
{-# LINE 87 "Network/Socket/Info.hsc" #-}
(AI_NUMERICSERV, 1024),
{-# LINE 88 "Network/Socket/Info.hsc" #-}
{-# LINE 91 "Network/Socket/Info.hsc" #-}
(AI_PASSIVE, 1),
{-# LINE 92 "Network/Socket/Info.hsc" #-}
{-# LINE 93 "Network/Socket/Info.hsc" #-}
(AI_V4MAPPED, 8)
{-# LINE 94 "Network/Socket/Info.hsc" #-}
{-# LINE 97 "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 115 "Network/Socket/Info.hsc" #-}
alignment ~_ = alignment (0 :: CInt)
peek p = do
ai_flags <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
{-# LINE 119 "Network/Socket/Info.hsc" #-}
ai_family <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) p
{-# LINE 120 "Network/Socket/Info.hsc" #-}
ai_socktype <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
{-# LINE 121 "Network/Socket/Info.hsc" #-}
ai_protocol <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) p
{-# LINE 122 "Network/Socket/Info.hsc" #-}
ai_addr <- ((\hsc_ptr -> peekByteOff hsc_ptr 24)) p >>= peekSockAddr
{-# LINE 123 "Network/Socket/Info.hsc" #-}
ai_canonname_ptr <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) p
{-# LINE 124 "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 142 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) p (packFamily family)
{-# LINE 143 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p c_stype
{-# LINE 144 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p protocol
{-# LINE 145 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 16)) p (0::CSize)
{-# LINE 149 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 24)) p nullPtr
{-# LINE 150 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 32)) p nullPtr
{-# LINE 151 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 40)) p nullPtr
{-# LINE 152 "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 180 "Network/Socket/Info.hsc" #-}
(NI_NAMEREQD, 8),
{-# LINE 181 "Network/Socket/Info.hsc" #-}
(NI_NOFQDN, 4),
{-# LINE 182 "Network/Socket/Info.hsc" #-}
(NI_NUMERICHOST, 1),
{-# LINE 183 "Network/Socket/Info.hsc" #-}
(NI_NUMERICSERV, 2)]
{-# LINE 184 "Network/Socket/Info.hsc" #-}
defaultHints :: AddrInfo
defaultHints = AddrInfo {
addrFlags = []
, addrFamily = AF_UNSPEC
, addrSocketType = NoSocketType
, addrProtocol = defaultProtocol
, addrAddress = SockAddrInet 0 0
, addrCanonName = Nothing
}
class GetAddrInfo t where
getAddrInfo
:: Maybe AddrInfo
-> Maybe HostName
-> Maybe ServiceName
-> IO (t AddrInfo)
instance GetAddrInfo [] where
getAddrInfo = getAddrInfoList
instance GetAddrInfo NE.NonEmpty where
getAddrInfo = getAddrInfoNE
getAddrInfoNE
:: Maybe AddrInfo
-> Maybe HostName
-> Maybe ServiceName
-> IO (NonEmpty AddrInfo)
getAddrInfoNE 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 = mask $ \release -> 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 <- release (followAddrInfo ptr_addrs) `onException` c_freeaddrinfo ptr_addrs
c_freeaddrinfo ptr_addrs
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: "
, maybe "Nothing" show node
, ", service name: "
, maybe "Nothing" show service
, ")"
]
{-# LINE 308 "Network/Socket/Info.hsc" #-}
filteredHints = hints
{-# LINE 310 "Network/Socket/Info.hsc" #-}
getAddrInfoList
:: Maybe AddrInfo
-> Maybe HostName
-> Maybe ServiceName
-> IO [AddrInfo]
getAddrInfoList hints node service =
NE.toList <$> getAddrInfoNE hints node service
followAddrInfo :: Ptr AddrInfo -> IO (NonEmpty AddrInfo)
followAddrInfo ptr_ai
| ptr_ai == nullPtr = ioError $ mkIOError NoSuchThing "getaddrinfo must return at least one addrinfo" Nothing Nothing
| otherwise = do
a <- peek ptr_ai
ptr <- ((\hsc_ptr -> peekByteOff hsc_ptr 40)) ptr_ai
{-# LINE 328 "Network/Socket/Info.hsc" #-}
(a :|) <$> go ptr
where
go :: Ptr AddrInfo -> IO [AddrInfo]
go ptr
| ptr == nullPtr = return []
| otherwise = do
a' <- peek ptr
ptr' <- ((\hsc_ptr -> peekByteOff hsc_ptr 40)) ptr
{-# LINE 336 "Network/Socket/Info.hsc" #-}
as' <- go ptr'
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 349 "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 356 "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 389 "Network/Socket/Info.hsc" #-}
withCStringIf doService (32) $ \c_servlen c_serv ->
{-# LINE 390 "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
showsPrec _ (SockAddrUnix str) = showString str
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]
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)