{-# LINE 1 "src/Network/Multicast.hsc" #-}
module Network.Multicast (
multicastSender, multicastReceiver
, addMembership, dropMembership
, setLoopbackMode, setTimeToLive, setInterface
, TimeToLive, LoopbackMode, enableLoopback, noLoopback
) where
import Network.BSD
import Network.Socket
import Foreign.C.Types
import Foreign.C.Error
import Foreign.Storable
import Foreign.Marshal
import Foreign.Ptr
import Control.Exception (bracketOnError)
import Data.Word (Word32)
type TimeToLive = Int
type LoopbackMode = Bool
enableLoopback, noLoopback :: LoopbackMode
enableLoopback = True
noLoopback = False
inet_addr :: HostName -> IO HostAddress
inet_addr = fmap hostAddress . getHostByName
multicastSender :: HostName -> PortNumber -> IO (Socket, SockAddr)
multicastSender host port = do
addr <- fmap (SockAddrInet port) (inet_addr host)
proto <- getProtocolNumber "udp"
sock <- socket AF_INET Datagram proto
return (sock, addr)
multicastReceiver :: HostName -> PortNumber -> IO Socket
multicastReceiver host port = bracketOnError get close setup
where
get :: IO Socket
get = do
proto <- getProtocolNumber "udp"
sock <- socket AF_INET Datagram proto
{-# LINE 90 "src/Network/Multicast.hsc" #-}
setSocketOption sock ReuseAddr 1
return sock
{-# LINE 93 "src/Network/Multicast.hsc" #-}
setup :: Socket -> IO Socket
setup sock = do
bind sock $ SockAddrInet port iNADDR_ANY
addMembership sock host Nothing
return sock
iNADDR_ANY :: HostAddress
iNADDR_ANY = htonl 0
foreign import ccall unsafe "htonl" htonl :: Word32 -> Word32
doSetSocketOption :: Storable a => CInt -> Socket -> a -> IO CInt
doSetSocketOption ip_multicast_option sock x = alloca $ \ptr -> do
poke ptr x
fd <- fdSocket sock
c_setsockopt fd _IPPROTO_IP ip_multicast_option (castPtr ptr) (toEnum $ sizeOf x)
setLoopbackMode :: Socket -> LoopbackMode -> IO ()
setLoopbackMode sock mode = maybeIOError "setLoopbackMode" $ do
let loop = if mode then 1 else 0 :: CUChar
doSetSocketOption _IP_MULTICAST_LOOP sock loop
setTimeToLive :: Socket -> TimeToLive -> IO ()
setTimeToLive sock ttl = maybeIOError "setTimeToLive" $ do
let val = toEnum ttl :: CInt
doSetSocketOption _IP_MULTICAST_TTL sock val
setInterface :: Socket -> HostName -> IO ()
setInterface sock host = maybeIOError "setInterface" $ do
addr <- inet_addr host
doSetSocketOption _IP_MULTICAST_IF sock addr
addMembership :: Socket -> HostName -> Maybe HostName -> IO ()
addMembership s host = maybeIOError "addMembership" . doMulticastGroup _IP_ADD_MEMBERSHIP s host
dropMembership :: Socket -> HostName -> Maybe HostName -> IO ()
dropMembership s host = maybeIOError "dropMembership" . doMulticastGroup _IP_DROP_MEMBERSHIP s host
maybeIOError :: String -> IO CInt -> IO ()
maybeIOError name f = f >>= \err -> case err of
0 -> return ()
_ -> ioError (errnoToIOError name (Errno (fromIntegral err)) Nothing Nothing)
doMulticastGroup :: CInt -> Socket -> HostName -> Maybe HostName -> IO CInt
doMulticastGroup flag sock host local = allocaBytes (8) $ \mReqPtr -> do
{-# LINE 146 "src/Network/Multicast.hsc" #-}
addr <- inet_addr host
iface <- case local of
Nothing -> return (0 `asTypeOf` addr)
{-# LINE 149 "src/Network/Multicast.hsc" #-}
Just loc -> inet_addr loc
(\hsc_ptr -> pokeByteOff hsc_ptr 0) mReqPtr addr
{-# LINE 151 "src/Network/Multicast.hsc" #-}
(\hsc_ptr -> pokeByteOff hsc_ptr 4) mReqPtr iface
{-# LINE 152 "src/Network/Multicast.hsc" #-}
fd <- fdSocket sock
c_setsockopt fd _IPPROTO_IP flag (castPtr mReqPtr) ((8))
{-# LINE 154 "src/Network/Multicast.hsc" #-}
{-# LINE 173 "src/Network/Multicast.hsc" #-}
foreign import ccall unsafe "setsockopt"
c_setsockopt :: CInt -> CInt -> CInt -> Ptr CInt -> CInt -> IO CInt
getLastError :: CInt -> IO CInt
getLastError = return
_IP_MULTICAST_IF, _IP_MULTICAST_TTL, _IP_MULTICAST_LOOP, _IP_ADD_MEMBERSHIP, _IP_DROP_MEMBERSHIP :: CInt
_IP_MULTICAST_IF = 32
{-# LINE 182 "src/Network/Multicast.hsc" #-}
_IP_MULTICAST_TTL = 33
{-# LINE 183 "src/Network/Multicast.hsc" #-}
_IP_MULTICAST_LOOP = 34
{-# LINE 184 "src/Network/Multicast.hsc" #-}
_IP_ADD_MEMBERSHIP = 35
{-# LINE 185 "src/Network/Multicast.hsc" #-}
_IP_DROP_MEMBERSHIP = 36
{-# LINE 186 "src/Network/Multicast.hsc" #-}
{-# LINE 188 "src/Network/Multicast.hsc" #-}
_IPPROTO_IP :: CInt
_IPPROTO_IP = 0
{-# LINE 191 "src/Network/Multicast.hsc" #-}