module Network.Socket.Msg.CMsg
( CSockLen
, CMsg(..)
, CMsgHdr(..)
, c_cmsg_firsthdr
, c_cmsg_nexthdr
, c_cmsg_data
, cmsgSpace
, peekCMsg
, pokeCMsg
) where
import Network.Socket.Msg.MsgHdr (MsgHdr)
import qualified Data.ByteString as B
import Data.Maybe (isNothing,fromJust)
import Foreign.C.Types (CUInt(..),CInt(..),CSize(..))
import Foreign.Marshal.Utils (copyBytes)
import Foreign.Ptr (Ptr,castPtr,nullPtr)
import Foreign.Storable (Storable(..))
type CSockLen = CUInt
data CMsg = CMsg
{ cmsgLevel :: Int
, cmsgType :: Int
, cmsgData :: B.ByteString
}
instance Show CMsg where
show cmsg = concat ["(",
"Level: ", show $ cmsgLevel cmsg, ", ",
"Type: ", show $ cmsgType cmsg, ", ",
"Data: ", show $ cmsgData cmsg, ")"]
data CMsgHdr = CMsgHdr
{ cmsgLen :: CSockLen
, cmsghdrLevel :: CInt
, cmsghdrType :: CInt
}
instance Storable CMsgHdr where
sizeOf _ = (16)
alignment _ = alignment (undefined :: CInt)
peek p = do
len <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
level <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
t <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) p
return $ CMsgHdr len level t
poke p cmh = do
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) p (cmsgLen cmh)
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p (cmsghdrLevel cmh)
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p (cmsghdrType cmh)
foreign import ccall unsafe "cmsg_firsthdr"
c_cmsg_firsthdr :: Ptr MsgHdr -> Ptr CMsgHdr
foreign import ccall unsafe "cmsg_nexthdr"
c_cmsg_nexthdr :: Ptr MsgHdr -> Ptr CMsgHdr -> Ptr CMsgHdr
foreign import ccall unsafe "cmsg_data"
c_cmsg_data :: Ptr CMsgHdr -> Ptr ()
foreign import ccall unsafe "cmsg_space"
c_cmsg_space :: CSize -> CSize
cmsgSpace :: CMsg -> Int
cmsgSpace = spc . B.length . cmsgData
where spc = fromIntegral . c_cmsg_space . fromIntegral
cmsgExtractData :: Ptr CMsgHdr -> IO (Maybe B.ByteString)
cmsgExtractData p = do
let dataPtr = castPtr $ c_cmsg_data p
dataLen <- return . cmsgLen =<< peek p
if dataPtr == nullPtr
then return Nothing
else return.Just =<< B.packCStringLen (dataPtr, fromIntegral dataLen)
peekCMsg :: Ptr CMsgHdr -> IO (Maybe CMsg)
peekCMsg pCMsgHdr =
peek pCMsgHdr >>= \cmsghdr ->
cmsgExtractData pCMsgHdr >>= \dat ->
return $ if isNothing dat
then Nothing
else Just CMsg { cmsgLevel = fromIntegral $ cmsghdrLevel cmsghdr
, cmsgType = fromIntegral $ cmsghdrType cmsghdr
, cmsgData = fromJust dat }
pokeCMsg :: Ptr CMsgHdr -> CMsg -> IO ()
pokeCMsg pHdr cmsg = do
poke pHdr cmsghdr
let dptr = castPtr $ c_cmsg_data pHdr
B.useAsCStringLen (cmsgData cmsg) $ \(bptr,len) -> copyBytes dptr bptr len
where
cmsghdr = CMsgHdr { cmsgLen = fromIntegral $ B.length $ cmsgData cmsg
, cmsghdrLevel = fromIntegral $ cmsgLevel cmsg
, cmsghdrType = fromIntegral $ cmsgType cmsg }