packages feed

ip6addr-0.3: src/IP6AddrGen.hs

import System.IO
import System.Exit
import Control.Monad (replicateM,replicateM_)
import System.Environment
import System.Console.GetOpt
import Data.List (groupBy,intercalate)
import System.Random (randomRIO)
import Data.Char (isDigit,intToDigit,toLower,isHexDigit)
import Data.Function (on)

-- IPv6 address prefix validation and random rewrite of trailing zero(s)

tokenizeIPv6Input :: String -> [String]
tokenizeIPv6Input = groupBy ((==) `on` (==':'))

is16bitsToken :: String -> Bool
is16bitsToken s = l < 5 && l > 0 && all isHexDigit s where l = length s

ipv6PrefixToken :: String -> Char
ipv6PrefixToken s
                 | s == ":" = 'c'
                 | is16bitsToken s = '6'
                 | otherwise = '?'
		   
inputIPv6PrefixToInternalRep :: [String] -> String
inputIPv6PrefixToInternalRep = map ipv6PrefixToken

readLast16bitsTokenOfPrefix :: (Monad m) => String -> m (String,Int)
readLast16bitsTokenOfPrefix s =
    let p = span (== '0') $ reverse s in return (reverse(snd p),length (fst p))

prefixRewrite :: String -> IO ()							
prefixRewrite s =
    do
        t <- readLast16bitsTokenOfPrefix s
        putStr $ map toLower $fst t
        g <- genNhex $ snd t
        putStr g
				
-- IPv6 Address random generation

genRandomHex :: IO Char
genRandomHex =
    do
        r <- randomRIO(0,15)::IO Int
        return (toLower $ intToDigit r)			
					
genNhex :: Int -> IO String
genNhex n = replicateM n genRandomHex

n16bitsToken :: Int -> IO ()
n16bitsToken n = replicateM n (genNhex 4) >>= putStr . intercalate ":"

count16bitsToken :: String -> Int
count16bitsToken = foldl (\x y -> if y == '6' then x+1 else x) 0

mkRandomIPv6Addr :: IO ()
mkRandomIPv6Addr = n16bitsToken 8 >> putStr "\n"

mkRandomIPv6AddrWithPrefix :: String -> String -> IO ()
mkRandomIPv6AddrWithPrefix s p =
    do
        let c = count16bitsToken p
        if c < 9 then
            do
                prefixRewrite s
                if c == 8 then putStr "" else putStr ":"
                n16bitsToken (8 - c)
                putStr "\n"
                else putStr "Prefix too long\n"  >> exit

ipv6AddrGen :: Int -> String -> IO ()
ipv6AddrGen n []	=	replicateM_ n mkRandomIPv6Addr 
ipv6AddrGen n s 	=
    let p = inputIPv6PrefixToInternalRep $ tokenizeIPv6Input s in
    if ('?' `notElem` p) && (head p /= 'c') && (last p /= 'c')
    then replicateM_ n (mkRandomIPv6AddrWithPrefix s p)
    else putStr "Bad input for -p option\n"
			
-- GetOpt stuff

data Flag = Help | Version | Count String | Prefix String deriving (Show)

options :: [OptDescr Flag]
options =
          [Option "n" [] (ReqArg Count "count")  "Number of IPv6 adresses to output"
          ,Option "p" [] (ReqArg Prefix "prefix") "An uncompressed IPv6 prefix"
          ,Option "v" [] (NoArg Version) "Show version number"
          ,Option "h" [] (NoArg Help)    "Show usage"]

safeReadN :: String -> String -> IO ()
safeReadN n p =
    if all isDigit n
    then ipv6AddrGen (read n :: Int) p >> exit
    else putStrLn "Bad input for -n option" >> exit

parseArgs :: [String] -> IO ()				
parseArgs args =
    case getOpt RequireOrder options args of
        ([Count n],_,_) -> safeReadN n ""
        ([Prefix p],_,_) -> ipv6AddrGen 1 p >> exit
        ([Count n,Prefix p],_,_) -> safeReadN n p
        ([Prefix p,Count n],_,_) -> safeReadN n p
        ([Version],_,_) -> putStrLn version >> exit
        ([Help],_,_) -> putStrLn (usageInfo help options) >> exit
        (_,_,_) -> putStrLn (usageInfo help options) >> exit

version = "ip6addrgen version 0.2"

help = "Usage: ip6addrgen [-v|-h] | [-n] [-p]\n"

exit = exitWith ExitSuccess

main :: IO ()
main = getArgs >>= parseArgs