{-# LINE 1 "Network/Socket/Info.hsc" #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
#include "HsNetDef.h"
module Network.Socket.Info where
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 74 "Network/Socket/Info.hsc" #-}
(AI_ADDRCONFIG, 32),
{-# LINE 75 "Network/Socket/Info.hsc" #-}
{-# LINE 78 "Network/Socket/Info.hsc" #-}
{-# LINE 79 "Network/Socket/Info.hsc" #-}
(AI_ALL, 16),
{-# LINE 80 "Network/Socket/Info.hsc" #-}
{-# LINE 83 "Network/Socket/Info.hsc" #-}
(AI_CANONNAME, 2),
{-# LINE 84 "Network/Socket/Info.hsc" #-}
(AI_NUMERICHOST, 4),
{-# LINE 85 "Network/Socket/Info.hsc" #-}
{-# LINE 86 "Network/Socket/Info.hsc" #-}
(AI_NUMERICSERV, 1024),
{-# LINE 87 "Network/Socket/Info.hsc" #-}
{-# LINE 90 "Network/Socket/Info.hsc" #-}
(AI_PASSIVE, 1),
{-# LINE 91 "Network/Socket/Info.hsc" #-}
{-# LINE 92 "Network/Socket/Info.hsc" #-}
(AI_V4MAPPED, 8)
{-# LINE 93 "Network/Socket/Info.hsc" #-}
{-# LINE 96 "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 114 "Network/Socket/Info.hsc" #-}
alignment ~_ = alignment (0 :: CInt)
peek p = do
ai_flags <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
{-# LINE 118 "Network/Socket/Info.hsc" #-}
ai_family <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) p
{-# LINE 119 "Network/Socket/Info.hsc" #-}
ai_socktype <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
{-# LINE 120 "Network/Socket/Info.hsc" #-}
ai_protocol <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) p
{-# LINE 121 "Network/Socket/Info.hsc" #-}
ai_addr <- ((\hsc_ptr -> peekByteOff hsc_ptr 24)) p >>= peekSockAddr
{-# LINE 122 "Network/Socket/Info.hsc" #-}
ai_canonname_ptr <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) p
{-# LINE 123 "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 141 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) p (packFamily family)
{-# LINE 142 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p c_stype
{-# LINE 143 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p protocol
{-# LINE 144 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 16)) p (0::CSize)
{-# LINE 148 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 24)) p nullPtr
{-# LINE 149 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 32)) p nullPtr
{-# LINE 150 "Network/Socket/Info.hsc" #-}
((\hsc_ptr -> pokeByteOff hsc_ptr 40)) p nullPtr
{-# LINE 151 "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 179 "Network/Socket/Info.hsc" #-}
(NI_NAMEREQD, 8),
{-# LINE 180 "Network/Socket/Info.hsc" #-}
(NI_NOFQDN, 4),
{-# LINE 181 "Network/Socket/Info.hsc" #-}
(NI_NUMERICHOST, 1),
{-# LINE 182 "Network/Socket/Info.hsc" #-}
(NI_NUMERICSERV, 2)]
{-# LINE 183 "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 = 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
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 306 "Network/Socket/Info.hsc" #-}
filteredHints = hints
{-# LINE 308 "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 326 "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 334 "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 347 "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 354 "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 387 "Network/Socket/Info.hsc" #-}
withCStringIf doService (32) $ \c_servlen c_serv ->
{-# LINE 388 "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]
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)