module Network.Pcap.Base
(
PcapTag
, PcapDumpTag
, Pdump
, BpfProgram
, BpfProgramTag
, Callback
, Direction(..)
, Link(..)
, Interface(..)
, PcapAddr(..)
, SockAddr(..)
, Network(..)
, PktHdr(..)
, Statistics(..)
, openOffline
, openLive
, openDead
, openDump
, setFilter
, compileFilter
, lookupDev
, findAllDevs
, lookupNet
, setNonBlock
, getNonBlock
, setDirection
, datalink
, setDatalink
, listDatalinks
, dispatch
, loop
, next
, dump
, sendPacket
, toPktHdr
, statistics
, version
, isSwapped
, snapshotLen
) where
import Control.Monad (when)
import Data.Maybe (isNothing, fromJust )
import Data.ByteString ()
import qualified Data.ByteString.Internal as B
import Data.Word (Word8, Word32)
import Foreign.Ptr (Ptr, plusPtr, nullPtr, FunPtr, freeHaskellFunPtr)
import Foreign.C.String (CString, peekCString, withCString)
import Foreign.C.Types (CInt, CUInt, CChar, CUChar, CLong)
import Foreign.ForeignPtr (ForeignPtr, FinalizerPtr, newForeignPtr)
import Foreign.Marshal.Alloc (alloca, allocaBytes, free)
import Foreign.Marshal.Array (allocaArray, peekArray)
import Foreign.Marshal.Utils (fromBool, toBool)
import Foreign.Storable (Storable(..))
import Network.Socket (Family(..), unpackFamily)
import System.IO.Error (userError)
newtype BpfProgramTag = BpfProgramTag ()
type BpfProgram = ForeignPtr BpfProgramTag
newtype PcapTag = PcapTag ()
newtype PcapDumpTag = PcapDumpTag ()
type Pdump = ForeignPtr PcapDumpTag
data PktHdr = PktHdr {
hdrSeconds :: !Word32
, hdrUseconds :: !Word32
, hdrCaptureLength :: !Word32
, hdrWireLength :: !Word32
} deriving (Eq, Show)
data Statistics = Statistics {
statReceived :: !Word32
, statDropped :: !Word32
, statIfaceDropped :: !Word32
} deriving (Eq, Show)
type ErrBuf = Ptr CChar
data Interface = Interface {
ifName :: String
, ifDescription :: String
, ifAddresses :: [PcapAddr]
, ifFlags :: Word32
} deriving (Eq, Read, Show)
data PcapAddr = PcapAddr {
addrSA :: SockAddr
, addrMask :: Maybe SockAddr
, addrBcast :: Maybe SockAddr
, addrPeer :: Maybe SockAddr
} deriving (Eq, Read, Show)
data SockAddr = SockAddr {
saFamily :: !Family
, saAddr :: !B.ByteString
} deriving (Eq, Read, Show)
data Network = Network {
netAddr :: !Word32
, netMask :: !Word32
} deriving (Eq, Read, Show)
withErrBuf :: (a -> Bool) -> (ErrBuf -> IO a) -> IO a
withErrBuf isError f = allocaArray (256) $ \errPtr -> do
ret <- f errPtr
if isError ret
then peekCString errPtr >>= ioError . userError
else return ret
openOffline :: FilePath
-> IO (ForeignPtr PcapTag)
openOffline name =
withCString name $ \namePtr -> do
ptr <- withErrBuf (== nullPtr) (pcap_open_offline namePtr)
final <- h2c pcap_close
newForeignPtr final ptr
openLive :: String
-> Int
-> Bool
-> Int
-> IO (ForeignPtr PcapTag)
openLive name snaplen promisc timeout =
withCString name $ \namePtr -> do
ptr <- withErrBuf (== nullPtr) $ pcap_open_live namePtr
(fromIntegral snaplen) (fromBool promisc) (fromIntegral timeout)
final <- h2c pcap_close
newForeignPtr final ptr
openDead :: Link
-> Int
-> IO (ForeignPtr PcapTag)
openDead link snaplen = do
ptr <- pcap_open_dead (packLink link)
(fromIntegral snaplen)
when (ptr == nullPtr) $
ioError $ userError "Can't open dead pcap device"
final <- h2c pcap_close
newForeignPtr final ptr
foreign import ccall unsafe pcap_open_offline
:: CString -> ErrBuf -> IO (Ptr PcapTag)
foreign import ccall unsafe pcap_close
:: Ptr PcapTag -> IO ()
foreign import ccall unsafe pcap_open_live
:: CString -> CInt -> CInt -> CInt -> ErrBuf -> IO (Ptr PcapTag)
foreign import ccall unsafe pcap_open_dead
:: CInt -> CInt -> IO (Ptr PcapTag)
foreign import ccall "wrapper" h2c
:: (Ptr PcapTag -> IO()) -> IO (FinalizerPtr a)
openDump :: Ptr PcapTag
-> FilePath
-> IO Pdump
openDump hdl name =
withCString name $ \namePtr -> do
ptr <- pcap_dump_open hdl namePtr >>= throwPcapIf hdl (== nullPtr)
final <- h2c' pcap_dump_close
newForeignPtr final ptr
foreign import ccall unsafe pcap_dump_open
:: Ptr PcapTag -> CString -> IO (Ptr PcapDumpTag)
foreign import ccall unsafe pcap_dump_close
:: Ptr PcapDumpTag -> IO ()
foreign import ccall "wrapper" h2c'
:: (Ptr PcapDumpTag -> IO()) -> IO (FinalizerPtr a)
setFilter :: Ptr PcapTag
-> String
-> Bool
-> Word32
-> IO ()
setFilter hdl filt opt mask =
withCString filt $ \filter -> do
allocaBytes ((8)) $ \bpfp -> do
pcap_compile hdl bpfp filter (fromBool opt) (fromIntegral mask) >>=
throwPcapIf hdl (== 1)
pcap_setfilter hdl bpfp >>= throwPcapIf hdl (== 1)
pcap_freecode bpfp
compileFilter :: Int
-> Link
-> String
-> Bool
-> Word32
-> IO BpfProgram
compileFilter snaplen link filt opt mask =
withCString filt $ \filter ->
allocaBytes ((8)) $ \bpfp -> do
ret <- pcap_compile_nopcap (fromIntegral snaplen)
(packLink link)
bpfp
filter
(fromBool opt)
(fromIntegral mask)
when (ret == (1)) $
ioError $ userError "Pcap.compileFilter error"
final <- h2c'' pcap_freecode
newForeignPtr final bpfp
foreign import ccall pcap_compile
:: Ptr PcapTag -> Ptr BpfProgramTag -> CString -> CInt -> CInt
-> IO CInt
foreign import ccall pcap_compile_nopcap
:: CInt -> CInt -> Ptr BpfProgramTag -> CString -> CInt -> CInt
-> IO CInt
foreign import ccall pcap_setfilter
:: Ptr PcapTag -> Ptr BpfProgramTag -> IO CInt
foreign import ccall pcap_freecode
:: Ptr BpfProgramTag -> IO ()
foreign import ccall "wrapper" h2c''
:: (Ptr BpfProgramTag -> IO ()) -> IO (FinalizerPtr a)
newtype DevBuf = DevBuf ()
newtype DevAddr = DevAddr ()
lookupDev :: IO String
lookupDev = withErrBuf (== nullPtr) pcap_lookupdev >>= peekCString
findAllDevs :: IO [Interface]
findAllDevs =
alloca $ \dptr -> do
withErrBuf (== 1) (pcap_findalldevs dptr)
dbuf <- peek dptr
dl <- devs2list dbuf
pcap_freealldevs dbuf
return dl
devs2list :: Ptr DevBuf -> IO [Interface]
devs2list dbuf
| dbuf == nullPtr = return []
| otherwise = do
nextdev <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) dbuf
ds <- devs2list nextdev
d <- oneDev dbuf
return (d : ds)
oneDev :: Ptr DevBuf -> IO Interface
oneDev dbuf = do
name <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) dbuf
desc <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) dbuf
addrs <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) dbuf
flags <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) dbuf
name' <- peekCString name
desc' <- if desc /= nullPtr
then peekCString desc
else return ""
addrs' <- addrs2list addrs
return Interface { ifName = name'
, ifDescription = desc'
, ifAddresses = addrs'
, ifFlags = fromIntegral (flags :: CUInt)
}
addrs2list :: Ptr DevAddr -> IO [PcapAddr]
addrs2list abuf
| abuf == nullPtr = return []
| otherwise = do
nextaddr <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) abuf
as <- addrs2list nextaddr
a <- oneAddr abuf
return (a : as)
oneAddr :: Ptr DevAddr -> IO PcapAddr
oneAddr abuf =
let socka :: Ptr a -> IO (Maybe SockAddr)
socka sa | sa == nullPtr = return Nothing
| otherwise = do
l <- return ((16)) :: IO CUChar
f <- (((\hsc_ptr -> peekByteOff hsc_ptr 0)) sa) :: IO CUChar
let off = ((2))
nbytes = ((fromIntegral l) off)
addr <- B.create nbytes $ \p ->
B.memcpy p (plusPtr sa off :: Ptr Word8)
(fromIntegral nbytes)
return (Just (SockAddr (unpackFamily (fromIntegral f)) addr))
in do
addr <- socka =<< ((\hsc_ptr -> peekByteOff hsc_ptr 4)) abuf
when (isNothing addr) $
ioError $ userError "Pcap.oneAddr: null address"
mask <- socka =<< ((\hsc_ptr -> peekByteOff hsc_ptr 8)) abuf
bcast <- socka =<< ((\hsc_ptr -> peekByteOff hsc_ptr 12)) abuf
peer <- socka =<< ((\hsc_ptr -> peekByteOff hsc_ptr 16)) abuf
return PcapAddr { addrSA = fromJust addr
, addrMask = mask
, addrBcast = bcast
, addrPeer = peer
}
lookupNet :: String
-> IO Network
lookupNet dev = withCString dev $ \name ->
alloca $ \netp -> alloca $ \maskp -> do
withErrBuf (== 1) (pcap_lookupnet name netp maskp)
net <- peek netp
mask <- peek maskp
return Network { netAddr = fromIntegral net
, netMask = fromIntegral mask
}
foreign import ccall unsafe pcap_lookupdev
:: CString -> IO CString
foreign import ccall unsafe pcap_findalldevs
:: Ptr (Ptr DevBuf) -> ErrBuf -> IO CInt
foreign import ccall unsafe pcap_freealldevs
:: Ptr DevBuf -> IO ()
foreign import ccall unsafe pcap_lookupnet
:: CString -> Ptr CUInt -> Ptr CUInt -> ErrBuf -> IO CInt
setNonBlock :: Ptr PcapTag -> Bool -> IO ()
setNonBlock hdl block = do
withErrBuf (== 1) (pcap_setnonblock hdl (fromBool block))
return ()
getNonBlock :: Ptr PcapTag -> IO Bool
getNonBlock hdl = toBool `fmap` withErrBuf (== 1) (pcap_getnonblock hdl)
data Direction = InOut
| In
| Out
deriving (Eq, Show, Read)
setDirection :: Ptr PcapTag -> Direction -> IO ()
setDirection hdl dir = do
pcap_setdirection hdl (packDirection dir) >>= throwPcapIf hdl (== 1)
return ()
packDirection :: Direction -> CInt
packDirection In = (1)
packDirection Out = (2)
packDirection InOut = (0)
foreign import ccall unsafe pcap_setnonblock
:: Ptr PcapTag -> CInt -> ErrBuf -> IO CInt
foreign import ccall unsafe pcap_getnonblock
:: Ptr PcapTag -> ErrBuf -> IO CInt
foreign import ccall unsafe pcap_setdirection
:: Ptr PcapTag -> CInt -> IO CInt
throwPcapIf :: Ptr PcapTag -> (a -> Bool) -> a -> IO a
throwPcapIf hdl p v = if p v
then pcap_geterr hdl >>= peekCString >>= ioError . userError
else return v
foreign import ccall unsafe pcap_geterr
:: Ptr PcapTag -> IO CString
sendPacket :: Ptr PcapTag
-> Ptr Word8
-> Int
-> IO ()
sendPacket hdl buf size = do
pcap_sendpacket hdl buf (fromIntegral size) >>= throwPcapIf hdl (== 1)
return ()
foreign import ccall unsafe pcap_sendpacket
:: Ptr PcapTag -> Ptr Word8 -> CInt -> IO CInt
type Callback = PktHdr -> Ptr Word8 -> IO ()
type CCallback = Ptr Word8 -> Ptr PktHdr -> Ptr Word8 -> IO ()
toPktHdr :: Ptr PktHdr -> IO PktHdr
toPktHdr hdr = do
let ts = ((\hsc_ptr -> hsc_ptr `plusPtr` 0)) hdr
s <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) ts
us <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) ts
caplen <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) hdr
len <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) hdr
return PktHdr { hdrSeconds = fromIntegral (s :: CLong)
, hdrUseconds = fromIntegral (us :: CLong)
, hdrCaptureLength = fromIntegral (caplen :: CUInt)
, hdrWireLength = fromIntegral (len :: CUInt)
}
exportCallback :: Callback -> IO (FunPtr CCallback)
exportCallback f = exportCCallback $ \_user chdr ptr -> do
hdr <- toPktHdr chdr
f hdr ptr
dispatch :: Ptr PcapTag
-> Int
-> Callback
-> IO Int
dispatch hdl count f = do
handler <- exportCallback f
result <- pcap_dispatch hdl (fromIntegral count) handler nullPtr
freeHaskellFunPtr handler
fromIntegral `fmap` throwPcapIf hdl (== 1) result
loop :: Ptr PcapTag
-> Int
-> Callback
-> IO Int
loop hdl count f = do
handler <- exportCallback f
result <- pcap_loop hdl (fromIntegral count) handler nullPtr
freeHaskellFunPtr handler
fromIntegral `fmap` throwPcapIf hdl (== 1) result
next :: Ptr PcapTag
-> IO (PktHdr, Ptr Word8)
next hdl =
allocaBytes ((16)) $ \chdr -> do
ptr <- pcap_next hdl chdr
if (ptr == nullPtr)
then return (PktHdr 0 0 0 0, ptr)
else do
hdr <- toPktHdr chdr
return (hdr, ptr)
dump :: Ptr PcapDumpTag
-> Ptr PktHdr
-> Ptr Word8
-> IO ()
dump hdl hdr pkt = pcap_dump hdl hdr pkt
foreign import ccall "wrapper" exportCCallback
:: CCallback -> IO (FunPtr CCallback)
foreign import ccall pcap_dispatch
:: Ptr PcapTag -> CInt -> FunPtr CCallback -> Ptr Word8 -> IO CInt
foreign import ccall pcap_loop
:: Ptr PcapTag -> CInt -> FunPtr CCallback -> Ptr Word8 -> IO CInt
foreign import ccall pcap_next
:: Ptr PcapTag -> Ptr PktHdr -> IO (Ptr Word8)
foreign import ccall pcap_dump
:: Ptr PcapDumpTag -> Ptr PktHdr -> Ptr Word8 -> IO ()
datalink :: Ptr PcapTag -> IO Link
datalink hdl = unpackLink `fmap` pcap_datalink hdl
setDatalink :: Ptr PcapTag -> Link -> IO ()
setDatalink hdl link = do
pcap_set_datalink hdl (packLink link) >>= throwPcapIf hdl (== 1)
return ()
listDatalinks :: Ptr PcapTag -> IO [Link]
listDatalinks hdl =
alloca $ \lptr -> do
ret <- pcap_list_datalinks hdl lptr >>= throwPcapIf hdl (== 1)
dlbuf <- peek lptr
dls <- peekArray (fromIntegral (ret :: CInt)) dlbuf
free dlbuf
return (map unpackLink dls)
foreign import ccall unsafe pcap_datalink
:: Ptr PcapTag -> IO CInt
foreign import ccall unsafe pcap_set_datalink
:: Ptr PcapTag -> CInt -> IO CInt
foreign import ccall unsafe pcap_list_datalinks
:: Ptr PcapTag -> Ptr (Ptr CInt) -> IO CInt
statistics :: Ptr PcapTag -> IO Statistics
statistics hdl =
allocaBytes ((12)) $ \stats -> do
pcap_stats hdl stats >>= throwPcapIf hdl (== 1)
recv <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) stats
drop <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) stats
ifdrop <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) stats
return Statistics { statReceived = fromIntegral (recv :: CUInt)
, statDropped = fromIntegral (drop :: CUInt)
, statIfaceDropped = fromIntegral (ifdrop :: CUInt)
}
foreign import ccall unsafe pcap_stats
:: Ptr PcapTag -> Ptr Statistics -> IO Int
version :: Ptr PcapTag -> IO (Int, Int)
version hdl = do
major <- pcap_major_version hdl
minor <- pcap_minor_version hdl
return (fromIntegral major, fromIntegral minor)
isSwapped :: Ptr PcapTag -> IO Bool
isSwapped hdl = toBool `fmap` pcap_is_swapped hdl
snapshotLen :: Ptr PcapTag -> IO Int
snapshotLen hdl = fromIntegral `fmap` pcap_snapshot hdl
foreign import ccall pcap_major_version
:: Ptr PcapTag -> IO CInt