{-# INCLUDE <pcap.h> #-}
{-# INCLUDE <pcap-bpf.h> #-}
{-# INCLUDE <netinet/in.h> #-}
{-# INCLUDE <sys/socket.h> #-}
{-# LINE 1 "Network/Pcap/Base.hsc" #-}
{-# OPTIONS_GHC -fno-warn-unused-binds #-}
{-# LINE 2 "Network/Pcap/Base.hsc" #-}
------------------------------------------------------------------------------
-- |
--  Module	: Network.Pcap.Base
--  Copyright	: Bryan O'Sullivan 2007, Antiope Associates LLC 2004
--  License	: BSD-style
--  Maintainer	: bos@serpentine.com
--  Stability	: experimental
--  Portability	: non-portable
-- 
-- The 'Network.Pcap.Base' module is a low-level binding to all of the
-- functions in @libpcap@.  See <http://www.tcpdump.org> for more
-- information.
-- 
-- Only a minimum of marshaling is done.  For a higher-level interface
-- that\'s more friendly, use the 'Network.Pcap' module.
-- 
-- To convert captured packet data to a list, extract the length of
-- the captured buffer from the packet header record and use
-- 'peekArray' to convert the captured data to a list.  For
-- illustration:
--
-- >	import Network.Pcap.Base
-- >	import Foreign.Marshal.Array (peekArray)
-- >
-- >	main = do
-- >        let printIt :: PktHdr -> Ptr Word8 -> IO ()
-- >		printIt ph bytep = do
-- >	          a <- peekArray (fromIntegral (hdrCaptureLength ph)) bytep 
-- >	       	  print a
-- >
-- >	    p <- openLive "em0" 100 True 10000
-- >	    s <- withForeignPtr p $ \ptr -> do
-- >	           dispatch ptr (-1) printIt
-- >	    return ()
-- 
-- Note that the 'SockAddr' exported here is not the @SockAddr@ from
-- 'Network.Socket'. The @SockAddr@ from 'Network.Socket' corresponds
-- to @struct sockaddr_in@ in BSD terminology. The 'SockAddr' record
-- here is BSD's @struct sockaddr@. See W.R.Stevens, TCP Illustrated,
-- volume 2, for further elucidation.
-- 
-- This binding should be portable across systems that can use the
-- @libpcap@ library from @tcpdump.org@. It will not work with
-- Winpcap, a similar library for Windows, although adapting it should
-- not prove difficult.
--
------------------------------------------------------------------------------


module Network.Pcap.Base
    (
      -- * Types
      PcapTag
    , PcapDumpTag
    , Pdump
    , BpfProgram
    , BpfProgramTag
    , Callback
    , Direction(..)
    , Link(..)
    , Interface(..)
    , PcapAddr(..)
    , SockAddr(..)
    , Network(..)
    , PktHdr(..)
    , Statistics(..)

    -- * Device opening
    , openOffline		-- :: FilePath -> IO Pcap
    , openLive           -- :: String -> Int -> Bool -> Int -> IO Pcap
    , openDead                  -- :: Int    -> Int -> IO Pcap
    , openDump                 -- :: Ptr PcapTag -> FilePath -> IO Pdump

    -- * Filter handling
    , setFilter -- :: Ptr PcapTag -> String -> Bool -> Word32 -> IO ()
    , compileFilter -- :: Int -> Int  -> String -> Bool -> Word32 -> IO BpfProgram

    -- * Device utilities
    , lookupDev                 -- :: IO String
    , findAllDevs		-- :: IO [Interface]
    , lookupNet                 -- :: String -> IO Network

    -- * Interface control

    , setNonBlock		-- :: Ptr PcapTag -> Bool -> IO ()
    , getNonBlock		-- :: Ptr PcapTag -> IO Bool
    , setDirection

    -- * Link layer utilities
    , datalink                  -- :: Ptr PcapTag -> IO Link
    , setDatalink		-- :: Ptr PcapTag -> Link -> IO ()
    , listDatalinks		-- :: Ptr PcapTag -> IO [Link]

    -- * Packet processing
    , dispatch		-- :: Ptr PcapTag -> Int -> Callback -> IO Int
    , loop			-- :: Ptr PcapTag -> Int -> Callback -> IO Int
    , next			-- :: Ptr PcapTag -> IO (PktHdr, Ptr Word8)
    , dump			-- :: Ptr PcapDumpTag -> Ptr PktHdr -> Ptr Word8 -> IO ()

    -- * Sending packets
    , sendPacket

    -- * Conversion
    , toPktHdr

    -- * Miscellaneous
    , statistics		-- :: Ptr PcapTag -> IO Statistics
    , version		-- :: Ptr PcapTag -> IO (Int, Int)
    , isSwapped		-- :: Ptr PcapTag -> IO Bool
    , snapshotLen		-- :: Ptr PcapTag -> IO Int
    ) where

import Control.Monad (when)
import Data.Maybe (isNothing, fromJust )
import Data.ByteString ()

{-# LINE 120 "Network/Pcap/Base.hsc" #-}
import qualified Data.ByteString.Internal as B

{-# LINE 122 "Network/Pcap/Base.hsc" #-}
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)


{-# LINE 135 "Network/Pcap/Base.hsc" #-}

{-# LINE 136 "Network/Pcap/Base.hsc" #-}

{-# LINE 137 "Network/Pcap/Base.hsc" #-}

{-# LINE 138 "Network/Pcap/Base.hsc" #-}

newtype BpfProgramTag = BpfProgramTag ()
-- | Compiled Berkeley Packet Filter program
type BpfProgram = ForeignPtr BpfProgramTag

newtype PcapTag = PcapTag ()
-- | packet capture descriptor

newtype PcapDumpTag = PcapDumpTag ()
-- | dump file descriptor
type Pdump = ForeignPtr PcapDumpTag

data PktHdr = PktHdr {
      hdrSeconds :: {-# UNPACK #-} !Word32     -- ^ timestamp (seconds)
    , hdrUseconds :: {-# UNPACK #-} !Word32    -- ^ timestamp (microseconds)
    , hdrCaptureLength :: {-# UNPACK #-} !Word32 -- ^ number of bytes present in capture
    , hdrWireLength :: {-# UNPACK #-} !Word32  -- ^ number of bytes on the wire
    } deriving (Eq, Show)

data Statistics = Statistics {
      statReceived :: {-# UNPACK #-} !Word32	-- ^ packets received
    , statDropped :: {-# UNPACK #-} !Word32	-- ^ packets dropped by @libpcap@
    , statIfaceDropped :: {-# UNPACK #-} !Word32 -- ^ packets dropped by the network interface
    } deriving (Eq, Show)

type ErrBuf = Ptr CChar

--
-- Data types for interface list
--

-- | The interface structure.
data Interface = Interface {
      ifName :: String		-- ^ the interface name
    , ifDescription :: String -- ^ interface description string (if any)
    , ifAddresses :: [PcapAddr] -- ^ address families supported by this interface
    , ifFlags :: Word32
    } deriving (Eq, Read, Show)

-- | The address structure.
data PcapAddr = PcapAddr {
      addrSA  :: SockAddr		-- ^ interface address
    , addrMask  :: Maybe SockAddr	-- ^ network mask
    , addrBcast :: Maybe SockAddr	-- ^ broadcast address
    , addrPeer  :: Maybe SockAddr	-- ^ address of peer, of a point-to-point link
    } deriving (Eq, Read, Show)

-- | The socket address record. Note that this is not the same as
-- SockAddr from 'Network.Socket'. (That is a Haskell version of C\'s
-- @struct sockaddr_in@. This is the real @struct sockaddr@ from the
-- BSD network stack.)
data SockAddr = SockAddr {
      saFamily  :: {-# UNPACK #-} !Family	-- ^ an address family exported by Network.Socket
    , saAddr    :: {-# UNPACK #-} !B.ByteString
    } deriving (Eq, Read, Show)

-- | The network address record. Both the address and mask are in
--   network byte order.
data Network = Network {
      netAddr :: {-# UNPACK #-} !Word32	-- ^ IPv4 network address
    , netMask :: {-# UNPACK #-} !Word32	-- ^ IPv4 netmask
    } deriving (Eq, Read, Show)

withErrBuf :: (a -> Bool) -> (ErrBuf -> IO a) -> IO a
withErrBuf isError f = allocaArray (256) $ \errPtr -> do
{-# LINE 203 "Network/Pcap/Base.hsc" #-}
    ret <- f errPtr
    if isError ret
      then peekCString errPtr >>= ioError . userError
      else return ret

-- | 'openOffline' opens a dump file for reading. The file format
-- is the same as used by @tcpdump@ and Wireshark. The string @\"-\"@
-- is a synonym for @stdin@.
--
openOffline :: FilePath	-- ^ filename
	    -> IO (ForeignPtr PcapTag)
openOffline name =
    withCString name $ \namePtr -> do
      ptr <- withErrBuf (== nullPtr) (pcap_open_offline namePtr)
      final <- h2c pcap_close
      newForeignPtr final ptr

-- | 'openLive' is used to get a packet descriptor that can be used to
-- look at packets on the network. The arguments are the device name,
-- the snapshot legnth (in bytes), the promiscuity of the interface
-- ('True' == promiscuous) and a timeout in milliseconds.
-- 
-- Using @\"any\"@ as the device name will capture packets from all
-- interfaces.  On some systems, reading from the @\"any\"@ device is
-- incompatible with setting the interfaces into promiscuous mode. In
-- that case, only packets whose link layer addresses match those of
-- the interfaces are captured.
--
openLive :: String	-- ^ device name
	 -> Int		-- ^ snapshot length
	 -> Bool	-- ^ set to promiscuous mode?
	 -> Int		-- ^ timeout in milliseconds
	 -> 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' is used to get a packet capture descriptor without
-- opening a file or device. It is typically used to test packet
-- filter compilation by 'setFilter'. The arguments are the link type
-- and the snapshot length.
--
openDead :: Link		-- ^ datalink type
	 -> Int		-- ^ snapshot length
	 -> IO (ForeignPtr PcapTag)	-- ^ packet capture descriptor
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)


--
-- Open a dump device
--

-- | 'openDump' opens a dump file for writing. This dump file is
-- written to by the 'dump' function. The arguments are a raw packet
-- capture descriptor and the file name, with \"-\" as a synonym for
-- @stdout@.
openDump :: Ptr PcapTag	-- ^ packet capture descriptor
	 -> FilePath	-- ^ dump file name
	 -> IO Pdump	-- ^ davefile descriptor
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)

--
-- Set the filter
--

-- | Set a filter on the specified packet capture descriptor. Valid
-- filter strings are those accepted by @tcpdump@.
setFilter :: Ptr PcapTag	-- ^ packet capture descriptor
	  -> String	-- ^ filter string
	  -> Bool		-- ^ optimize?
	  -> Word32	-- ^ IPv4 network mask
	  -> IO ()
setFilter hdl filt opt mask =
    withCString filt $ \filter -> do
      allocaBytes ((8)) $ \bpfp -> do
{-# LINE 310 "Network/Pcap/Base.hsc" #-}
        pcap_compile hdl bpfp filter (fromBool opt) (fromIntegral mask) >>=
            throwPcapIf hdl (== -1)
	pcap_setfilter hdl bpfp >>= throwPcapIf hdl (== -1)
	pcap_freecode bpfp

-- | Compile a filter for use by another program using the Berkeley
-- Packet Filter library.
compileFilter :: Int		-- ^ snapshot length
	      -> Link		-- ^ datalink type
	      -> String	-- ^ filter string
	      -> Bool		-- ^ optimize?
	      -> Word32	-- ^ IPv4 network mask
	      -> IO BpfProgram
compileFilter snaplen link filt opt mask =
    withCString filt $ \filter ->
      allocaBytes ((8)) $ \bpfp -> do
{-# LINE 326 "Network/Pcap/Base.hsc" #-}
	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)

--
-- Find devices
--

newtype DevBuf = DevBuf ()
newtype DevAddr = DevAddr ()


-- | 'lookupDev' returns the name of a device suitable for use with
-- 'openLive' and 'lookupNet'. If you only have one interface, it is
-- the function of choice. If not, see 'findAllDevs'.
lookupDev :: IO String
lookupDev = withErrBuf (== nullPtr) pcap_lookupdev >>= peekCString

-- | 'findAllDevs' returns a list of all the network devices that can
-- be opened by 'openLive'. It returns only those devices that the
-- calling process has sufficient privileges to open, so it may not
-- find every device on the system.
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
{-# LINE 382 "Network/Pcap/Base.hsc" #-}
	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
{-# LINE 389 "Network/Pcap/Base.hsc" #-}
    desc  <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) dbuf
{-# LINE 390 "Network/Pcap/Base.hsc" #-}
    addrs <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) dbuf
{-# LINE 391 "Network/Pcap/Base.hsc" #-}
    flags <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) dbuf
{-# LINE 392 "Network/Pcap/Base.hsc" #-}

    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
{-# LINE 411 "Network/Pcap/Base.hsc" #-}
        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

{-# LINE 423 "Network/Pcap/Base.hsc" #-}
          l <- return ((16)) :: IO CUChar
{-# LINE 424 "Network/Pcap/Base.hsc" #-}

{-# LINE 425 "Network/Pcap/Base.hsc" #-}
          f <- (((\hsc_ptr -> peekByteOff hsc_ptr 0)) sa) :: IO CUChar
{-# LINE 426 "Network/Pcap/Base.hsc" #-}
					
          let off = ((2))
{-# LINE 428 "Network/Pcap/Base.hsc" #-}
              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
{-# LINE 436 "Network/Pcap/Base.hsc" #-}

      when (isNothing addr) $
	   ioError $ userError "Pcap.oneAddr: null address"

      mask <- socka =<< ((\hsc_ptr -> peekByteOff hsc_ptr 8)) abuf
{-# LINE 441 "Network/Pcap/Base.hsc" #-}
      bcast <- socka =<< ((\hsc_ptr -> peekByteOff hsc_ptr 12)) abuf
{-# LINE 442 "Network/Pcap/Base.hsc" #-}
      peer <- socka =<< ((\hsc_ptr -> peekByteOff hsc_ptr 16)) abuf
{-# LINE 443 "Network/Pcap/Base.hsc" #-}

      return PcapAddr { addrSA = fromJust addr
                      , addrMask = mask
                      , addrBcast = bcast
                      , addrPeer = peer
                      }


-- | Return the network address and mask for the specified interface
-- name. Only valid for IPv4. For other protocols, use 'findAllDevs'
-- and search the 'Interface' list for the associated network mask.
lookupNet :: String	-- ^ device name
	  -> 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

--
-- Set or read the device mode (blocking/nonblocking)
--

-- | Set a packet capture descriptor into non-blocking mode if the
-- second argument is 'True', otherwise put it in blocking mode. Note
-- that the packet capture descriptor must have been obtaine from
-- 'openLive'.
--
setNonBlock :: Ptr PcapTag -> Bool -> IO ()
setNonBlock hdl block = do
    withErrBuf (== -1) (pcap_setnonblock hdl (fromBool block))
    return ()

-- | Return the blocking status of the packet capture
-- descriptor. 'True' indicates that the descriptor is
-- non-blocking. Descriptors referring to dump files opened by
-- 'openDump' always return 'False'.
getNonBlock :: Ptr PcapTag -> IO Bool
getNonBlock hdl = toBool `fmap` withErrBuf (== -1) (pcap_getnonblock hdl)

-- | The direction in which packets are to be captured.  See
-- 'setDirection'.
data Direction = InOut          -- ^ incoming and outgoing packets (the default
               | In             -- ^ incoming packets
               | Out            -- ^ outgoing packets
                 deriving (Eq, Show, Read)

-- | Specify the direction in which packets are to be captured.
-- Complete functionality is not necessarily available on all
-- platforms.
setDirection :: Ptr PcapTag -> Direction -> IO ()
setDirection hdl dir = do
    pcap_setdirection hdl (packDirection dir) >>= throwPcapIf hdl (== -1)
    return ()

packDirection :: Direction -> CInt
packDirection In = (1)
{-# LINE 514 "Network/Pcap/Base.hsc" #-}
packDirection Out = (2)
{-# LINE 515 "Network/Pcap/Base.hsc" #-}
packDirection InOut = (0)
{-# LINE 516 "Network/Pcap/Base.hsc" #-}

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

--
-- Error handling
--

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

-- | Send a raw packet through the network interface.
sendPacket :: Ptr PcapTag
           -> Ptr Word8         -- ^ packet data (including link-level header)
           -> Int               -- ^ packet size
           -> 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

-- | the type of the callback function passed to 'dispatch' or 'loop'.
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
{-# LINE 555 "Network/Pcap/Base.hsc" #-}

    s <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) ts
{-# LINE 557 "Network/Pcap/Base.hsc" #-}
    us <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) ts
{-# LINE 558 "Network/Pcap/Base.hsc" #-}
    caplen <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) hdr
{-# LINE 559 "Network/Pcap/Base.hsc" #-}
    len <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) hdr
{-# LINE 560 "Network/Pcap/Base.hsc" #-}

    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


-- | Collect and process packets. The arguments are the packet capture
-- descriptor, the count and a callback function.
--
-- The count is the maximum number of packets to process before
-- returning.  A count of -1 means process all of the packets received
-- in one buffer (if a live capture) or all of the packets in a
-- dump file (if offline).
--
-- The callback function is passed two arguments, a packet header
-- record and a pointer to the packet data (@Ptr Word8@). THe header
-- record contains the number of bytes captured, whcih can be used to
-- marshal the data into a list or array.
--
dispatch :: Ptr PcapTag	-- ^ packet capture descriptor
	 -> Int		-- ^ number of packets to process
	 -> Callback	-- ^ packet processing function
	 -> IO Int	-- ^ number of packets read
dispatch hdl count f = do
    handler <- exportCallback f
    result  <- pcap_dispatch hdl (fromIntegral count) handler nullPtr

    freeHaskellFunPtr handler

    fromIntegral `fmap` throwPcapIf hdl (== -1) result

-- | Similar to 'dispatch', but loop until the number of packets
-- specified by the second argument are read. A negative value loops
-- forever.
-- 
-- This function does not return when a live read tiemout occurs. Use
-- 'dispatch' instead if you wnat to specify a timeout.
loop :: Ptr PcapTag	-- ^ packet capture descriptor
     -> Int		-- ^ number of packet to read
     -> Callback	-- ^ packet processing function
     -> IO Int	-- ^ number of packets read
loop hdl count f = do
    handler <- exportCallback f
    result  <- pcap_loop hdl (fromIntegral count) handler nullPtr

    freeHaskellFunPtr handler

    fromIntegral `fmap` throwPcapIf hdl (== -1) result

-- | Read the next packet (equivalent to calling 'dispatch' with a
-- count of 1).
next :: Ptr PcapTag			-- ^ packet capture descriptor
     -> IO (PktHdr, Ptr Word8)	-- ^ packet header and data of the next packet
next hdl =
    allocaBytes ((16)) $ \chdr -> do
{-# LINE 622 "Network/Pcap/Base.hsc" #-}
      ptr <- pcap_next hdl chdr
      if (ptr == nullPtr)
        then return (PktHdr 0 0 0 0, ptr)
        else do
          hdr <- toPktHdr chdr
          return (hdr, ptr)

-- | Write the packet data given by the second and third arguments to
-- a dump file opened by 'openDead'. 'dump' is designed so it can be
-- easily used as a default callback function by 'dispatch' or 'loop'.
dump :: Ptr PcapDumpTag	-- ^ dump file descriptor
     -> Ptr PktHdr		-- ^ packet header record
     -> Ptr Word8		-- ^ packet data
     -> 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 manipulation
--

-- | Returns the datalink type associated with the given pcap
-- descriptor.
--
datalink :: Ptr PcapTag -> IO Link
datalink hdl = unpackLink `fmap` pcap_datalink hdl


-- | Sets the datalink type for a given pcap descriptor.
--
setDatalink :: Ptr PcapTag -> Link -> IO ()
setDatalink hdl link = do
    pcap_set_datalink hdl (packLink link) >>= throwPcapIf hdl (== -1)
    return ()

-- | List all the datalink types supported by a pcap
-- descriptor. Entries from the resulting list are valid arguments to
-- 'setDatalink'.
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
--

-- | Returns the number of packets received, the number of packets
-- dropped by the packet filter and the number of packets dropped by
-- the interface (before processing by the packet filter).
--
statistics :: Ptr PcapTag -> IO Statistics
statistics hdl =
    allocaBytes ((12)) $ \stats -> do
{-# LINE 699 "Network/Pcap/Base.hsc" #-}
      pcap_stats hdl stats >>= throwPcapIf hdl (== -1)
      recv   <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) stats
{-# LINE 701 "Network/Pcap/Base.hsc" #-}
      drop   <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) stats
{-# LINE 702 "Network/Pcap/Base.hsc" #-}
      ifdrop <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) stats
{-# LINE 703 "Network/Pcap/Base.hsc" #-}

      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 of the library.  The returned pair consists of the major
-- and minor version numbers.
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' returns 'True' if the current dump file uses a
-- different byte order than the one native to the system.
isSwapped :: Ptr PcapTag -> IO Bool
isSwapped hdl = toBool `fmap`  pcap_is_swapped hdl


-- | The snapshot length that was used in the call to 'openLive'.
snapshotLen :: Ptr PcapTag -> IO Int
snapshotLen hdl = fromIntegral `fmap` pcap_snapshot hdl

foreign import ccall pcap_major_version
    :: Ptr PcapTag -> IO CInt