{- Copyright 2016 Markus Ongyerth This file is part of Monky. Monky is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Monky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Monky. If not, see . -} {-# LANGUAGE ForeignFunctionInterface #-} {-| Module : Monky.IP.Raw Description : Lowlevel IP interfaces Maintainer : ongy Stability : experimental Portability : Linux This may change at any time when the main IP module changes. Consider this API unstable! -} module Monky.IP.Raw ( IP(..) , IP4 , IP6 , parseIP , ipFromBS , familyToNum , AddressFamily(..) , getAddrFamily ) where import Data.ByteString (ByteString, useAsCStringLen, packCStringLen) import qualified Data.ByteString as BS (length) import Data.Serialize (decode) import Data.Word (Word32, Word64) import Foreign.C.String (CString, withCString, peekCString) import Foreign.C.Types (CInt(..), CChar) import Foreign.Marshal.Alloc (alloca, allocaBytes) import Foreign.Marshal.Utils (with) import Foreign.Ptr (Ptr, castPtr) import Foreign.Storable (Storable(..)) import System.IO.Unsafe (unsafePerformIO) foreign import ccall "inet_pton" c_pton :: CInt -> CString -> Ptr IP4 -> IO () foreign import ccall "inet_ntop" c_ntop :: CInt -> Ptr a -> Ptr CChar -> Word64 -> IO (Ptr CChar) foreign import ccall "ntohl" ntohl :: Word32 -> Word32 foreign import ccall "htonl" htonl :: Word32 -> Word32 foreign import ccall "memcpy" memcpy :: Ptr a -> Ptr b -> Word64 -> IO () -- |IPv4 addresses newtype IP4 = IP4 Word32 deriving (Eq) -- |IPv6 addresses newtype IP6 = IP6 ByteString deriving (Eq) -- |Datatype for IP addresses, abstracts over v4/v6 data IP = IPv4 IP4 | IPv6 IP6 deriving (Eq) -- |AddressFamilies support for libraries data AddressFamily = AF_UNSPEC | AF_INET | AF_INET6 #include #include #include instance Storable IP4 where sizeOf _ = 4 alignment _ = alignment (undefined :: Word32) -- be compatible with the default implementation hsc chooses peek p = fmap (IP4 . ntohl) . peek $ castPtr p poke p (IP4 w) = poke (castPtr p) $ htonl w instance Storable IP6 where sizeOf _ = 16 alignment _ = alignment (undefined :: Word64) peek p = fmap IP6 $ packCStringLen (castPtr p, 16) poke p (IP6 w) = useAsCStringLen w (\(b, _) -> memcpy p b 16) instance Show IP where show (IPv4 ip) = show ip show (IPv6 ip) = show ip instance Show IP6 where show = showIP6 instance Show IP4 where show = showIP showIPIO :: IP4 -> IO String showIPIO ip = allocaBytes #{const INET_ADDRSTRLEN} (\str -> with ip (\ptr -> c_ntop (familyToNum AF_INET) ptr str #{const INET_ADDRSTRLEN}) >> peekCString str) -- All sideeffects are contained in the IO action and it is deterministic, so we can drop the IO showIP :: IP4 -> String showIP ip = unsafePerformIO (showIPIO ip) {-# NOINLINE showIP #-} parseIPIO :: String -> IO IP4 parseIPIO xs = withCString xs (\str -> do alloca (\ptr -> c_pton (familyToNum AF_INET) str ptr >> peek ptr)) -- All sideeffects are contained in the IO action and it is deterministic, so we can drop the IO -- |Parse an IP4 from a String parseIP :: String -> IP4 parseIP str = unsafePerformIO (parseIPIO str) {-# NOINLINE parseIP #-} -- |Read an IP from a ByteString. The type is determined by the size of the ByteString. ipFromBS :: ByteString -> IP ipFromBS bs = if BS.length bs == 16 then IPv6 (IP6 bs) else case decode bs of (Left err) -> error ("Failed to decode ip: " ++ err) (Right x) -> IPv4 (IP4 x) showIP6IO :: IP6 -> IO String showIP6IO ip = allocaBytes #{const INET_ADDRSTRLEN} (\str -> with ip (\ptr -> c_ntop (familyToNum AF_INET6) ptr str #{const INET6_ADDRSTRLEN}) >> peekCString str) -- All sideeffects are contained in the IO action and it is deterministic, so we can drop the IO showIP6 :: IP6 -> String showIP6 ip = unsafePerformIO (showIP6IO ip) {-# NOINLINE showIP6 #-} -- A few things good to have: -- |Get the number associated with the family address. This is for interfacing with libraries familyToNum :: Num a => AddressFamily -> a familyToNum AF_UNSPEC = 0 familyToNum AF_INET = #{const AF_INET} familyToNum AF_INET6 = #{const AF_INET6} -- |Get the address family for a given ip address getAddrFamily :: IP -> AddressFamily getAddrFamily (IPv6 _) = AF_INET6 getAddrFamily (IPv4 _) = AF_INET