packages feed

hsns-0.5.3: Hsns.hs

-- written by austin seipp, 2007
module Main where
import System.Console.GetOpt
import System.Environment
import System.Process
import qualified Network.Pcap as PCap
import Control.Monad
import Text.Printf
import System.Exit
import Data.Maybe
import System.IO
import Data.List
import Data.Char
import Options
import Foreign




-- our packet output format
data PacketFmt = PacketFmt {
                   ascii       :: String,
                   hexadecimal :: [Word8]
                 }
 deriving (Eq,Show)

initPktFmt a h = 
  PacketFmt {
    ascii       = a,
    hexadecimal = h
}

hsnsVersion = "hsns: the haskell network sniffer, version 0.5.3"

formatData :: PacketFmt -> IO ()
formatData pf = do
  let (x,x') = splitAt 10 a
      (y,y') = splitAt 10 h
  unless (x' == [] && y' == []) $ do
    mapM_ (printf "%02.2x ") (fmtHex y)
    printf "\t\t%s\n" x
    formatData (initPktFmt x' y')

  -- this will be called with x' and y' are finally empty,
  -- but data still remains in our buffer

  -- note: z is the difference between how many characters
  -- we output on a 'complete' line (30) and how many
  -- we're outputting for this 'incomplete' line (every
  -- hex byte is of length 2 plus the trailing space
  when (length x < 10 && length y < 10) $ do
    mapM_ (printf "%02.2x ") (fmtHex y)
    let z = 30 - (3 * (length $ fmtHex y)) 
    fmtIrregular z
    printf "\t%s\n" x
 where
  a = ascii pf
  h = hexadecimal pf
  fmtHex :: [Word8] -> [Int]
  fmtHex bytes = [ (read $ show y) | y <- bytes] :: [Int]
  fmtIrregular :: Int -> IO ()
  fmtIrregular z = do
    printf "\t"
    if ((z-8) > 0) then fmtIrregular (z - 8) else return ()


captcha :: PCap.PktHdr -> Ptr Word8 -> IO ()
captcha pkth datap = do
  a  <- peekArray (fromIntegral (PCap.hdrCaptureLength pkth)) datap
  s  <- return $ map (\x -> if (x >= 32 && x <= 126) then x else 46) a
  s' <- return $ map (\x -> chr (read $ show x)) s
  formatData (initPktFmt s' a)
  printf "\n\n"
  hFlush stdout

starter :: HsnsOpts -> IO ()
starter o = do

  -- options that don't depend on anything else
  when ((help o) == "True") $ putStrLn (usageInfo "hsns: \"filter program\" [OPTIONS]..." options) >> exitWith ExitSuccess 
  when ((version o) == "True") $ (putStrLn hsnsVersion >> (exitWith ExitSuccess))
  when ((buffered o) == "True") $ hSetBuffering stdout LineBuffering

  -- list devices
  devs <- PCap.findAllDevs
  let devnames = map (\i -> (PCap.ifName i)) devs

  when ((iflist o) == "True") $ do
    putStrLn "interfaces:" 
    mapM_ (printf " -- %s\n") devnames
    exitWith ExitSuccess

  -- variables that depend on root access
  let dev      = (interface o)
  net  <- PCap.lookupNet dev
  spy  <- PCap.openLive dev (read $ snarflen o) (if (nopromiscuous o) == "True" then True else False) 100000
  -- set filter
  unless (null (bpf o)) $ do PCap.setFilter spy (bpf o) False (PCap.netMask net)

  -- loop and capture
  PCap.loop spy (read $ count o) captcha
  --print statistics
  s <- PCap.statistics spy
  putStrLn ("Packets recieved: " ++ (show $ PCap.statReceived s))
  putStrLn ("Packets dropped: " ++ (show $ PCap.statDropped s))
  putStrLn ("Packets dropped by interface: " ++ (show $ PCap.statIfaceDropped s))


main = do
  (o,n)  <- parseOpts -- our options and filter in a pair, we assume the filter is the first thing
                      -- on the command line

  starter (create o n)
  return ()
 where
  parseOpts     = (getArgs >>= hsnsOptions)
  create x f =
    let y = construct x in
    Opts {count         = fromMaybe "5"        (lookup "count"     y),
          snarflen      = fromMaybe "68"       (lookup "snarf"     y),
          interface     = fromMaybe "eth0"     (lookup "listen"    y),
          version       = fromMaybe "False"    (lookup "version"   y),
          nopromiscuous = fromMaybe "False"    (lookup "nopromisc" y),
          iflist        = fromMaybe "False"    (lookup "ifList"    y),
          buffered      = fromMaybe "False"    (lookup "buffered"  y),
          help          = fromMaybe "False"    (lookup "help"      y),
          bpf           = if f == [] then "" else (f !! 0)
         }

  construct   f = map construct' f
   where
     construct' x = case x of
       Help          -> ("help","True")
       Count    s    -> ("count",s)
       SnarfLen s    -> ("snarf",s)
       Listen   s    -> ("listen",s)
       NoPromiscuous -> ("nopromisc","True")
       IfList        -> ("ifList","True")
       LineBuffered  -> ("buffered","True")
       Version       -> ("version","True")