packages feed

netw-0.1.0.0: src/Netw/Internal/Cmsg.hsc

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE DerivingStrategies #-}
module Netw.Internal.Cmsg where
#include <sys/socket.h>

import Control.Monad.Primitive
import Data.Primitive
import Data.Primitive.ByteArray.Unaligned

import Foreign
import Foreign.C

deriving newtype instance PrimUnaligned CSize

type CmsghdrType  = CInt
type CmsghdrLevel = CInt

data {-# CTYPE "sys/socket.h" "struct cmsghdr" #-} Cmsghdr  
data {-# CTYPE "unsigned char" #-} Cmsgdata

-- these function are not too nessisary
foreign import capi unsafe "sys/socket.h CMSG_ALIGN"
  _CMSG_ALIGN :: CSize -> CSize
foreign import capi unsafe "sys/socket.h CMSG_SPACE"
  _CMSG_SPACE :: CSize -> CSize
foreign import capi unsafe "sys/socket.h CMSG_LEN"
  _CMSG_LEN   :: CSize -> CSize
foreign import capi unsafe "sys/socket.h CMSG_DATA"
  _CMSG_DATA  :: Ptr Cmsghdr -> Ptr Cmsgdata

foreign import ccall unsafe "cmsg_nxthdr"
  _CMSG_NXTHDR   :: Ptr a -> CSize -> Ptr Cmsghdr -> IO (Ptr Cmsghdr)
foreign import ccall unsafe "cmsg_firsthdr"
  _CMSG_FIRSTHDR :: Ptr a -> CSize -> Ptr Cmsghdr

cmsgSpace :: Int -> Int
cmsgSpace = fromIntegral . _CMSG_SPACE . fromIntegral

{- |
 - `peekCmsg buffer hdroffs reader`
 - `reader` is a function that accept the control message level and type, and the data payload.
 - The payload is given as a buffer, the offset of the payload section (cmsg_data), and the size of the payload (cmsg_len - CMSG_LEN(0)).
 - -}
peekCmsg :: ByteArray -> Int -> (CmsghdrLevel -> CmsghdrType -> ByteArray -> Int -> Int -> a) -> a
peekCmsg buffer hdroffs reader = reader cmsgLevel cmsgType buffer (hdroffs + cmsghdrDataOffset) (fromIntegral $ cmsgSize - cmsghdrSize)
  where cmsgSize  = indexUnalignedByteArray @CSize        buffer (hdroffs + #{offset struct cmsghdr, cmsg_len})
        cmsgLevel = indexUnalignedByteArray @CmsghdrLevel buffer (hdroffs + #{offset struct cmsghdr, cmsg_level})
        cmsgType  = indexUnalignedByteArray @CmsghdrType  buffer (hdroffs + #{offset struct cmsghdr, cmsg_type})
        cmsghdrDataOffset = #{const (int)(ptrdiff_t)((char*)CMSG_DATA((struct cmsghdr*)0x01) - (char*)0x01)}
        cmsghdrSize       = _CMSG_LEN 0

{- |
 - `pokeCmsg buffer hdroffs cmsgLevel cmsgType cmsgPayloadLen cmsgPayload`
 - Store a control message element info the `buffer` at `hdroffs`.
 - `cmsgPayloadLen` is the length of the data section.
 - `cmsgPayload` is a function that take a buffer, offset into that buffer, and write the data section at that location.
 - -}
pokeCmsg :: PrimMonad m => MutableByteArray (PrimState m) -> Int -> CmsghdrLevel -> CmsghdrType -> Int -> (MutableByteArray (PrimState m) -> Int -> m ()) -> m ()
pokeCmsg buffer hdroffs cmsgLevel cmsgType cmsgPayloadLen cmsgPayload = do
  writeUnalignedByteArray @_ @CSize        buffer (hdroffs + #{offset struct cmsghdr, cmsg_len})   (_CMSG_LEN $ fromIntegral cmsgPayloadLen)
  writeUnalignedByteArray @_ @CmsghdrLevel buffer (hdroffs + #{offset struct cmsghdr, cmsg_level}) cmsgLevel
  writeUnalignedByteArray @_ @CmsghdrType  buffer (hdroffs + #{offset struct cmsghdr, cmsg_type})  cmsgType
  cmsgPayload buffer (hdroffs + cmsghdrDataOffset)
  where cmsghdrDataOffset = #{const (int)(ptrdiff_t)((char*)CMSG_DATA((struct cmsghdr*)0x01) - (char*)0x01)}

newCmsgBuffer :: PrimMonad m => Int -> m (MutableByteArray (PrimState m))
newCmsgBuffer totalSize = do
  buffer <- newAlignedPinnedByteArray totalSize #{alignment struct cmsghdr}
  fillByteArray buffer 0 totalSize 0
  return buffer