module Network.Pcap (
Pcap,
Pdump,
BpfProgram,
Callback,
Link(..),
Interface(..),
PcapAddr(..),
SockAddr(..),
Network(..),
PktHdr(..),
Statistics(..),
openOffline,
openLive,
openDead,
openDump,
setFilter,
compileFilter,
lookupDev,
findAllDevs,
lookupNet,
setNonBlock,
getNonBlock,
datalink,
setDatalink,
listDatalinks,
dispatch,
loop,
next,
dump,
statistics,
majorVersion,
minorVersion,
isSwapped,
snapshotLen,
) where
import Maybe (isNothing, fromJust )
import Data.Word ( Word8, Word32 )
import Foreign.Ptr ( Ptr, plusPtr, nullPtr, FunPtr, freeHaskellFunPtr )
import Foreign.C.String ( 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 )
data BpfProgramTag
type BpfProgram = ForeignPtr BpfProgramTag
data PcapTag
type Pcap = ForeignPtr PcapTag
data PcapDumpTag
type Pdump = ForeignPtr PcapDumpTag
data PktHdr = PktHdr { sec :: Word32,
usec :: Word32,
caplen :: Word32,
len :: Word32
}
deriving (Show)
data Statistics = Statistics { recv :: Word32,
drop :: Word32,
ifdrop :: Word32
}
deriving (Show)
data Interface = Interface { ifName :: String,
ifDescription :: String,
ifAddresses :: [PcapAddr],
ifFlags :: Word32
}
deriving (Read, Show)
data PcapAddr = PcapAddr { ifAddr :: SockAddr,
ifMask :: Maybe SockAddr,
ifBcast :: Maybe SockAddr,
ifPeer :: Maybe SockAddr
}
deriving (Read, Show)
data SockAddr = SockAddr { sockAddrFamily :: Family,
sockAddrAddr :: [Word8]
}
deriving (Read, Show)
data Network = Network { netAddr :: Word32,
netMask :: Word32
}
deriving (Read, Show)
openOffline
:: String
-> IO Pcap
openOffline name =
withCString name $ \namePtr ->
allocaArray (256) $ \errPtr -> do
ptr <- pcap_open_offline namePtr errPtr
if ptr == nullPtr
then peekCString errPtr >>= ioError . userError
else do
final <- h2c pcap_close
newForeignPtr final ptr
openLive
:: String
-> Int
-> Bool
-> Int
-> IO Pcap
openLive name snaplen promisc timeout =
withCString name $ \namePtr ->
allocaArray (256) $ \errPtr -> do
ptr <- pcap_open_live namePtr
(fromIntegral snaplen)
(fromBool promisc)
(fromIntegral timeout)
errPtr
if ptr == nullPtr
then peekCString errPtr >>= ioError . userError
else do
final <- h2c pcap_close
newForeignPtr final ptr
openDead
:: Link
-> Int
-> IO Pcap
openDead link snaplen =
do
ptr <- pcap_open_dead (packLink link)
(fromIntegral snaplen)
if ptr == nullPtr
then ioError $ userError "Can't open dead pcap device"
else do
final <- h2c pcap_close
newForeignPtr final ptr
foreign import ccall unsafe pcap_open_offline :: Ptr CChar -> Ptr CChar -> IO (Ptr PcapTag)
foreign import ccall unsafe pcap_close :: Ptr PcapTag -> IO ()
foreign import ccall unsafe pcap_open_live :: Ptr CChar -> CInt -> CInt -> CInt -> Ptr CChar -> 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
-> String
-> IO Pdump
openDump hdl name =
withCString name $ \namePtr -> do
ptr <- pcap_dump_open hdl namePtr
if (ptr == nullPtr) then
throwPcapError hdl
else do
final <- h2c' pcap_dump_close
newForeignPtr final ptr
foreign import ccall unsafe pcap_dump_open :: Ptr PcapTag -> Ptr CChar -> 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
ret <- pcap_compile hdl
bpfp
filter
(fromBool opt)
(fromIntegral mask)
if ret == (1) then
throwPcapError hdl
else do
ret <- pcap_setfilter hdl bpfp
if ret == (1) then
throwPcapError hdl
else
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)
if ret == (1) then
ioError $ userError "Pcap.compileFilter error"
else do
final <- h2c'' pcap_freecode
newForeignPtr final bpfp
foreign import ccall pcap_compile
:: Ptr PcapTag -> Ptr BpfProgramTag -> Ptr CChar -> CInt -> CInt -> IO CInt
foreign import ccall pcap_compile_nopcap
:: CInt -> CInt -> Ptr BpfProgramTag -> Ptr CChar -> 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)
data DevBuf
data DevAddr
lookupDev :: IO String
lookupDev =
allocaArray (256) $ \errPtr -> do
ptr <- pcap_lookupdev errPtr
if ptr == nullPtr
then peekCString errPtr >>= ioError . userError
else peekCString ptr
findAllDevs :: IO [Interface]
findAllDevs =
alloca $ \dptr -> do
allocaArray (256) $ \errPtr -> do
ret <- pcap_findalldevs dptr errPtr
if (ret == 1) then
peekCString errPtr >>= ioError . userError
else do
dbuf <- peek dptr
dl <- devs2list dbuf
pcap_freealldevs dbuf
return dl
devs2list :: Ptr DevBuf -> IO [Interface]
devs2list dbuf
| dbuf == nullPtr = do 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 name' desc' addrs' (fromIntegral (flags :: CUInt)))
addrs2list :: Ptr DevAddr -> IO [PcapAddr]
addrs2list abuf
| abuf == nullPtr = do 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 =
if sa /= nullPtr then
do
l <- return ((16)) :: IO CUChar
f <- (((\hsc_ptr -> peekByteOff hsc_ptr 0)) sa) :: IO CUChar
addr <- peekArray ((fromIntegral l) ((2)))
((plusPtr sa ((2))) :: Ptr Word8)
return (Just (SockAddr (unpackFamily (fromIntegral f)) addr))
else
return Nothing
in
do
addr <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) abuf >>= socka
mask <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) abuf >>= socka
bcast <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) abuf >>= socka
peer <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) abuf >>= socka
if isNothing addr then
ioError $ userError "Pcap.oneAddr: null address"
else
return (PcapAddr (fromJust addr) mask bcast peer)
lookupNet
:: String
-> IO Network
lookupNet dev =
withCString dev $ \name ->
alloca $ \netp ->
alloca $ \maskp -> do
allocaArray (256) $ \errPtr -> do
ret <- pcap_lookupnet name netp maskp errPtr
if ret == (1) then
peekCString errPtr >>= ioError . userError
else do
net <- peek netp
mask <- peek maskp
return (Network (fromIntegral net)
(fromIntegral mask) )
foreign import ccall unsafe pcap_lookupdev :: Ptr CChar -> IO (Ptr CChar)
foreign import ccall unsafe pcap_findalldevs :: Ptr (Ptr DevBuf) -> Ptr CChar -> IO CInt
foreign import ccall unsafe pcap_freealldevs :: Ptr DevBuf -> IO ()
foreign import ccall unsafe pcap_lookupnet :: Ptr CChar -> Ptr CUInt -> Ptr CUInt -> Ptr CChar -> IO CInt
setNonBlock :: Ptr PcapTag -> Bool -> IO ()
setNonBlock ptr block =
allocaArray (256) $ \errPtr -> do
ret <- pcap_setnonblock ptr (fromBool block) errPtr
if ret == (1)
then peekCString errPtr >>= ioError . userError
else return ()
getNonBlock :: Ptr PcapTag -> IO Bool
getNonBlock ptr =
allocaArray (256) $ \errPtr -> do
ret <- pcap_getnonblock ptr errPtr
if ret == (1)
then peekCString errPtr >>= ioError . userError
else return (toBool ret)
foreign import ccall unsafe pcap_setnonblock :: Ptr PcapTag -> CInt -> Ptr CChar -> IO CInt
foreign import ccall unsafe pcap_getnonblock :: Ptr PcapTag -> Ptr CChar -> IO CInt
throwPcapError :: Ptr PcapTag -> IO a
throwPcapError hdl = do
msg <- pcap_geterr hdl >>= peekCString
ioError (userError msg)
foreign import ccall unsafe pcap_geterr :: Ptr PcapTag -> IO (Ptr CChar)
type Callback = PktHdr -> Ptr Word8 -> IO ()
type CCallback = Ptr Word8 -> Ptr PktHdr -> Ptr Word8 -> IO ()
exportCallback :: Callback -> IO (FunPtr CCallback)
exportCallback f = exportCCallback $ \_user hdr ptr -> 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
f
(PktHdr
(fromIntegral (s :: CLong))
(fromIntegral (us :: CLong))
(fromIntegral (caplen :: CUInt))
(fromIntegral (len :: CUInt)))
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
if (result == 1) then
throwPcapError hdl
else
return (fromIntegral 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
if (result == 1) then
throwPcapError hdl
else
return (fromIntegral result)
next
:: Ptr PcapTag
-> IO (PktHdr, Ptr Word8)
next hdl =
allocaBytes ((16)) $ \hdr -> do
ptr <- pcap_next hdl hdr
if (ptr == nullPtr) then
return (PktHdr 0 0 0 0, ptr)
else 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
(fromIntegral (s :: CLong))
(fromIntegral (us :: CLong))
(fromIntegral (caplen :: CUInt))
(fromIntegral (len :: CUInt)),
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 = do
ret <- pcap_datalink hdl
return (unpackLink ret)
setDatalink :: Ptr PcapTag -> Link -> IO ()
setDatalink hdl link = do
ret <- pcap_set_datalink hdl (packLink link)
if (ret == 1) then
throwPcapError hdl
else
return ()
listDatalinks :: Ptr PcapTag -> IO [Link]
listDatalinks hdl =
alloca $ \lptr -> do
ret <- pcap_list_datalinks hdl lptr
if (ret == 1) then
throwPcapError hdl
else do
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
data PcapStats = PcapStats
statistics :: Ptr PcapTag -> IO Statistics
statistics hdl =
allocaBytes ((12)) $ \stats -> do
ret <- pcap_stats hdl stats
if (ret == 1) then
throwPcapError hdl
else do
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
(fromIntegral (recv :: CUInt))
(fromIntegral (drop :: CUInt))
(fromIntegral (ifdrop :: CUInt)))
foreign import ccall unsafe pcap_stats :: Ptr PcapTag -> Ptr PcapStats -> IO Int
majorVersion :: Ptr PcapTag -> IO Int
majorVersion ptr = do
v <- pcap_major_version ptr
return (fromIntegral (v :: CInt))
minorVersion :: Ptr PcapTag -> IO Int
minorVersion ptr = do
v <- pcap_major_version ptr
return (fromIntegral (v :: CInt))
<