packages feed

Win32-dhcp-server 0.2 → 0.2.1

raw patch · 18 files changed

+1678/−1628 lines, 18 filesdep ~Win32

Dependency ranges changed: Win32

Files

ChangeLog view
@@ -1,6 +1,11 @@-0.2
-===
-
-* All functions throw Win32Exception exceptions.
-* strict Text values are favored over String
-* drop dependency on safe
+0.2.1+=====++* bug fix: Internal memory structures were not being freed properly.++0.2+===++* All functions throw Win32Exception exceptions.+* strict Text values are favored over String+* drop dependency on safe
Data/Ip.hs view
@@ -1,81 +1,81 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE OverloadedStrings #-}
-
--- | This module was taken, with modifications, from the
--- <http://hackage.haskell.org/package/maccatcher maccatcher> package.
-module Data.Ip
-    ( Ip ()
-    -- * String conversions
-    , readIp
-    , showIp
-    -- * Octet conversions
-    , fromOctets
-    , toOctets
-    -- * Other conversions
-    , toWord32
-    ) where
-
-import Control.Applicative
-import Control.Monad (unless)
-import Data.Bits
-import Data.Text (Text)
-import qualified Data.Text as T
-import Data.Text.Read
-import Data.Word
-import Foreign
-
-import Utils (fmapL, note)
-
--- |An Ip can be used as an IP address or subnet address.
---
--- A `Show` instance is omitted to avoid confusion. Use `showIp` and `readIp`
--- to convert between `String`s.
-newtype Ip = Ip {unIp :: Word32}
-   deriving (Eq, Ord, Bounded, Storable)
-
--- |Represent an `Ip` as a `String`.
---
--- >>> showIp $ fromOctets 192 168 1 100
--- "192.168.1.100"
-showIp :: Ip -> Text
-showIp ip = T.intercalate "." . map (T.pack . show) $ [a, b, c, d]
-  where (a, b, c, d) = toOctets ip
-
--- |Parse a `Text` value as an `Ip`. The string should be of the form
--- "X.X.X.X" where each 'X' is a decimal value between 0 and 255 inclusive.
---
--- >>> let Right ip = readIp "192.168.1.100"
--- >>> toOctets ip
--- (192, 168, 1, 100)
-readIp :: Text -> Either String Ip
-readIp s = fmapL (\e -> "Error parsing IP address: " ++ e) $ do
-   (a, s2) <- decimal s
-   (b, s3) <- dot s2 >>= decimal
-   (c, s4) <- dot s3 >>= decimal
-   (d, s5) <- dot s4 >>= decimal
-   unless (s5 == "") $ Left "exactly 4 octets were expected."
-   -- Confirm that all digits are in range
-   fromOctets <$> digit a <*> digit b <*> digit c <*> digit d
-  where
-    dot = note "Expected '.' character." . T.stripPrefix "."
-    digit :: Int -> Either String Word8
-    digit x | x < 0 || x > 255 = Left "digit out of range."
-            | otherwise = Right $ fromIntegral x
-
--- |An IP address is 32-bits wide. This function will construct an `Ip` from
--- 4 octets.
-fromOctets :: Word8 -> Word8 -> Word8 -> Word8 -> Ip
-fromOctets a b c d = Ip
-      $ (fromIntegral a `shiftL` 24)
-    .|. (fromIntegral b `shiftL` 16)
-    .|. (fromIntegral c `shiftL` 8)
-    .|. (fromIntegral d)
-
--- |Extract each of the 4 octets from an `Ip`.
-toOctets :: Ip -> (Word8, Word8, Word8, Word8)
-toOctets (Ip word) = (byte 3 word, byte 2 word, byte 1 word, byte 0 word)
-  where
-    byte i w = fromIntegral (w `shiftR` (i * 8))
-
-toWord32 :: Ip -> Word32
-toWord32 = unIp
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}++-- | This module was taken, with modifications, from the+-- <http://hackage.haskell.org/package/maccatcher maccatcher> package.+module Data.Ip+    ( Ip ()+    -- * String conversions+    , readIp+    , showIp+    -- * Octet conversions+    , fromOctets+    , toOctets+    -- * Other conversions+    , toWord32+    ) where++import Control.Applicative+import Control.Monad (unless)+import Data.Bits+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Read+import Data.Word+import Foreign++import Utils (fmapL, note)++-- |An Ip can be used as an IP address or subnet address.+--+-- A `Show` instance is omitted to avoid confusion. Use `showIp` and `readIp`+-- to convert between `String`s.+newtype Ip = Ip {unIp :: Word32}+   deriving (Eq, Ord, Bounded, Storable)++-- |Represent an `Ip` as a `String`.+--+-- >>> showIp $ fromOctets 192 168 1 100+-- "192.168.1.100"+showIp :: Ip -> Text+showIp ip = T.intercalate "." . map (T.pack . show) $ [a, b, c, d]+  where (a, b, c, d) = toOctets ip++-- |Parse a `Text` value as an `Ip`. The string should be of the form+-- "X.X.X.X" where each 'X' is a decimal value between 0 and 255 inclusive.+--+-- >>> let Right ip = readIp "192.168.1.100"+-- >>> toOctets ip+-- (192, 168, 1, 100)+readIp :: Text -> Either String Ip+readIp s = fmapL (\e -> "Error parsing IP address: " ++ e) $ do+   (a, s2) <- decimal s+   (b, s3) <- dot s2 >>= decimal+   (c, s4) <- dot s3 >>= decimal+   (d, s5) <- dot s4 >>= decimal+   unless (s5 == "") $ Left "exactly 4 octets were expected."+   -- Confirm that all digits are in range+   fromOctets <$> digit a <*> digit b <*> digit c <*> digit d+  where+    dot = note "Expected '.' character." . T.stripPrefix "."+    digit :: Int -> Either String Word8+    digit x | x < 0 || x > 255 = Left "digit out of range."+            | otherwise = Right $ fromIntegral x++-- |An IP address is 32-bits wide. This function will construct an `Ip` from+-- 4 octets.+fromOctets :: Word8 -> Word8 -> Word8 -> Word8 -> Ip+fromOctets a b c d = Ip+      $ (fromIntegral a `shiftL` 24)+    .|. (fromIntegral b `shiftL` 16)+    .|. (fromIntegral c `shiftL` 8)+    .|. (fromIntegral d)++-- |Extract each of the 4 octets from an `Ip`.+toOctets :: Ip -> (Word8, Word8, Word8, Word8)+toOctets (Ip word) = (byte 3 word, byte 2 word, byte 1 word, byte 0 word)+  where+    byte i w = fromIntegral (w `shiftR` (i * 8))++toWord32 :: Ip -> Word32+toWord32 = unIp
Data/Mac.hs view
@@ -1,121 +1,121 @@-{-# LANGUAGE OverloadedStrings #-}
-
--- | This module was taken, with modifications, from the
--- <http://hackage.haskell.org/package/maccatcher maccatcher> package.
-module Data.Mac
-    ( Mac ()
-    -- ** String conversions
-    , readMac
-    , showMac
-    -- ** Octet conversions
-    , fromOctets
-    , toOctets
-    -- ** Other conversions
-    , toWord64
-    ) where
-
-import Control.Applicative
-import Control.Monad (unless)
-import Data.Bits
-import Data.Monoid
-import Data.Text (Text)
-import qualified Data.Text as T
-import qualified Data.Text.Lazy as L
-import qualified Data.Text.Lazy.Builder as L
-import qualified Data.Text.Lazy.Builder.Int as L
-import Data.Text.Read
-import Data.Word
-import Foreign
-
-import Utils
-
--- |A Mac is a 6-byte unique identifier used in layer-two network addressing.
--- Its `Storable` instance occupies 6 bytes of memory when `poke`d with the
--- first byte occupying the lowest address, and the last byte occupying the
--- highest address.
---
--- A `Show` instance is omitted to avoid confusion. Use `showMac` and
--- `readMac` to convert between `String`s.
-newtype Mac =  Mac { unMac :: Word64 }
-   deriving (Bounded, Eq, Ord)
-
-instance Storable Mac where
-  sizeOf _ =  6
-  alignment _ =  1
-  peek p =  fromOctets <$> (peek $ castPtr p) <*> peekByteOff p 1
-      <*> peekByteOff p 2 <*> peekByteOff p 3 <*> peekByteOff p 4
-      <*> peekByteOff p 5
-  poke p mac   =  do
-    poke (castPtr p) a
-    pokeByteOff p 1 b
-    pokeByteOff p 2 c
-    pokeByteOff p 3 d
-    pokeByteOff p 4 e
-    pokeByteOff p 5 f
-    where
-      (a, b, c, d, e, f) = toOctets mac
-
-toWord64 :: Mac -> Word64
-toWord64 = unMac
-
--- |Represent a `Mac` as a `String`. The supplied separator will be placed
--- between each octet in the final output.
---
--- >>> showMac "" $ fromOctets 0xa 0xb 0xc 0xd 0xe 0xf
--- "0a0b0c0d0e0f"
---
--- >>> showMac ":" $ fromOctets 0x11 0x22 0x33 0x44 0x55 0x66
--- "11:22:33:44:55:66"
-showMac :: Text -> Mac -> Text
-showMac sep mac = T.intercalate sep . map octet $ [a, b, c, d, e, f]
-  where
-    octet x = pad (2 :: Int) . L.toStrict . L.toLazyText . L.hexadecimal $ x
-    (a, b, c, d, e, f) = toOctets mac
-    pad n str = T.replicate (max 0 (n - T.length str)) "0" <> str
-
--- |Parse a `Text` value as a `Mac`. The string should not use any
--- separators between octets.
---
--- >>> let Right mac = readMac "000102030405"
--- >>> toOctets mac
--- (0, 1, 2, 3, 4, 5)
-readMac :: Text -> Either String Mac
-readMac s = fmapL (\e -> "Error parsing MAC address: " ++ e) $ do
-    (a, s2) <- octet s
-    (b, s3) <- octet s2
-    (c, s4) <- octet s3
-    (d, s5) <- octet s4
-    (e, s6) <- octet s5
-    (f, s7) <- octet s6
-    unless (s7 == "") $ Left "exactly 6 octets were expected."
-    fromOctets <$> digit a <*> digit b <*> digit c
-               <*> digit d <*> digit e <*> digit f
-  where
-    octet :: Text -> Either String (Int, Text)
-    octet s0 = do
-        (a, s2) <- hexadecimal a0
-        unless (s2 == "") $ Left "invalid characters"
-        return (a, rest)
-      where
-        (a0, rest) = T.splitAt 2 s0
-    digit :: Int -> Either String Word8
-    digit x | x < 0 || x > 255 = Left "digit out of range."
-            | otherwise = Right $ fromIntegral x
-
--- |A Mac address is 48-bits wide. This function will construct a `Mac` from
--- 6 octets.
-fromOctets :: Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Mac
-fromOctets a b c d e f = Mac
-      $ (fromIntegral a `shiftL` 40)
-    .|. (fromIntegral b `shiftL` 32)
-    .|. (fromIntegral c `shiftL` 24)
-    .|. (fromIntegral d `shiftL` 16)
-    .|. (fromIntegral e `shiftL` 8)
-    .|. (fromIntegral f)
-
--- |Extract each of the 6 octets from a `Mac`.
-toOctets :: Mac -> (Word8, Word8, Word8, Word8, Word8, Word8)
-toOctets (Mac word) = ( byte 5 word, byte 4 word, byte 3 word
-                      , byte 2 word, byte 1 word, byte 0 word)
-  where
-    byte i w = fromIntegral (w `shiftR` (i * 8))
+{-# LANGUAGE OverloadedStrings #-}++-- | This module was taken, with modifications, from the+-- <http://hackage.haskell.org/package/maccatcher maccatcher> package.+module Data.Mac+    ( Mac ()+    -- ** String conversions+    , readMac+    , showMac+    -- ** Octet conversions+    , fromOctets+    , toOctets+    -- ** Other conversions+    , toWord64+    ) where++import Control.Applicative+import Control.Monad (unless)+import Data.Bits+import Data.Monoid+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Builder as L+import qualified Data.Text.Lazy.Builder.Int as L+import Data.Text.Read+import Data.Word+import Foreign++import Utils++-- |A Mac is a 6-byte unique identifier used in layer-two network addressing.+-- Its `Storable` instance occupies 6 bytes of memory when `poke`d with the+-- first byte occupying the lowest address, and the last byte occupying the+-- highest address.+--+-- A `Show` instance is omitted to avoid confusion. Use `showMac` and+-- `readMac` to convert between `String`s.+newtype Mac =  Mac { unMac :: Word64 }+   deriving (Bounded, Eq, Ord)++instance Storable Mac where+  sizeOf _ =  6+  alignment _ =  1+  peek p =  fromOctets <$> (peek $ castPtr p) <*> peekByteOff p 1+      <*> peekByteOff p 2 <*> peekByteOff p 3 <*> peekByteOff p 4+      <*> peekByteOff p 5+  poke p mac   =  do+    poke (castPtr p) a+    pokeByteOff p 1 b+    pokeByteOff p 2 c+    pokeByteOff p 3 d+    pokeByteOff p 4 e+    pokeByteOff p 5 f+    where+      (a, b, c, d, e, f) = toOctets mac++toWord64 :: Mac -> Word64+toWord64 = unMac++-- |Represent a `Mac` as a `String`. The supplied separator will be placed+-- between each octet in the final output.+--+-- >>> showMac "" $ fromOctets 0xa 0xb 0xc 0xd 0xe 0xf+-- "0a0b0c0d0e0f"+--+-- >>> showMac ":" $ fromOctets 0x11 0x22 0x33 0x44 0x55 0x66+-- "11:22:33:44:55:66"+showMac :: Text -> Mac -> Text+showMac sep mac = T.intercalate sep . map octet $ [a, b, c, d, e, f]+  where+    octet x = pad (2 :: Int) . L.toStrict . L.toLazyText . L.hexadecimal $ x+    (a, b, c, d, e, f) = toOctets mac+    pad n str = T.replicate (max 0 (n - T.length str)) "0" <> str++-- |Parse a `Text` value as a `Mac`. The string should not use any+-- separators between octets.+--+-- >>> let Right mac = readMac "000102030405"+-- >>> toOctets mac+-- (0, 1, 2, 3, 4, 5)+readMac :: Text -> Either String Mac+readMac s = fmapL (\e -> "Error parsing MAC address: " ++ e) $ do+    (a, s2) <- octet s+    (b, s3) <- octet s2+    (c, s4) <- octet s3+    (d, s5) <- octet s4+    (e, s6) <- octet s5+    (f, s7) <- octet s6+    unless (s7 == "") $ Left "exactly 6 octets were expected."+    fromOctets <$> digit a <*> digit b <*> digit c+               <*> digit d <*> digit e <*> digit f+  where+    octet :: Text -> Either String (Int, Text)+    octet s0 = do+        (a, s2) <- hexadecimal a0+        unless (s2 == "") $ Left "invalid characters"+        return (a, rest)+      where+        (a0, rest) = T.splitAt 2 s0+    digit :: Int -> Either String Word8+    digit x | x < 0 || x > 255 = Left "digit out of range."+            | otherwise = Right $ fromIntegral x++-- |A Mac address is 48-bits wide. This function will construct a `Mac` from+-- 6 octets.+fromOctets :: Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Mac+fromOctets a b c d e f = Mac+      $ (fromIntegral a `shiftL` 40)+    .|. (fromIntegral b `shiftL` 32)+    .|. (fromIntegral c `shiftL` 24)+    .|. (fromIntegral d `shiftL` 16)+    .|. (fromIntegral e `shiftL` 8)+    .|. (fromIntegral f)++-- |Extract each of the 6 octets from a `Mac`.+toOctets :: Mac -> (Word8, Word8, Word8, Word8, Word8, Word8)+toOctets (Mac word) = ( byte 5 word, byte 4 word, byte 3 word+                      , byte 2 word, byte 1 word, byte 0 word)+  where+    byte i w = fromIntegral (w `shiftR` (i * 8))
System/Win32/DHCP.hs view
@@ -1,357 +1,358 @@-{-# LANGUAGE OverloadedStrings #-}
-
--- |
--- module: System.Win32.DHCP
--- copyright: (c) Michael Steele, 2014
--- license: BSD3
--- maintainer: mikesteele81@gmail.com
--- stability: experimental
--- portability: Windows
-module System.Win32.DHCP
-    ( DhcpApi ()
-    , loadDHCP
-    -- * General Types
-    , Context (..)
-    , ClientType (..)
-    , DATE_TIME (..)
-    , HOST_INFO (..)
-    , SEARCH_INFO (..)
-    -- * Leases
-    , Client (..)
-    , enumClients
-    , lookupClient
-    , deleteClient
-    -- * Reservations
-    , Mapping (..)
-    , Reservation(..)
-    , addReservation
-    , enumReservations
-    , removeReservation
-    ) where
-
-import Control.Applicative
-import Control.Monad (unless)
-import Data.Char (chr)
-import Data.Text (Text)
-import qualified Data.Text as T
-import qualified Data.Text.Foreign as T
-import Foreign
-import Foreign.C.Types
-
-import System.Win32.Types hiding (withTString, withTStringLen)
-import qualified System.Win32.Error as E
-import qualified System.Win32.Error.Foreign as E
-
-import Data.Ip
-import System.Win32.DHCP.DhcpStructure
-import System.Win32.DHCP.HOST_INFO
-import System.Win32.DHCP.Internal
-import System.Win32.DHCP.Client
-import System.Win32.DHCP.LengthBuffer
-import System.Win32.DHCP.Reservation
-import System.Win32.DHCP.SEARCH_INFO
-import System.Win32.DHCP.SUBNET_CLIENT_INFO_ARRAY_V4
-import System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4
-import System.Win32.DHCP.SUBNET_ELEMENT_INFO_ARRAY_V4
-import System.Win32.DHCP.Types
-
--- | A Context defines which server and scope within that server a command
--- refers to.  Microsoft's DHCP server supports multiple scopes. This allows
--- different configurations to be sent to devices based on their hardware
--- (MAC) address.  Scopes are identified by their network address.
-data Context = Context
-    { -- | The DHCP server management API uses an RPC mechanism to control the
-      -- server.
-      contextServer :: !Text
-    , -- | Scopes are identified by a subnet identifier. This is useful in
-      -- cases where multiple scopes are defined, but still required in the
-      -- common case of a single scope.
-      contextSubnet :: !Ip
-    } deriving (Eq, Ord)
-
--- | Delete an active DHCP lease from the server.
--- The `SEARCH_INFO` argument determines which search criteria
--- to use. Searching by name will delete all active leases
--- with that name. This action corresponds to MSDN's DhcpDeleteClientInfoV4
--- function.
-deleteClient :: DhcpApi
-    -> Text
-    -- ^ String which specifies the IP address or hostname of the DHCP
-    -- server.
-    -> SEARCH_INFO
-    -- ^ Define how to lookup a client to delete. Only deleting based on
-    -- IP addresses have been tested.
-    -> IO ()
-    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32
-    -- call returnes an error condition. MSDN lists the following exceptions,
-    -- but others might be thrown as well:
-    --
-    --   ['E.DhcpReservedClient'] The specified DHCP client is a reserved DHCP
-    --   client.
-    --
-    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server
-    --   database, or the client entry is not present in the database.
-deleteClient api server si =
-    withTString server $ \pserver ->
-    withSearchInfo si $ \psi ->
-    E.failUnlessSuccess "DeleteClientInfo"
-    $ c_DeleteClientInfo api pserver psi
-
--- | Perform a lookup operation for all client lease records within a
--- scope. This action corresponds to MSDN's DhcpEnumSubnetClientsV4 function.
-enumClients :: DhcpApi
-    -> Context
-    -- ^ Specify which server and scope to search for client leases.
-    -> IO [Client]
-    -- ^ The empty list means that no client records exist for the provided
-    -- subnet. This function will throw an 'E.Win32Exception' when the
-    -- internal Win32 call returnes an error condition. MSDN lists the
-    -- following exceptions, but others might be thrown as well:
-    --
-    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server
-    --   database.
-enumClients dhcp (Context server subnet) =
-    enumSubnetClientsV4 dhcp server subnet
-
--- | Search the DHCP server for a lease matching the given search criteria.
--- `Nothing` is returned when no lease was found. This corresponds to MSDN's
--- DhcpGetClientInfoV4 function.
-lookupClient :: DhcpApi
-    -> Text
-    -- ^ According to MSDN this must specify the IP address of the server.
-    -- Other functions (including this one) may or may not also accept
-    -- a Unicode host name.
-    -> SEARCH_INFO
-    -- ^ Define how to lookup a client. Only searching based on an
-    -- IP addresses has been tested.
-    -> IO (Maybe Client)
-    -- ^ A `Nothing` indicates that no client was found. This function will throw an 'E.Win32Exception' when the
-    -- internal Win32 call returnes an error condition. MSDN lists the
-    -- following exceptions, but others might be thrown as well:
-    --
-    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server
-    --   database.
-lookupClient api serverip si =
-    withTString serverip $ \pserverip ->
-    withSearchInfo si $ \psi ->
-    with nullPtr $ \ppclientinfo -> do
-    -- DhcpGetClientInfo takes a structure on the stack.
-    siType <- peek (castPtr psi :: Ptr CInt)
-    siPayload <- peekElemOff (castPtr psi :: Ptr (Ptr ())) 4
-    E.failUnlessSuccess "GetClientInfoV4"
-        $ c_GetClientInfoV4  api pserverip siType siPayload ppclientinfo
-    pclientinfo <- peek ppclientinfo
-    if pclientinfo == nullPtr
-      then return Nothing
-      else do
-        clientinfo <- peekDhcp clientInfo pclientinfo
-        freeDhcp clientInfo (rpcFreeMemory api) pclientinfo
-        poke (castPtr pclientinfo) nullPtr
-        return $ Just clientinfo
-
-addReservation :: DhcpApi -> Context -> Mapping
-    -> IO ()
-    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32
-    -- call returnes an error condition. MSDN lists the following exceptions,
-    -- but others might be thrown as well:
-    --
-    --   ['E.DhcpSubnetNotPresent'] The specified IPv4 subnet does not exist
-    --   on the DHCP server.
-    --
-    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server
-    --   database.
-    --
-    --   ['E.DhcpNotReservedClient'] The specified DHCP client is not a
-    --   reserved client.
-    --
-    --   ['E.DhcpInvalidRange'] The specified IPv4 range does not match an
-    --   existing IPv4 range.
-    --
-    --   ['E.ScopeRangePolicyChangeConflict'] An IP address range is
-    --   configured for a policy in this scope. This operation cannot be
-    --   performed on the scope IP address range until the policy IP address
-    --   range is suitably modified.
-addReservation dhcp (Context server subnet) mapping =
-    addSubnetElementV4 dhcp server subnet
-    $ ReservedIps (Reservation mapping Both)
-
-enumReservations :: DhcpApi -> Context -> IO [Reservation]
-enumReservations dhcp (Context server subnet) = do
-    ex <- enumSubnetElementsV4 dhcp server subnet 2
-    res <- flip mapM ex $ \element -> do
-        case element of
-          ReservedIps res -> return res
-          _ -> error "bug in Win32 API."
-    return res
-
--- | Remove a reservation from the server
-removeReservation :: DhcpApi -> Context -> Mapping
-    -> ClientType
-    -- ^ Specify a DHCP reservation, BOOTP reservation, or both. This is untested.
-    -> Bool
-    -- ^ Specify whether any active leases for the reservation should be
-    -- removed as well.
-    -> IO ()
-    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32
-    -- call returnes an error condition. MSDN lists the following exceptions,
-    -- but others might be thrown as well:
-    --
-    --   ['E.DhcpSubnetNotPresent'] The specified IPv4 subnet does not exist
-    --   on the DHCP server.
-    --
-    --   ['E.DhcpElementCantRemove'] Failure can occur for any number of
-    --   reasons.
-    --
-    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server
-    --   database.
-    --
-    --   ['E.DhcpInvalidRange'] The specified IPv4 range does not match an
-    --   existing IPv4 range.
-    --
-    --   ['E.ScopeRangePolicyChangeConflict'] An IP address range is
-    --   configured for a policy in this scope. This operation cannot be
-    --   performed on the scope IP address range until the policy IP address
-    --   range is suitably modified.
-removeReservation dhcp (Context server subnet) mapping ct force =
-    removeSubnetElementV4 dhcp server subnet
-    (ReservedIps $ Reservation mapping ct) fForce
- where
-   fForce = if force then FullForce else NoForce
-
-enumSubnetClientsV4 :: DhcpApi -> Text -> Ip -> IO [Client]
-enumSubnetClientsV4 dhcp server subnet =
-    withTString server $ \pServer ->
-    -- inout parameter. Supply on successive calls to retreive more elements
-    with 0 $ \pResumeHandle ->
-    alloca $ \pElementsRead ->
-    alloca $ \pElementsTotal ->
-    with nullPtr $ \ppInfoArray -> do
-    -- We have to call enumSubnetElementsV4 at least twice for a sucessfull run.
-    -- Failure to use the returned resumeHandle may result in an internal access
-    -- violation within RPCRT4.dll.
-    let loop acc = do
-            ret <- E.fromDWORD <$> c_EnumSubnetClientsV4 dhcp pServer (toWord32 subnet)
-                                   pResumeHandle 0xFFFFFFFF ppInfoArray
-                                   pElementsRead pElementsTotal
-            unless (elem ret [E.Success, E.MoreData, E.NoMoreItems])
-                $ E.failWith "EnumSubnetClientsV4" ret
-            pInfoArray <- peek ppInfoArray
-            elems <- if pInfoArray == nullPtr
-                     then return []
-                     else do
-                       SUBNET_CLIENT_INFO_ARRAY_V4 (LengthBuffer _ elems) <- peekDhcp clientInfoArray pInfoArray
-                       freeDhcp clientInfoArray (rpcFreeMemory dhcp) pInfoArray
-                       poke ppInfoArray nullPtr
-                       return elems
-            elementsTotal <- peek pElementsTotal
-            elementsRead <- peek pElementsRead
-            if (ret == E.NoMoreItems || (ret == E.Success && elementsTotal == elementsRead))
-              then return (elems:acc)
-              else loop (elems:acc)
-
-    revelemss <- loop []
-    return $ concat $ reverse revelemss
-
-enumSubnetElementsV4 :: DhcpApi -> Text -> Ip -> CInt
-    -> IO [SUBNET_ELEMENT_DATA_V4]
-enumSubnetElementsV4 dhcp server subnet elementType =
-    withTString server $ \pServer ->
-    -- inout parameter. Supply on successive calls to retreive more elements
-    with 0 $ \pResumeHandle ->
-    alloca $ \pElementsRead ->
-    alloca $ \pElementsTotal ->
-    with nullPtr $ \ppEnumElementInfoArray -> do
-    -- We have to call enumSubnetElementsV4 at least twice for a sucessfull run.
-    -- Failure to use the returned resumeHandle may result in an internal access
-    -- violation within RPCRT4.dll.
-    let loop acc = do
-            ret <- E.fromDWORD <$> c_EnumSubnetElementsV4 dhcp pServer (toWord32 subnet) elementType
-                                   pResumeHandle 0xFFFFFFFF ppEnumElementInfoArray
-                                   pElementsRead pElementsTotal
-            unless (elem ret [E.Success, E.MoreData, E.NoMoreItems])
-                $ E.failWith "EnumSubnetElementsV4" ret
-            pEnumElementInfoArray <- peek ppEnumElementInfoArray
-            elems <- if pEnumElementInfoArray == nullPtr
-                     then return []
-                     else do
-                       SUBNET_ELEMENT_INFO_ARRAY_V4 (LengthBuffer _ elems) <- peekDhcp infoArray pEnumElementInfoArray
-                       freeDhcp infoArray (rpcFreeMemory dhcp) pEnumElementInfoArray
-                       poke ppEnumElementInfoArray nullPtr
-                       return elems
-            elementsTotal <- peek pElementsTotal
-            if (ret == E.NoMoreItems || (ret == E.Success && elementsTotal == 0))
-              then return (elems:acc)
-              else loop (elems:acc)
-
-    revelemss <- loop []
-    return $ concat $ reverse revelemss
-
-addSubnetElementV4 :: DhcpApi -> Text -> Ip -> SUBNET_ELEMENT_DATA_V4 -> IO ()
-addSubnetElementV4 dhcp server subnet elementData =
-    withTString server $ \pServer ->
-    withDhcp subnetElementData elementData $ \pElementData ->
-    E.failUnlessSuccess "AddSubnetElementsV4"
-    $ c_AddSubnetElementV4 dhcp pServer (toWord32 subnet) pElementData
-
--- | Remove an IPv4 subnet element from an IPv4 subnet defined on the DHCPv4
---   server.
-removeSubnetElementV4
-    :: DhcpApi
-    -> Text   -- ^ Unicode string that specifies the IP address, such as
-              --   \"123.456.789.012\", or hostname, such as \"server\",
-              --   of the DHCP server.
-    -> Ip -- ^ 'DWORD' value that specifies the IP address of the
-                  --   subnet gateway and uniquely identifies it. As an
-                  --   example, with the above IP address and a subnet mask
-                  --   of 255.255.255.0, the subnet would be 123.456.789.0.
-                  --   This must then be converted to a 'DWORD' value of
-                  --   2,093,683,968.
-    -> SUBNET_ELEMENT_DATA_V4 -- ^ 'DHCP_SUBNET_ELEMENT_DATA_V4' structure
-                              --   that contains information used to find the
-                              --   element that will be removed from subnet
-                              --   specified in SubnetAddress.
-    -> FORCE_FLAG -- ^ DHCP_FORCE_FLAG enumeration value that indicates
-                  --   whether or not the clients affected by the removal of
-                  --   the subnet element should also be deleted.
-                  --
-                  --   Note If the flag is set to DhcpNoForce and this subnet
-                  --   has served an IPv4 address to DHCPv4/BOOTP clients, the
-                  --   IPv4 range is not deleted; conversely, if the flag is
-                  --   set to DhcpFullForce, the IPv4 range is deleted along
-                  --   with the DHCPv4 client lease record on the DHCPv4
-                  --   server.
-    -> IO ()
-    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32
-    -- call returnes an error condition. MSDN lists the following exceptions,
-    -- but others might be thrown as well:
-    --
-    --   ['E.DhcpSubnetNotPresent'] The specified IPv4 subnet does not exist
-    --   on the DHCP server.
-    --
-    --   ['E.DhcpElementCantRemove'] Failure can occur for any number of
-    --   reasons.
-    --
-    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server
-    --   database.
-    --
-    --   ['E.DhcpInvalidRange'] The specified IPv4 range does not match an
-    --   existing IPv4 range.
-    --
-    --   ['E.ScopeRangePolicyChangeConflict'] An IP address range is
-    --   configured for a policy in this scope. This operation cannot be
-    --   performed on the scope IP address range until the policy IP address
-    --   range is suitably modified.
-removeSubnetElementV4 dhcp server subnet elementData forceFlag =
-    withTString server $ \pServer ->
-    withDhcp subnetElementData elementData $ \pElementData ->
-    E.failUnlessSuccess "RemoveSubnetElementV4"
-    $ c_RemoveSubnetElementV4 dhcp pServer (toWord32 subnet) pElementData
-          (fromIntegral . fromEnum $ forceFlag)
-
-withTStringLen :: Text -> (LPTSTR -> T.I16 -> IO a) -> IO a
-withTStringLen text act = T.useAsPtr (T.snoc text (chr 0x0)) $ \ptr len ->
-    act (castPtr ptr) len
-
-withTString :: Text -> (LPTSTR -> IO a) -> IO a
-withTString text act = withTStringLen text $ \ptr _ -> do
-    act ptr
+{-# LANGUAGE OverloadedStrings #-}++-- |+-- module: System.Win32.DHCP+-- copyright: (c) Michael Steele, 2014+-- license: BSD3+-- maintainer: mikesteele81@gmail.com+-- stability: experimental+-- portability: Windows+module System.Win32.DHCP+    ( DhcpApi ()+    , loadDHCP+    -- * General Types+    , Context (..)+    , ClientType (..)+    , DATE_TIME (..)+    , HOST_INFO (..)+    , SEARCH_INFO (..)+    -- * Leases+    , Client (..)+    , enumClients+    , lookupClient+    , deleteClient+    -- * Reservations+    , Mapping (..)+    , Reservation(..)+    , addReservation+    , enumReservations+    , removeReservation+    ) where++import Control.Applicative+import Control.Monad (unless)+import Data.Char (chr)+import Data.Maybe (fromMaybe)+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Foreign as T+import Foreign+import Foreign.C.Types++import System.Win32.Types hiding (withTString, withTStringLen)+import qualified System.Win32.Error as E+import qualified System.Win32.Error.Foreign as E++import Data.Ip+import System.Win32.DHCP.DhcpStructure+import System.Win32.DHCP.HOST_INFO+import System.Win32.DHCP.Internal+import System.Win32.DHCP.Client+import System.Win32.DHCP.LengthBuffer+import System.Win32.DHCP.Reservation+import System.Win32.DHCP.SEARCH_INFO+import System.Win32.DHCP.SUBNET_CLIENT_INFO_ARRAY_V4+import System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4+import System.Win32.DHCP.SUBNET_ELEMENT_INFO_ARRAY_V4+import System.Win32.DHCP.Types+import Utils++-- | A Context defines which server and scope within that server a command+-- refers to.  Microsoft's DHCP server supports multiple scopes. This allows+-- different configurations to be sent to devices based on their hardware+-- (MAC) address.  Scopes are identified by their network address.+data Context = Context+    { -- | The DHCP server management API uses an RPC mechanism to control the+      -- server.+      contextServer :: !Text+    , -- | Scopes are identified by a subnet identifier. This is useful in+      -- cases where multiple scopes are defined, but still required in the+      -- common case of a single scope.+      contextSubnet :: !Ip+    } deriving (Eq, Ord)++-- | Delete an active DHCP lease from the server.+-- The `SEARCH_INFO` argument determines which search criteria+-- to use. Searching by name will delete all active leases+-- with that name. This action corresponds to MSDN's DhcpDeleteClientInfoV4+-- function.+deleteClient :: DhcpApi+    -> Text+    -- ^ String which specifies the IP address or hostname of the DHCP+    -- server.+    -> SEARCH_INFO+    -- ^ Define how to lookup a client to delete. Only deleting based on+    -- IP addresses have been tested.+    -> IO ()+    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32+    -- call returnes an error condition. MSDN lists the following exceptions,+    -- but others might be thrown as well:+    --+    --   ['E.DhcpReservedClient'] The specified DHCP client is a reserved DHCP+    --   client.+    --+    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server+    --   database, or the client entry is not present in the database.+deleteClient api server si =+    withTString server $ \pserver ->+    withSearchInfo si $ \psi ->+    E.failUnlessSuccess "DeleteClientInfo"+    $ c_DeleteClientInfo api pserver psi++-- | Perform a lookup operation for all client lease records within a+-- scope. This action corresponds to MSDN's DhcpEnumSubnetClientsV4 function.+enumClients :: DhcpApi+    -> Context+    -- ^ Specify which server and scope to search for client leases.+    -> IO [Client]+    -- ^ The empty list means that no client records exist for the provided+    -- subnet. This function will throw an 'E.Win32Exception' when the+    -- internal Win32 call returnes an error condition. MSDN lists the+    -- following exceptions, but others might be thrown as well:+    --+    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server+    --   database.+enumClients dhcp (Context server subnet) =+    enumSubnetClientsV4 dhcp server subnet++-- | Search the DHCP server for a lease matching the given search criteria.+-- `Nothing` is returned when no lease was found. This corresponds to MSDN's+-- DhcpGetClientInfoV4 function.+lookupClient :: DhcpApi+    -> Text+    -- ^ According to MSDN this must specify the IP address of the server.+    -- Other functions (including this one) may or may not also accept+    -- a Unicode host name.+    -> SEARCH_INFO+    -- ^ Define how to lookup a client. Only searching based on an+    -- IP addresses has been tested.+    -> IO (Maybe Client)+    -- ^ A `Nothing` indicates that no client was found. This function will throw an 'E.Win32Exception' when the+    -- internal Win32 call returnes an error condition. MSDN lists the+    -- following exceptions, but others might be thrown as well:+    --+    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server+    --   database.+lookupClient api serverip si =+    withTString serverip $ \pserverip ->+    withSearchInfo si $ \psi ->+    with nullPtr $ \ppclientinfo -> do+    -- DhcpGetClientInfo takes a structure on the stack.+    siType <- peek (castPtr psi :: Ptr CInt)+    siPayload <- peekElemOff (castPtr psi :: Ptr (Ptr ())) 4+    E.failUnlessSuccess "GetClientInfoV4"+        $ c_GetClientInfoV4  api pserverip siType siPayload ppclientinfo++    -- Extract client information from out parameter and free the memory+    -- 'Nothing' will be returned the case where GetClientInfoV4 returns a+    -- null pointer.+    scrubWith ppclientinfo $ \pclientinfo -> do+        clientinfo <- peekDhcp clientInfo pclientinfo+        freeDhcp clientInfo (rpcFreeMemory api) pclientinfo+        return clientinfo++addReservation :: DhcpApi -> Context -> Mapping+    -> IO ()+    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32+    -- call returnes an error condition. MSDN lists the following exceptions,+    -- but others might be thrown as well:+    --+    --   ['E.DhcpSubnetNotPresent'] The specified IPv4 subnet does not exist+    --   on the DHCP server.+    --+    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server+    --   database.+    --+    --   ['E.DhcpNotReservedClient'] The specified DHCP client is not a+    --   reserved client.+    --+    --   ['E.DhcpInvalidRange'] The specified IPv4 range does not match an+    --   existing IPv4 range.+    --+    --   ['E.ScopeRangePolicyChangeConflict'] An IP address range is+    --   configured for a policy in this scope. This operation cannot be+    --   performed on the scope IP address range until the policy IP address+    --   range is suitably modified.+addReservation dhcp (Context server subnet) mapping =+    addSubnetElementV4 dhcp server subnet+    $ ReservedIps (Reservation mapping Both)++enumReservations :: DhcpApi -> Context -> IO [Reservation]+enumReservations dhcp (Context server subnet) = do+    ex <- enumSubnetElementsV4 dhcp server subnet 2+    res <- flip mapM ex $ \element -> do+        case element of+          ReservedIps res -> return res+          _ -> error "bug in Win32 API."+    return res++-- | Remove a reservation from the server+removeReservation :: DhcpApi -> Context -> Mapping+    -> ClientType+    -- ^ Specify a DHCP reservation, BOOTP reservation, or both. This is untested.+    -> Bool+    -- ^ Specify whether any active leases for the reservation should be+    -- removed as well.+    -> IO ()+    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32+    -- call returnes an error condition. MSDN lists the following exceptions,+    -- but others might be thrown as well:+    --+    --   ['E.DhcpSubnetNotPresent'] The specified IPv4 subnet does not exist+    --   on the DHCP server.+    --+    --   ['E.DhcpElementCantRemove'] Failure can occur for any number of+    --   reasons.+    --+    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server+    --   database.+    --+    --   ['E.DhcpInvalidRange'] The specified IPv4 range does not match an+    --   existing IPv4 range.+    --+    --   ['E.ScopeRangePolicyChangeConflict'] An IP address range is+    --   configured for a policy in this scope. This operation cannot be+    --   performed on the scope IP address range until the policy IP address+    --   range is suitably modified.+removeReservation dhcp (Context server subnet) mapping ct force =+    removeSubnetElementV4 dhcp server subnet+    (ReservedIps $ Reservation mapping ct) fForce+ where+   fForce = if force then FullForce else NoForce++enumSubnetClientsV4 :: DhcpApi -> Text -> Ip -> IO [Client]+enumSubnetClientsV4 dhcp server subnet =+    withTString server $ \pServer ->+    -- inout parameter. Supply on successive calls to retreive more elements+    with 0 $ \pResumeHandle ->+    alloca $ \pElementsRead ->+    alloca $ \pElementsTotal ->+    with nullPtr $ \ppInfoArray -> do+    -- We have to call enumSubnetElementsV4 at least twice for a sucessfull run.+    -- Failure to use the returned resumeHandle may result in an internal access+    -- violation within RPCRT4.dll.+    let loop acc = do+            ret <- E.fromDWORD <$> c_EnumSubnetClientsV4 dhcp pServer (toWord32 subnet)+                                   pResumeHandle 0xFFFFFFFF ppInfoArray+                                   pElementsRead pElementsTotal+            unless (elem ret [E.Success, E.MoreData, E.NoMoreItems])+                $ E.failWith "EnumSubnetClientsV4" ret++            melems <- scrubWith ppInfoArray $ \pInfoArray -> do+                SUBNET_CLIENT_INFO_ARRAY_V4 (LengthBuffer _ elems) <- peekDhcp clientInfoArray pInfoArray+                freeDhcp clientInfoArray (rpcFreeMemory dhcp) pInfoArray+                return elems+            let elems = fromMaybe [] melems++            if (ret == E.NoMoreItems || ret == E.Success)+              then return (elems:acc)+              else loop (elems:acc)++    revelemss <- loop []+    return $ concat $ reverse revelemss++enumSubnetElementsV4 :: DhcpApi -> Text -> Ip -> CInt+    -> IO [SUBNET_ELEMENT_DATA_V4]+enumSubnetElementsV4 dhcp server subnet elementType =+    withTString server $ \pServer ->+    -- inout parameter. Supply on successive calls to retreive more elements+    with 0 $ \pResumeHandle ->+    alloca $ \pElementsRead ->+    alloca $ \pElementsTotal ->+    with nullPtr $ \ppInfoArray -> do+    -- We have to call enumSubnetElementsV4 at least twice for a sucessfull run.+    -- Failure to use the returned resumeHandle may result in an internal access+    -- violation within RPCRT4.dll.+    let loop acc = do+            ret <- E.fromDWORD <$> c_EnumSubnetElementsV4 dhcp pServer (toWord32 subnet) elementType+                                   pResumeHandle 0xFFFFFFFF ppInfoArray+                                   pElementsRead pElementsTotal+            unless (elem ret [E.Success, E.MoreData, E.NoMoreItems])+                $ E.failWith "EnumSubnetElementsV4" ret++            melems <- scrubWith ppInfoArray $ \pInfoArray -> do+                SUBNET_ELEMENT_INFO_ARRAY_V4 (LengthBuffer _ elems) <- peekDhcp infoArray pInfoArray+                freeDhcp infoArray (rpcFreeMemory dhcp) pInfoArray+                return elems+            let elems = fromMaybe [] melems++            -- Intentionally ignore elementsTotal. Microsoft's documentation+            -- on how this should work doesn't seem right. In my testing+            -- elementsTotal always returns 0x7fffffff until the last loop, at+            -- which time it always matches elementsRead.+            if (ret == E.NoMoreItems || ret == E.Success)+              then return (elems:acc)+              else loop (elems:acc)++    revelemss <- loop []+    return $ concat $ reverse revelemss++addSubnetElementV4 :: DhcpApi -> Text -> Ip -> SUBNET_ELEMENT_DATA_V4 -> IO ()+addSubnetElementV4 dhcp server subnet elementData =+    withTString server $ \pServer ->+    withDhcp subnetElementData elementData $ \pElementData ->+    E.failUnlessSuccess "AddSubnetElementsV4"+    $ c_AddSubnetElementV4 dhcp pServer (toWord32 subnet) pElementData++-- | Remove an IPv4 subnet element from an IPv4 subnet defined on the DHCPv4+--   server.+removeSubnetElementV4+    :: DhcpApi+    -> Text   -- ^ Unicode string that specifies the IP address, such as+              --   \"123.456.789.012\", or hostname, such as \"server\",+              --   of the DHCP server.+    -> Ip -- ^ 'DWORD' value that specifies the IP address of the+                  --   subnet gateway and uniquely identifies it. As an+                  --   example, with the above IP address and a subnet mask+                  --   of 255.255.255.0, the subnet would be 123.456.789.0.+                  --   This must then be converted to a 'DWORD' value of+                  --   2,093,683,968.+    -> SUBNET_ELEMENT_DATA_V4 -- ^ 'DHCP_SUBNET_ELEMENT_DATA_V4' structure+                              --   that contains information used to find the+                              --   element that will be removed from subnet+                              --   specified in SubnetAddress.+    -> FORCE_FLAG -- ^ DHCP_FORCE_FLAG enumeration value that indicates+                  --   whether or not the clients affected by the removal of+                  --   the subnet element should also be deleted.+                  --+                  --   Note If the flag is set to DhcpNoForce and this subnet+                  --   has served an IPv4 address to DHCPv4/BOOTP clients, the+                  --   IPv4 range is not deleted; conversely, if the flag is+                  --   set to DhcpFullForce, the IPv4 range is deleted along+                  --   with the DHCPv4 client lease record on the DHCPv4+                  --   server.+    -> IO ()+    -- ^ This function will throw an 'E.Win32Exception' when the internal Win32+    -- call returnes an error condition. MSDN lists the following exceptions,+    -- but others might be thrown as well:+    --+    --   ['E.DhcpSubnetNotPresent'] The specified IPv4 subnet does not exist+    --   on the DHCP server.+    --+    --   ['E.DhcpElementCantRemove'] Failure can occur for any number of+    --   reasons.+    --+    --   ['E.DhcpJetError'] An error occurred while accessing the DHCP server+    --   database.+    --+    --   ['E.DhcpInvalidRange'] The specified IPv4 range does not match an+    --   existing IPv4 range.+    --+    --   ['E.ScopeRangePolicyChangeConflict'] An IP address range is+    --   configured for a policy in this scope. This operation cannot be+    --   performed on the scope IP address range until the policy IP address+    --   range is suitably modified.+removeSubnetElementV4 dhcp server subnet elementData forceFlag =+    withTString server $ \pServer ->+    withDhcp subnetElementData elementData $ \pElementData ->+    E.failUnlessSuccess "RemoveSubnetElementV4"+    $ c_RemoveSubnetElementV4 dhcp pServer (toWord32 subnet) pElementData+          (fromIntegral . fromEnum $ forceFlag)++withTStringLen :: Text -> (LPTSTR -> T.I16 -> IO a) -> IO a+withTStringLen text act = T.useAsPtr (T.snoc text (chr 0x0)) $ \ptr len ->+    act (castPtr ptr) len++withTString :: Text -> (LPTSTR -> IO a) -> IO a+withTString text act = withTStringLen text $ \ptr _ -> do+    act ptr
System/Win32/DHCP/Client.hs view
@@ -1,117 +1,122 @@-module System.Win32.DHCP.Client
-  ( Client (..)
-  , clientInfo
-  ) where
-
-import Control.Applicative
-import Foreign
-
-import System.Win32.Types
-
-import Data.Ip
-import Data.Mac
-import System.Win32.DHCP.CLIENT_UID
-import System.Win32.DHCP.DhcpStructure
-import System.Win32.DHCP.HOST_INFO
-import System.Win32.DHCP.Types
-import Utils
-
--- | Information about an active lease. This type corresponds to
--- MSDN's DHCP_CLIENT_INFO_V4 structure.
---
--- > typedef struct _DHCP_CLIENT_INFO_V4 {
--- >   DHCP_IP_ADDRESS ClientIpAddress;
--- >   DHCP_IP_MASK    SubnetMask;
--- >   DHCP_CLIENT_UID ClientHardwareAddress;
--- >   LPWSTR          ClientName;
--- >   LPWSTR          ClientComment;
--- >   DATE_TIME       ClientLeaseExpires;
--- >   DHCP_HOST_INFO  OwnerHost;
--- >   BYTE            bClientType;
--- > } DHCP_CLIENT_INFO_V4, *LPDHCP_CLIENT_INFO_V4;
-data Client = Client
-    { clientIp              :: !Ip
-    , clientSubnetMask      :: !Ip
-    , clientHardwareAddress :: !Mac
-    , clientName            :: Maybe String
-    , clientComment         :: Maybe String
-    , clientLeaseExpires    :: !DATE_TIME
-    -- ^ MSDN: The date and time the DHCP client lease will expire, in UTC time.
-    --
-    -- I don't know of any available functions to work with a `DATE_TIME`.
-    , clientOwnerHost       :: !HOST_INFO
-    -- ^ Information on the DHCP server that assigned the lease to the client.
-    , clientType            :: !ClientType
-    }
- 
-clientInfo :: DhcpStructure Client
-clientInfo = DhcpStructure
-    { peekDhcp = peekClientInfoV4
-    , freeDhcp = freeClientInfoV4
-    , withDhcp' = withClientInfo'
-    -- 12-byte alignment because of the inlined HOST_INFO struct
-    , sizeDhcp = 48
-    }
-
-withClientInfo' :: Client -> Ptr Client -> IO r -> IO r
-withClientInfo' c ptr f = 
-    -- The Mac is inlined, so we'll need to copy pmacsrc into pmac
-    withMac     (clientHardwareAddress c) $ \pcuidsrc ->
-    withMaybeTString (clientName c)       $ \pclientName ->
-    withMaybeTString (clientComment c)    $ \pclientComment ->
-    withDhcp' hostInfo   (clientOwnerHost c) (pownerHost ptr) $ do
-    poke (pclientIP ptr)     $ clientIp c
-    poke (psubnetMask ptr)   $ clientSubnetMask c
-    -- we can't use the Storable instance for Mac here.
-    copyBytes (pmac ptr) pcuidsrc $ sizeDhcp clientUid
-    poke (ppclientName ptr)    pclientName
-    poke (ppclientComment ptr) pclientComment
-    poke (pleaseExpires ptr) $ clientLeaseExpires c
-     -- Owner host has already been poked
-    poke (pclientType ptr)   $ clientType c
-    f
-
-peekClientInfoV4 :: Ptr Client -> IO Client
-peekClientInfoV4 ptr = Client
-    <$> (peek $ pclientIP ptr)
-    <*> (peek $ psubnetMask ptr)
-    <*> (macCuid <$> peekDhcp clientUid (pmac ptr))
-    <*> (peek (ppclientName ptr) >>= peekMaybeTString)
-    <*> (peek (ppclientComment ptr) >>= peekMaybeTString)
-    <*> (peek $ pleaseExpires ptr)
-    <*> (peekDhcp hostInfo $ pownerHost ptr)
-    <*> (peek $ pclientType ptr)
-
-freeClientInfoV4 :: (Ptr a -> IO ()) -> Ptr Client -> IO ()
-freeClientInfoV4 freefunc ptr = do
-    freeDhcp clientUid freefunc $ pmac ptr
-    freeptr freefunc $ ppclientName ptr
-    freeptr freefunc $ ppclientComment ptr
-    freeDhcp hostInfo freefunc $ pownerHost ptr
-    freefunc $ castPtr ptr
-
-pclientIP :: Ptr Client -> Ptr Ip
-pclientIP = castPtr
-
-psubnetMask :: Ptr Client -> Ptr Ip
-psubnetMask ptr = castPtr ptr `plusPtr` 4
-
--- The client_uid is inlined into the client_info structure.
-pmac :: Ptr Client -> Ptr CLIENT_UID
-pmac ptr = castPtr ptr `plusPtr` 8
-
-ppclientName :: Ptr Client -> Ptr LPWSTR
-ppclientName ptr = castPtr ptr `plusPtr` 16
-
-ppclientComment :: Ptr Client -> Ptr LPWSTR
-ppclientComment ptr = castPtr ptr `plusPtr` 20
-
-pleaseExpires :: Ptr Client -> Ptr DATE_TIME
-pleaseExpires ptr = castPtr ptr `plusPtr` 24
-
--- the HOST_INFO is inlined into the CLIENT_INFO structure
-pownerHost :: Ptr Client -> Ptr HOST_INFO
-pownerHost ptr = castPtr ptr `plusPtr` 32
-
-pclientType :: Ptr Client -> Ptr ClientType
-pclientType ptr = castPtr ptr `plusPtr` 44
+{-# LANGUAGE RankNTypes #-}++module System.Win32.DHCP.Client+  ( Client (..)+  , clientInfo+  ) where++import Control.Applicative+import Foreign++import System.Win32.Types++import Data.Ip+import Data.Mac+import System.Win32.DHCP.CLIENT_UID+import System.Win32.DHCP.DhcpStructure+import System.Win32.DHCP.HOST_INFO+import System.Win32.DHCP.Types+import Utils++-- | Information about an active lease. This type corresponds to+-- MSDN's DHCP_CLIENT_INFO_V4 structure.+--+-- > typedef struct _DHCP_CLIENT_INFO_V4 {+-- >   DHCP_IP_ADDRESS ClientIpAddress;+-- >   DHCP_IP_MASK    SubnetMask;+-- >   DHCP_CLIENT_UID ClientHardwareAddress;+-- >   LPWSTR          ClientName;+-- >   LPWSTR          ClientComment;+-- >   DATE_TIME       ClientLeaseExpires;+-- >   DHCP_HOST_INFO  OwnerHost;+-- >   BYTE            bClientType;+-- > } DHCP_CLIENT_INFO_V4, *LPDHCP_CLIENT_INFO_V4;+data Client = Client+    { clientIp              :: !Ip+    , clientSubnetMask      :: !Ip+    , clientHardwareAddress :: !Mac+    , clientName            :: Maybe String+    , clientComment         :: Maybe String+    , clientLeaseExpires    :: !DATE_TIME+    -- ^ MSDN: The date and time the DHCP client lease will expire, in UTC+    -- time.+    --+    -- I don't know of any available functions to work with a `DATE_TIME`.+    , clientOwnerHost       :: !HOST_INFO+    -- ^ Information on the DHCP server that assigned the lease to the client.+    , clientType            :: !ClientType+    }+ +clientInfo :: DhcpStructure Client+clientInfo = DhcpStructure+    { peekDhcp = peekClientInfoV4+    , freeDhcpChildren = freeClientInfoV4+    , withDhcp' = withClientInfo'+    -- 12-byte alignment because of the inlined HOST_INFO struct+    , sizeDhcp = 48+    }++withClientInfo' :: Client -> Ptr Client -> IO r -> IO r+withClientInfo' c ptr f = +    -- The Mac is inlined, so we'll need to copy pmacsrc into pmac+    withMac     (clientHardwareAddress c) $ \pcuidsrc ->+    withMaybeTString (clientName c)       $ \pclientName ->+    withMaybeTString (clientComment c)    $ \pclientComment ->+    withDhcp' hostInfo   (clientOwnerHost c) (pownerHost ptr) $ do+    poke (pclientIP ptr)     $ clientIp c+    poke (psubnetMask ptr)   $ clientSubnetMask c+    -- we can't use the Storable instance for Mac here.+    copyBytes (pmac ptr) pcuidsrc $ sizeDhcp clientUid+    poke (ppclientName ptr)    pclientName+    poke (ppclientComment ptr) pclientComment+    poke (pleaseExpires ptr) $ clientLeaseExpires c+     -- Owner host has already been poked+    poke (pclientType ptr)   $ clientType c+    f++peekClientInfoV4 :: Ptr Client -> IO Client+peekClientInfoV4 ptr = Client+    <$> (peek $ pclientIP ptr)+    <*> (peek $ psubnetMask ptr)+    <*> (macCuid <$> peekDhcp clientUid (pmac ptr))+    <*> (peek (ppclientName ptr) >>= peekMaybeTString)+    <*> (peek (ppclientComment ptr) >>= peekMaybeTString)+    <*> (peek $ pleaseExpires ptr)+    <*> (peekDhcp hostInfo $ pownerHost ptr)+    <*> (peek $ pclientType ptr)++freeClientInfoV4 :: (forall a. Ptr a -> IO ()) -> Ptr Client -> IO ()+freeClientInfoV4 freefunc ptr = do+    -- The client_uid is inlined into the client_info structure.+    freeDhcpChildren clientUid freefunc $ pmac ptr+    freefunc `scrubbing_` ppclientName ptr+    freefunc `scrubbing_` ppclientComment ptr+    -- The HOST_INFO structure is inlined within the ClientInfoV4, so we don't+    -- have to free its main pointer.+    freeDhcpChildren hostInfo freefunc $ pownerHost ptr++pclientIP :: Ptr Client -> Ptr Ip+pclientIP = castPtr++psubnetMask :: Ptr Client -> Ptr Ip+psubnetMask ptr = castPtr ptr `plusPtr` 4++-- The client_uid is inlined into the client_info structure.+pmac :: Ptr Client -> Ptr CLIENT_UID+pmac ptr = castPtr ptr `plusPtr` 8++ppclientName :: Ptr Client -> Ptr LPWSTR+ppclientName ptr = castPtr ptr `plusPtr` 16++ppclientComment :: Ptr Client -> Ptr LPWSTR+ppclientComment ptr = castPtr ptr `plusPtr` 20++pleaseExpires :: Ptr Client -> Ptr DATE_TIME+pleaseExpires ptr = castPtr ptr `plusPtr` 24++-- the HOST_INFO is inlined into the CLIENT_INFO structure+pownerHost :: Ptr Client -> Ptr HOST_INFO+pownerHost ptr = castPtr ptr `plusPtr` 32++pclientType :: Ptr Client -> Ptr ClientType+pclientType ptr = castPtr ptr `plusPtr` 44
System/Win32/DHCP/DhcpStructure.hs view
@@ -1,177 +1,187 @@-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE RankNTypes #-}
-
-module System.Win32.DHCP.DhcpStructure where
-
-import Control.Applicative
-import Foreign.Marshal.Alloc (allocaBytes)
-import Foreign.Ptr
-import Foreign.Storable
-
-import Utils (freeptr)
-
--- |Function dictionary for objects used with the DHCP api.
---  * Ability to peek from a pointer to that object.
---  * Ability to properly free an object using Win32's rpcFreeMemory
---    created by the DHCP api.
---  * Ability to use the with* metaphor to temporarily poke an
---    object into C memory, call a continuation on it, and then
---    free the memory from Haskell's heap.
---
--- Extra features made possible by the typeclass
---  * Ability to turn any Storable instance into a DhpcStructure instance
---    by wrapping it into a StorableDhcpStructure.
---  * Ability to peek an array of DHCP structures into a list.
---  * Ability to poke a list of objects into contiguous memory, then
---    call a continuation on that block of memory.
-data DhcpStructure a = DhcpStructure
-    { peekDhcp  :: Ptr a -> IO a
-    , freeDhcp  :: forall x. (Ptr x -> IO ()) -> Ptr a -> IO ()
-    -- |Like `withDhcp`, but without any allocation or cleanup of memory.
-    -- The continuation is not automatically given a pointer because
-    -- the caller should already have it.
-    , withDhcp' :: forall r. a -> Ptr a -> IO r -> IO r
-    , sizeDhcp :: Int
-    }
-
--- |Allocate memory for a structure, poke it into memory, apply
--- a function, and then clean up the memory.
-withDhcp :: DhcpStructure a -> a -> (Ptr a -> IO r) -> IO r
-withDhcp dict a f = allocaBytes (sizeDhcp dict) $ \ptr ->
-    withDhcp' dict a ptr $ f ptr
-
--- |Convert a DhcpStructure so that it can be used with a newtype
--- wrapper.
-newtypeDhcpStructure :: (a -> nt) -> (nt -> a)
-    -> DhcpStructure a -> DhcpStructure nt
-newtypeDhcpStructure wrap unwrap dict =
-    DhcpStructure peekNt freeNt withNt' (sizeDhcp dict)
-  where
-    peekNt ptr = wrap <$> peekDhcp dict (castPtr ptr)
-    freeNt f = freeDhcp dict f . castPtr
-    withNt' a ptr f = withDhcp' dict (unwrap a) (castPtr ptr) f
-
-storableDhcpStructure :: forall a. (Storable a) => DhcpStructure a
-storableDhcpStructure = DhcpStructure
-    { peekDhcp  = peek
-    , freeDhcp  = free
-    , withDhcp' = withStorable'
-    , sizeDhcp  = sizeOf (undefined :: a)
-    }
-  where
-    free freefunc ptr = freeptr freefunc $ castPtr ptr
-    withStorable' x ptr f = poke ptr x >> f
-
-data DhcpArray a = DhcpArray
-    { peekDhcpArray  :: Int -> Ptr a -> IO [a]
-    , freeDhcpArray  :: forall x. (Ptr x -> IO ()) -> Int -> Ptr a -> IO ()
-    , withDhcpArray' :: forall r. [a] -> Ptr a -> IO r -> IO r
-    , dhcpStructure   :: DhcpStructure a
-    }
-
--- |Allocate enough contiguous memory for each element. Recursively
--- free all memory once the supplied function returns.
--- The continuation receives a length argument. This is because
--- the length must be calculated in the course of execution, and will
--- likely be needed again by the caller.
-withDhcpArray ::  DhcpArray a -> [a] -> (Int -> Ptr a -> IO r) -> IO r
-withDhcpArray dict elems f =
-    allocaBytes (stride * size) $ \ptr ->
-    -- `f` is meant to be called on the array as a whole; not individual elements.
-    -- It's supplied its pointer here because we want it called on position 0.
-    withDhcpArray' dict elems ptr $ f size ptr
-  where
-    size = length elems
-    stride = sizeDhcp . dhcpStructure $ dict
-
--- |This dictionary is a default set to "base" other versions on.
--- Scanning through the buffer happens dhcpSize bytes at a time. Memory
--- is freed by calling the freeing function on every element in the buffer.
-baseDhcpArray :: DhcpStructure a -> DhcpArray a
-baseDhcpArray s = DhcpArray
-    { peekDhcpArray = basePeekArray s
-    , freeDhcpArray = baseFreeArray s
-    , withDhcpArray' = baseWithArray' s
-    , dhcpStructure = s
-    }
-
--- |This differs from `baseDhcpArray` in that the entire buffer
--- is freed with a single call to the freeing function.
-basicDhcpArray :: DhcpStructure a -> DhcpArray a
-basicDhcpArray dict = (baseDhcpArray dict)
-    { freeDhcpArray = basicFreeArray dict
-    }
-
-ptrDhcpArray :: DhcpStructure a -> DhcpArray a
-ptrDhcpArray dict = (baseDhcpArray dict)
-    { peekDhcpArray = ptrPeekArray dict
-    , freeDhcpArray = ptrFreeArray dict
-    , withDhcpArray' = ptrWithArray' dict
-    }
-
-basePeekArray :: DhcpStructure a -> Int -> Ptr a -> IO [a]
-basePeekArray dict numElements ptr
-  | numElements <= 0 = return []
-  | otherwise = f (numElements - 1) []
-  where
-    f 0 acc = do
-        e <- peekDhcp dict ptr
-        return (e:acc)
-    f n acc = do
-        e <- peekDhcp dict (ptr `plusPtr` (sizeDhcp dict * n))
-        f (n - 1) (e:acc)
-
-baseFreeArray :: DhcpStructure a -> (Ptr x -> IO ()) -> Int -> Ptr a -> IO ()
-baseFreeArray dict freefunc len ptr = f (len - 1)
-  where
-    f 0 = freeDhcp dict freefunc ptr
-    f n = do
-        freeDhcp dict freefunc $ ptr `plusPtr` (n * sizeDhcp dict)
-        f (n - 1)
-
-baseWithArray' :: DhcpStructure a -> [a] -> Ptr a -> IO r -> IO r
-baseWithArray' _    []     _   f = f
-baseWithArray' dict (e:es) ptr f =
-    -- We're not concerned with the individual element.
-    withDhcp' dict e ptr
-    $ baseWithArray' dict es (ptr `plusPtr` sizeDhcp dict) f
-
-basicFreeArray :: DhcpStructure a -> (Ptr x -> IO ()) -> Int -> Ptr a -> IO ()
-basicFreeArray dict freefunc _ ptr = freeDhcp dict freefunc ptr
-
-ptrPeekArray :: DhcpStructure a -> Int -> Ptr a -> IO [a]
-ptrPeekArray dict numElements ptr
-  | numElements <= 0 = return []
-  | otherwise = f (numElements - 1) []
-  where
-    --Each element is a pointer to the real data
-    pptr = castPtr ptr
-    f 0 acc = do
-        e <- peek pptr >>= peekDhcp dict
-        return (e:acc)
-    f n acc = do
-        pe <- peek $ pptr `plusPtr` (sizeOf nullPtr * n)
-        e <- peekDhcp dict pe
-        f (n - 1) (e:acc)
-
-ptrFreeArray :: DhcpStructure a -> (Ptr x -> IO ()) -> Int -> Ptr a -> IO ()
-ptrFreeArray dict freefunc len ptr = f (len - 1)
-  where
-    --Each element is a pointer to the real data
-    pptr = castPtr ptr
-    f 0 = peek pptr >>= freeDhcp dict freefunc
-    f n = do
-        pElem <- peek $ pptr `plusPtr` (sizeOf nullPtr * n)
-        freeDhcp dict freefunc pElem
-        f (n - 1)
-
-ptrWithArray' :: DhcpStructure a -> [a] -> Ptr a -> IO r -> IO r
-ptrWithArray' _    []     _   f = f
-ptrWithArray' dict (e:es) ptr f =
-    -- We're not concerned with the individual element.
-    withDhcp dict e $ \pe -> do
-    poke pptr pe
-    ptrWithArray' dict es (ptr `plusPtr` sizeOf nullPtr) f
-  where
-    --Each element is a pointer to the real data
-    pptr = castPtr ptr
+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RankNTypes #-}++module System.Win32.DHCP.DhcpStructure where++import Control.Applicative+import Control.Monad (when)+import Foreign.Marshal.Alloc (allocaBytes)+import Foreign.Ptr+import Foreign.Storable++import Utils++-- |Function dictionary for objects used with the DHCP api.+--  * Ability to peek from a pointer to that object.+--  * Ability to properly free an object using Win32's rpcFreeMemory+--    created by the DHCP api.+--  * Ability to use the with* metaphor to temporarily poke an+--    object into C memory, call a continuation on it, and then+--    free the memory from Haskell's heap.+--+-- Extra features made possible by the typeclass+--  * Ability to turn any Storable instance into a DhpcStructure instance+--    by wrapping it into a StorableDhcpStructure.+--  * Ability to peek an array of DHCP structures into a list.+--  * Ability to poke a list of objects into contiguous memory, then+--    call a continuation on that block of memory.+data DhcpStructure a = DhcpStructure+    { peekDhcp  :: Ptr a -> IO a+    -- |Cleaning up memory is the responsibility of the client of this+    -- library. Most out parameters return compound structures which need to+    -- be recursively navigated, freeing all children, before freeing the main+    -- pointer.+    --+    -- This function only frees child objects without freeing the pointer+    -- itself. It's necessary because some structures contained inline+    -- structures instead of the usual pointer. A separate 'freeDhcp' function+    -- will call this one before freeing its supplied pointer.+    , freeDhcpChildren  :: (forall x. Ptr x -> IO ()) -> Ptr a -> IO ()+    -- |Like `withDhcp`, but without any allocation or cleanup of memory.+    -- The continuation is not automatically given a pointer because+    -- the caller should already have it.+    , withDhcp' :: forall r. a -> Ptr a -> IO r -> IO r+    , sizeDhcp :: Int+    }++freeDhcp :: DhcpStructure a -> (forall x. Ptr x -> IO ()) -> Ptr a -> IO ()+freeDhcp dict free ptr = do+    freeDhcpChildren dict free ptr+    free ptr++-- |Allocate memory for a structure, poke it into memory, apply+-- a function, and then clean up the memory.+withDhcp :: DhcpStructure a -> a -> (Ptr a -> IO r) -> IO r+withDhcp dict a f = allocaBytes (sizeDhcp dict) $ \ptr ->+    withDhcp' dict a ptr $ f ptr++-- |Convert a DhcpStructure so that it can be used with a newtype+-- wrapper.+newtypeDhcpStructure :: (a -> nt) -> (nt -> a)+    -> DhcpStructure a -> DhcpStructure nt+newtypeDhcpStructure wrap unwrap dict =+    DhcpStructure peekNt freeNt withNt' (sizeDhcp dict)+  where+    peekNt ptr = wrap <$> peekDhcp dict (castPtr ptr)+    freeNt :: (forall x. Ptr x -> IO ()) -> Ptr a -> IO ()+    freeNt f ptr = freeDhcpChildren dict f (castPtr ptr)+    withNt' a ptr f = withDhcp' dict (unwrap a) (castPtr ptr) f++-- |This is used in cases like 'CLIENT_UID' where we want to treat it like+-- a 'LengthArray' but individual elements of the array are simple values.+-- We reuse the 'Storable' instances peek and poke functions.+storableDhcpStructure :: forall a. (Storable a) => DhcpStructure a+storableDhcpStructure = DhcpStructure+    { peekDhcp  = peek+    , freeDhcpChildren  = \freeFunc ptr -> freeFunc ptr+    , withDhcp' = withStorable'+    , sizeDhcp  = sizeOf (undefined :: a)+    }+  where+    withStorable' x ptr f = poke ptr x >> f++data DhcpArray a = DhcpArray+    { peekDhcpArray  :: Int -> Ptr a -> IO [a]+    , freeDhcpArray  :: (forall x. Ptr x -> IO ()) -> Int -> Ptr a -> IO ()+    , withDhcpArray' :: forall r. [a] -> Ptr a -> IO r -> IO r+    , dhcpStructure   :: DhcpStructure a+    }++-- |Allocate enough contiguous memory for each element. Recursively+-- free all memory once the supplied function returns.+-- The continuation receives a length argument. This is because+-- the length must be calculated in the course of execution, and will+-- likely be needed again by the caller.+withDhcpArray ::  DhcpArray a -> [a] -> (Int -> Ptr a -> IO r) -> IO r+withDhcpArray dict elems f =+    allocaBytes (stride * size) $ \ptr ->+    -- `f` is meant to be called on the array as a whole; not individual elements.+    -- It's supplied its pointer here because we want it called on position 0.+    withDhcpArray' dict elems ptr $ f size ptr+  where+    size = length elems+    stride = sizeDhcp . dhcpStructure $ dict++-- |This dictionary is a default set to "base" other versions on.+-- Scanning through the buffer happens dhcpSize bytes at a time. Memory+-- is freed by calling the freeing function on every element in the buffer.+baseDhcpArray :: DhcpStructure a -> DhcpArray a+baseDhcpArray s = DhcpArray+    { peekDhcpArray = basePeekArray s+    , freeDhcpArray = baseFreeArray s+    , withDhcpArray' = baseWithArray' s+    , dhcpStructure = s+    }++-- |This differs from `baseDhcpArray` in that the entire buffer+-- is freed with a single call to the freeing function.+basicDhcpArray :: DhcpStructure a -> DhcpArray a+basicDhcpArray dict = (baseDhcpArray dict)+    { freeDhcpArray = basicFreeArray dict+    }++ptrDhcpArray :: DhcpStructure a -> DhcpArray a+ptrDhcpArray dict = (baseDhcpArray dict)+    { peekDhcpArray = ptrPeekArray dict+    , freeDhcpArray = ptrFreeArray dict+    , withDhcpArray' = ptrWithArray' dict+    }++basePeekArray :: DhcpStructure a -> Int -> Ptr a -> IO [a]+basePeekArray dict len ptr0 = mapM (peekDhcp dict) ptrs+  where+    ptrs = take len . iterate (`plusPtr` sizeDhcp dict) $ ptr0++-- |Elements are arranged end to end in a buffer. The buffer is freed+-- at once after each element's children are freed.+baseFreeArray :: DhcpStructure a+    -> (forall x. Ptr x -> IO ()) -> Int -> Ptr a -> IO ()+baseFreeArray dict freefunc len ptr+  | len <= 0 = return ()+  | otherwise = do+    f (len - 1)+    freefunc ptr+  where+    f 0 = freeDhcpChildren dict freefunc ptr+    f n = do+        freeDhcpChildren dict freefunc $ ptr `plusPtr` (n * sizeDhcp dict)+        f (n - 1)++baseWithArray' :: DhcpStructure a -> [a] -> Ptr a -> IO r -> IO r+baseWithArray' _    []     _   f = f+baseWithArray' dict (e:es) ptr f =+    -- We're not concerned with the individual element.+    withDhcp' dict e ptr+    $ baseWithArray' dict es (ptr `plusPtr` sizeDhcp dict) f++basicFreeArray :: DhcpStructure a -> (forall x. Ptr x -> IO ())+    -> Int -> Ptr a -> IO ()+basicFreeArray dict freefunc _ ptr = freeDhcp dict freefunc ptr++ptrPeekArray :: DhcpStructure a -> Int -> Ptr a -> IO [a]+ptrPeekArray dict len ptr = mapM peekElement pptrs+  where+    --Each element is a pointer to the real data+    pptrs = take len . iterate (`plusPtr` sizeOf nullPtr) $ castPtr ptr+    peekElement pptr = peek pptr >>= peekDhcp dict++ptrFreeArray :: DhcpStructure a+    -> (forall x. Ptr x -> IO ()) -> Int -> Ptr a -> IO ()+ptrFreeArray dict freefunc len ptr = do+    mapM (freeDhcp dict freefunc `scrubbing_`) pptrs+    -- Len may very well be 0 in which case there's really nothing to free.+    when (len > 0) $ freefunc ptr+  where+    --Each element is a pointer to the real data+    pptrs = take len . iterate (`plusPtr` sizeOf nullPtr) $ castPtr ptr++ptrWithArray' :: DhcpStructure a -> [a] -> Ptr a -> IO r -> IO r+ptrWithArray' _    []     _   f = f+ptrWithArray' dict (e:es) ptr f =+    -- We're not concerned with the individual element.+    withDhcp dict e $ \pe -> do+    poke pptr pe+    ptrWithArray' dict es (ptr `plusPtr` sizeOf nullPtr) f+  where+    --Each element is a pointer to the real data+    pptr = castPtr ptr
System/Win32/DHCP/HOST_INFO.hs view
@@ -1,56 +1,58 @@-module System.Win32.DHCP.HOST_INFO
-    ( HOST_INFO (..)
-    , hostInfo
-    ) where
-
-import Control.Applicative
-import Foreign
-
-import System.Win32.Types
-
-import Data.Ip
-import System.Win32.DHCP.DhcpStructure
-import Utils
-
--- typedef struct _DHCP_HOST_INFO {
---   DHCP_IP_ADDRESS IpAddress;
---   LPWSTR          NetBiosName;
---   LPWSTR          HostName;
--- } DHCP_HOST_INFO, *LPDHCP_HOST_INFO;
-data HOST_INFO = HOST_INFO !Ip (Maybe String) (Maybe String)
-
-
-hostInfo :: DhcpStructure HOST_INFO
-hostInfo = DhcpStructure
-    { peekDhcp = peekHostInfo
-    , freeDhcp = freeHostInfoChildren
-    , withDhcp' = withHostInfo'
-    , sizeDhcp = 12
-    }
-
-peekHostInfo :: Ptr HOST_INFO -> IO HOST_INFO
-peekHostInfo ptr = do
-    pnetBiosName <- peek $ ppnetBiosName ptr
-    phostName <- peek $ pphostName ptr
-    HOST_INFO <$> peek (castPtr ptr) <*> peekMaybeTString pnetBiosName
-              <*> peekMaybeTString phostName
-
-withHostInfo' :: HOST_INFO -> Ptr HOST_INFO -> IO r -> IO r
-withHostInfo' (HOST_INFO ip netbiosname hostname) ptr f =
-    withMaybeTString netbiosname $ \pnetbiosname ->
-    withMaybeTString hostname    $ \phostname -> do
-    poke (castPtr ptr) ip
-    pokeByteOff (castPtr ptr) 4 pnetbiosname
-    pokeByteOff (castPtr ptr) 8 phostname
-    f
-
-freeHostInfoChildren :: (Ptr a -> IO ()) -> Ptr HOST_INFO -> IO ()
-freeHostInfoChildren rpcfree phi = do
-    freeptr rpcfree $ ppnetBiosName phi
-    freeptr rpcfree $ pphostName phi
-
-ppnetBiosName :: Ptr HOST_INFO -> Ptr LPWSTR
-ppnetBiosName ptr = castPtr ptr `plusPtr` 4
-
-pphostName :: Ptr HOST_INFO -> Ptr LPWSTR
-pphostName ptr = castPtr ptr `plusPtr` 8
+{-# LANGUAGE RankNTypes #-}++module System.Win32.DHCP.HOST_INFO+    ( HOST_INFO (..)+    , hostInfo+    ) where++import Control.Applicative+import Foreign++import System.Win32.Types++import Data.Ip+import System.Win32.DHCP.DhcpStructure+import Utils++-- typedef struct _DHCP_HOST_INFO {+--   DHCP_IP_ADDRESS IpAddress;+--   LPWSTR          NetBiosName;+--   LPWSTR          HostName;+-- } DHCP_HOST_INFO, *LPDHCP_HOST_INFO;+data HOST_INFO = HOST_INFO !Ip (Maybe String) (Maybe String)+++hostInfo :: DhcpStructure HOST_INFO+hostInfo = DhcpStructure+    { peekDhcp = peekHostInfo+    , freeDhcpChildren = freeHostInfoChildren+    , withDhcp' = withHostInfo'+    , sizeDhcp = 12+    }++peekHostInfo :: Ptr HOST_INFO -> IO HOST_INFO+peekHostInfo ptr = do+    pnetBiosName <- peek $ ppnetBiosName ptr+    phostName <- peek $ pphostName ptr+    HOST_INFO <$> peek (castPtr ptr) <*> peekMaybeTString pnetBiosName+              <*> peekMaybeTString phostName++withHostInfo' :: HOST_INFO -> Ptr HOST_INFO -> IO r -> IO r+withHostInfo' (HOST_INFO ip netbiosname hostname) ptr f =+    withMaybeTString netbiosname $ \pnetbiosname ->+    withMaybeTString hostname    $ \phostname -> do+    poke (castPtr ptr) ip+    pokeByteOff (castPtr ptr) 4 pnetbiosname+    pokeByteOff (castPtr ptr) 8 phostname+    f++freeHostInfoChildren :: (forall x. Ptr x -> IO ()) -> Ptr HOST_INFO -> IO ()+freeHostInfoChildren rpcfree phi = do+    rpcfree `scrubbing_` ppnetBiosName phi+    rpcfree `scrubbing_` pphostName phi++ppnetBiosName :: Ptr HOST_INFO -> Ptr LPWSTR+ppnetBiosName ptr = castPtr ptr `plusPtr` 4++pphostName :: Ptr HOST_INFO -> Ptr LPWSTR+pphostName ptr = castPtr ptr `plusPtr` 8
System/Win32/DHCP/IP_CLUSTER.hs view
@@ -1,25 +1,25 @@-module System.Win32.DHCP.IP_CLUSTER
-    ( IP_CLUSTER (..)
-    ) where
-
-import Control.Applicative
-import Foreign
-
-import System.Win32.Types
-
-import Data.Ip
-
--- typedef struct _DHCP_IP_CLUSTER {
---   DHCP_IP_ADDRESS ClusterAddress;
---   DWORD           ClusterMask;
--- } DHCP_IP_CLUSTER, *LPDHCP_IP_CLUSTER;
-data IP_CLUSTER = IP_CLUSTER !Ip !DWORD
-
-instance Storable IP_CLUSTER where
-  sizeOf _ = 8
-  alignment _ = 1
-  peek ptr = IP_CLUSTER <$> (peek . castPtr $ ptr)
-      <*> (castPtr ptr `peekByteOff` 4)
-  poke ptr (IP_CLUSTER ip cmask) = do
-      poke (castPtr ptr) ip
-      pokeByteOff (castPtr ptr) 4 cmask
+module System.Win32.DHCP.IP_CLUSTER+    ( IP_CLUSTER (..)+    ) where++import Control.Applicative+import Foreign++import System.Win32.Types++import Data.Ip++-- typedef struct _DHCP_IP_CLUSTER {+--   DHCP_IP_ADDRESS ClusterAddress;+--   DWORD           ClusterMask;+-- } DHCP_IP_CLUSTER, *LPDHCP_IP_CLUSTER;+data IP_CLUSTER = IP_CLUSTER !Ip !DWORD++instance Storable IP_CLUSTER where+  sizeOf _ = 8+  alignment _ = 1+  peek ptr = IP_CLUSTER <$> (peek . castPtr $ ptr)+      <*> (castPtr ptr `peekByteOff` 4)+  poke ptr (IP_CLUSTER ip cmask) = do+      poke (castPtr ptr) ip+      pokeByteOff (castPtr ptr) 4 cmask
System/Win32/DHCP/IP_RANGE.hs view
@@ -1,22 +1,22 @@-module System.Win32.DHCP.IP_RANGE
-    ( IP_RANGE (..)
-    ) where
-
-import Control.Applicative
-import Foreign
-
-import Data.Ip
-
-data IP_RANGE = IP_RANGE !Ip !Ip
-
-instance Storable IP_RANGE where
-  sizeOf _ = 8
-  alignment _ = 1
-  peek ptr = IP_RANGE
-      <$> (peek . castPtr) ptr
-      <*> castPtr ptr `peekByteOff` 4
-  poke ptr (IP_RANGE start end) = do
-      pokeElemOff addrPtr 0 start
-      pokeElemOff addrPtr 1 end
-    where
-      addrPtr = castPtr ptr :: Ptr Ip
+module System.Win32.DHCP.IP_RANGE+    ( IP_RANGE (..)+    ) where++import Control.Applicative+import Foreign++import Data.Ip++data IP_RANGE = IP_RANGE !Ip !Ip++instance Storable IP_RANGE where+  sizeOf _ = 8+  alignment _ = 1+  peek ptr = IP_RANGE+      <$> (peek . castPtr) ptr+      <*> castPtr ptr `peekByteOff` 4+  poke ptr (IP_RANGE start end) = do+      pokeElemOff addrPtr 0 start+      pokeElemOff addrPtr 1 end+    where+      addrPtr = castPtr ptr :: Ptr Ip
System/Win32/DHCP/Internal.hs view
@@ -1,151 +1,151 @@-{-# LANGUAGE ForeignFunctionInterface #-}
-
-module System.Win32.DHCP.Internal where
-
-import Foreign
-import Foreign.C
-import System.Win32.DLL
-import System.Win32.Types
-
-import System.Win32.DHCP.Client
-import System.Win32.DHCP.SEARCH_INFO
-import System.Win32.DHCP.SUBNET_CLIENT_INFO_ARRAY_V4
-import System.Win32.DHCP.SUBNET_ELEMENT_INFO_ARRAY_V4
-import System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4
-import System.Win32.DHCP.Types
-
--- DWORD DHCP_API_FUNCTION DhcpDeleteClientInfo(
---   _In_  DHCP_CONST WCHAR *ServerIpAddress,
---   _In_  DHCP_CONST DHCP_SEARCH_INFO *ClientInfo
--- );
-type DeleteClientInfo = CWString -> Ptr SEARCH_INFO -> IO DWORD
-foreign import stdcall "dynamic"
-    mkDeleteClientInfo :: FunPtr DeleteClientInfo -> DeleteClientInfo
-
--- DWORD DHCP_API_FUNCTION DhcpEnumSubnetClientsV4(
---   _In_     DHCP_CONST WCHAR *ServerIpAddress,
---   _In_     DHCP_IP_ADDRESS SubnetAddress,
---   _Inout_  DHCP_RESUME_HANDLE *ResumeHandle,
---   _In_     DWORD PreferredMaximum,
---   _Out_    LPDHCP_CLIENT_INFO_ARRAY_V4 *ClientInfo,
---   _Out_    DWORD *ClientsRead,
---   _Out_    DWORD *ClientsTotal
--- );
-type EnumSubnetClientsV4 = CWString -> Word32 -> Ptr RESUME_HANDLE
-    -> DWORD -> Ptr (Ptr SUBNET_CLIENT_INFO_ARRAY_V4) -> Ptr DWORD
-    -> Ptr DWORD -> IO DWORD
-foreign import stdcall "dynamic"
-    mkEnumSubnetClientsV4 :: FunPtr EnumSubnetClientsV4
-        -> EnumSubnetClientsV4
-
--- DWORD DHCP_API_FUNCTION DhcpEnumSubnetElementsV4(
---   __in     DHCP_CONST WCHAR *ServerIpAddress,
---   __in     DHCP_IP_ADDRESS SubnetAddress,
---   __in     DHCP_SUBNET_ELEMENT_TYPE EnumElementType,
---   __inout  DHCP_RESUME_HANDLE *ResumeHandle,
---   __in     DWORD PreferredMaximum,
---   __out    LPDHCP_SUBNET_ELEMENT_INFO_ARRAY_V4 *EnumElementInfo,
---  __out    DWORD *ElementsRead,
---  __out    DWORD *ElementsTotal
--- );
-type EnumSubnetElementsV4 = CWString -> Word32 -> CInt -> Ptr RESUME_HANDLE
-    -> DWORD -> Ptr (Ptr SUBNET_ELEMENT_INFO_ARRAY_V4) -> Ptr DWORD
-    -> Ptr DWORD -> IO DWORD
-foreign import stdcall "dynamic"
-    mkEnumSubnetElementsV4 :: FunPtr EnumSubnetElementsV4
-        -> EnumSubnetElementsV4
-
--- DWORD DHCP_API_FUNCTION DhcpGetClientInfoV4(
---   _In_   DHCP_CONST WCHAR ServerIpAddress,
---   _In_   DHCP_CONST DHCP_SEARCH_INFO SearchInfo,
---   _Out_  LPDHCP_CLIENT_INFO_V4 *ClientInfo
--- );
-type GetClientInfoV4 = CWString -> SEARCH_INFO_TYPE -> Ptr ()
-    -> Ptr (Ptr Client) -> IO DWORD
-foreign import stdcall "dynamic"
-    mkGetClientInfoV4 :: FunPtr GetClientInfoV4 -> GetClientInfoV4
-
--- | Foreign wrapper to DhcpRemoveSubnetElementV4.
--- 
---   The DhcpRemoveSubnetElementV4 function removes an IPv4 subnet element
---   from an IPv4 subnet defined on the DHCPv4 server. The function extends
---   the functionality provided by DhcpRemoveSubnetElement by allowing the
---   specification of a subnet that contains client type (DHCP or BOOTP)
---   information.
--- 
---   See <http://msdn.microsoft.com/en-us/library/ee309533(v=VS.85).aspx> for
---   official documentation.
--- 
--- @
---   DWORD DHCP_API_FUNCTION DhcpRemoveSubnetElementV4(
---     __in  DHCP_CONST WCHAR *ServerIpAddress,
---     __in  DHCP_IP_ADDRESS SubnetAddress,
---     __in  DHCP_CONST DHCP_SUBNET_ELEMENT_DATA_V4 *RemoveElementInfo,
---     __in  DHCP_FORCE_FLAG ForceFlag
---   );
--- @
-type RemoveSubnetElementV4 = CWString -> Word32 -> Ptr SUBNET_ELEMENT_DATA_V4
-    -> CInt -> IO DWORD
-foreign import stdcall "dynamic"
-    mkRemoveSubnetElementV4 :: FunPtr RemoveSubnetElementV4
-        -> RemoveSubnetElementV4
-
--- DWORD DhcpAddSubnetElementV4(
---   __in  DHCP_CONST WCHAR *ServerIpAddress,
---   __in  DHCP_IP_ADDRESS SubnetAddress,
---   __in  DHCP_CONST DHCP_SUBNET_ELEMENT_DATA_V4 AddElementInfo
--- );
-type AddSubnetElementV4 = CWString -> Word32 -> Ptr SUBNET_ELEMENT_DATA_V4
-    -> IO DWORD
-foreign import stdcall "dynamic"
-    mkAddSubnetElementV4 :: FunPtr AddSubnetElementV4 -> AddSubnetElementV4
-
--- VOID DHCP_API_FUNCTION DhcpRpcFreeMemory(
---   PVOID BufferPointer
--- );
-type RpcFreeMemory = Ptr () -> IO ()
-foreign import stdcall "dynamic"
-    mkRpcFreeMemory :: FunPtr RpcFreeMemory -> RpcFreeMemory
-
--- | In an effort to avoid potential compile-time linker errors this package
--- uses runtime dynamic linking. Internally a `DhcpApi` is a dictionary of
--- dynamically bound foreign calls. Most actions require one to be passed.
--- Simply call `loadDhcp` to obtain one.
-data DhcpApi = DhcpApi
-    { c_DeleteClientInfo :: DeleteClientInfo
-    , c_EnumSubnetClientsV4 :: EnumSubnetClientsV4
-    , c_EnumSubnetElementsV4 :: EnumSubnetElementsV4
-    , c_GetClientInfoV4 :: GetClientInfoV4
-    , c_RemoveSubnetElementV4 :: RemoveSubnetElementV4
-    , c_AddSubnetElementV4 :: AddSubnetElementV4
-    , c_RpcFreeMemory :: RpcFreeMemory
-    }
-
--- | Calling this function performs runtime dynamic linking for every internal
--- foreign call into the Dhcp Server Management Api. It is safe to call this
--- action multiple times. I recommend calling this function once as the part
--- of a process's initialization, and then pass the returned `DhcpApi` to
--- functions that need it.
-loadDHCP :: IO DhcpApi
-loadDHCP = do
-    lib <- loadLibrary "dhcpsapi"
-    deleteclient <- getProcAddress lib "DhcpDeleteClientInfo"
-    enumClients <- getProcAddress lib "DhcpEnumSubnetClientsV4"
-    enumElements <- getProcAddress lib "DhcpEnumSubnetElementsV4"
-    getclient <- getProcAddress lib "DhcpGetClientInfoV4"
-    remove <- getProcAddress lib "DhcpRemoveSubnetElementV4"
-    add <- getProcAddress lib "DhcpAddSubnetElementV4"
-    rpcfree <- getProcAddress lib "DhcpRpcFreeMemory"
-    return $ DhcpApi (mkDeleteClientInfo . castPtrToFunPtr $ deleteclient)
-                     (mkEnumSubnetClientsV4 . castPtrToFunPtr $ enumClients)
-                     (mkEnumSubnetElementsV4 . castPtrToFunPtr $ enumElements)
-                     (mkGetClientInfoV4 . castPtrToFunPtr $ getclient)
-                     (mkRemoveSubnetElementV4 . castPtrToFunPtr $ remove)
-                     (mkAddSubnetElementV4 . castPtrToFunPtr $ add)
-                     (mkRpcFreeMemory . castPtrToFunPtr $ rpcfree)
-
--- |Free a block of memory created by DHCP's api.
---  MSDN only mensions this function in DhcpEnumSubnetElementsV5. The original,
---  v4, and v6 variants of the function don't say how memory should be freed.
-rpcFreeMemory :: DhcpApi -> Ptr a -> IO ()
-rpcFreeMemory api ptr = c_RpcFreeMemory api $ castPtr ptr
+{-# LANGUAGE ForeignFunctionInterface #-}++module System.Win32.DHCP.Internal where++import Foreign+import Foreign.C+import System.Win32.DLL+import System.Win32.Types++import System.Win32.DHCP.Client+import System.Win32.DHCP.SEARCH_INFO+import System.Win32.DHCP.SUBNET_CLIENT_INFO_ARRAY_V4+import System.Win32.DHCP.SUBNET_ELEMENT_INFO_ARRAY_V4+import System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4+import System.Win32.DHCP.Types++-- DWORD DHCP_API_FUNCTION DhcpDeleteClientInfo(+--   _In_  DHCP_CONST WCHAR *ServerIpAddress,+--   _In_  DHCP_CONST DHCP_SEARCH_INFO *ClientInfo+-- );+type DeleteClientInfo = CWString -> Ptr SEARCH_INFO -> IO DWORD+foreign import stdcall "dynamic"+    mkDeleteClientInfo :: FunPtr DeleteClientInfo -> DeleteClientInfo++-- DWORD DHCP_API_FUNCTION DhcpEnumSubnetClientsV4(+--   _In_     DHCP_CONST WCHAR *ServerIpAddress,+--   _In_     DHCP_IP_ADDRESS SubnetAddress,+--   _Inout_  DHCP_RESUME_HANDLE *ResumeHandle,+--   _In_     DWORD PreferredMaximum,+--   _Out_    LPDHCP_CLIENT_INFO_ARRAY_V4 *ClientInfo,+--   _Out_    DWORD *ClientsRead,+--   _Out_    DWORD *ClientsTotal+-- );+type EnumSubnetClientsV4 = CWString -> Word32 -> Ptr RESUME_HANDLE+    -> DWORD -> Ptr (Ptr SUBNET_CLIENT_INFO_ARRAY_V4) -> Ptr DWORD+    -> Ptr DWORD -> IO DWORD+foreign import stdcall "dynamic"+    mkEnumSubnetClientsV4 :: FunPtr EnumSubnetClientsV4+        -> EnumSubnetClientsV4++-- DWORD DHCP_API_FUNCTION DhcpEnumSubnetElementsV4(+--   __in     DHCP_CONST WCHAR *ServerIpAddress,+--   __in     DHCP_IP_ADDRESS SubnetAddress,+--   __in     DHCP_SUBNET_ELEMENT_TYPE EnumElementType,+--   __inout  DHCP_RESUME_HANDLE *ResumeHandle,+--   __in     DWORD PreferredMaximum,+--   __out    LPDHCP_SUBNET_ELEMENT_INFO_ARRAY_V4 *EnumElementInfo,+--  __out    DWORD *ElementsRead,+--  __out    DWORD *ElementsTotal+-- );+type EnumSubnetElementsV4 = CWString -> Word32 -> CInt -> Ptr RESUME_HANDLE+    -> DWORD -> Ptr (Ptr SUBNET_ELEMENT_INFO_ARRAY_V4) -> Ptr DWORD+    -> Ptr DWORD -> IO DWORD+foreign import stdcall "dynamic"+    mkEnumSubnetElementsV4 :: FunPtr EnumSubnetElementsV4+        -> EnumSubnetElementsV4++-- DWORD DHCP_API_FUNCTION DhcpGetClientInfoV4(+--   _In_   DHCP_CONST WCHAR ServerIpAddress,+--   _In_   DHCP_CONST DHCP_SEARCH_INFO SearchInfo,+--   _Out_  LPDHCP_CLIENT_INFO_V4 *ClientInfo+-- );+type GetClientInfoV4 = CWString -> SEARCH_INFO_TYPE -> Ptr ()+    -> Ptr (Ptr Client) -> IO DWORD+foreign import stdcall "dynamic"+    mkGetClientInfoV4 :: FunPtr GetClientInfoV4 -> GetClientInfoV4++-- | Foreign wrapper to DhcpRemoveSubnetElementV4.+-- +--   The DhcpRemoveSubnetElementV4 function removes an IPv4 subnet element+--   from an IPv4 subnet defined on the DHCPv4 server. The function extends+--   the functionality provided by DhcpRemoveSubnetElement by allowing the+--   specification of a subnet that contains client type (DHCP or BOOTP)+--   information.+-- +--   See <http://msdn.microsoft.com/en-us/library/ee309533(v=VS.85).aspx> for+--   official documentation.+-- +-- @+--   DWORD DHCP_API_FUNCTION DhcpRemoveSubnetElementV4(+--     __in  DHCP_CONST WCHAR *ServerIpAddress,+--     __in  DHCP_IP_ADDRESS SubnetAddress,+--     __in  DHCP_CONST DHCP_SUBNET_ELEMENT_DATA_V4 *RemoveElementInfo,+--     __in  DHCP_FORCE_FLAG ForceFlag+--   );+-- @+type RemoveSubnetElementV4 = CWString -> Word32 -> Ptr SUBNET_ELEMENT_DATA_V4+    -> CInt -> IO DWORD+foreign import stdcall "dynamic"+    mkRemoveSubnetElementV4 :: FunPtr RemoveSubnetElementV4+        -> RemoveSubnetElementV4++-- DWORD DhcpAddSubnetElementV4(+--   __in  DHCP_CONST WCHAR *ServerIpAddress,+--   __in  DHCP_IP_ADDRESS SubnetAddress,+--   __in  DHCP_CONST DHCP_SUBNET_ELEMENT_DATA_V4 AddElementInfo+-- );+type AddSubnetElementV4 = CWString -> Word32 -> Ptr SUBNET_ELEMENT_DATA_V4+    -> IO DWORD+foreign import stdcall "dynamic"+    mkAddSubnetElementV4 :: FunPtr AddSubnetElementV4 -> AddSubnetElementV4++-- VOID DHCP_API_FUNCTION DhcpRpcFreeMemory(+--   PVOID BufferPointer+-- );+type RpcFreeMemory = Ptr () -> IO ()+foreign import stdcall "dynamic"+    mkRpcFreeMemory :: FunPtr RpcFreeMemory -> RpcFreeMemory++-- | In an effort to avoid potential compile-time linker errors this package+-- uses runtime dynamic linking. Internally a `DhcpApi` is a dictionary of+-- dynamically bound foreign calls. Most actions require one to be passed.+-- Simply call `loadDhcp` to obtain one.+data DhcpApi = DhcpApi+    { c_DeleteClientInfo :: DeleteClientInfo+    , c_EnumSubnetClientsV4 :: EnumSubnetClientsV4+    , c_EnumSubnetElementsV4 :: EnumSubnetElementsV4+    , c_GetClientInfoV4 :: GetClientInfoV4+    , c_RemoveSubnetElementV4 :: RemoveSubnetElementV4+    , c_AddSubnetElementV4 :: AddSubnetElementV4+    , c_RpcFreeMemory :: RpcFreeMemory+    }++-- | Calling this function performs runtime dynamic linking for every internal+-- foreign call into the Dhcp Server Management Api. It is safe to call this+-- action multiple times. I recommend calling this function once as the part+-- of a process's initialization, and then pass the returned `DhcpApi` to+-- functions that need it.+loadDHCP :: IO DhcpApi+loadDHCP = do+    lib <- loadLibrary "dhcpsapi"+    deleteclient <- getProcAddress lib "DhcpDeleteClientInfo"+    enumClients <- getProcAddress lib "DhcpEnumSubnetClientsV4"+    enumElements <- getProcAddress lib "DhcpEnumSubnetElementsV4"+    getclient <- getProcAddress lib "DhcpGetClientInfoV4"+    remove <- getProcAddress lib "DhcpRemoveSubnetElementV4"+    add <- getProcAddress lib "DhcpAddSubnetElementV4"+    rpcfree <- getProcAddress lib "DhcpRpcFreeMemory"+    return $ DhcpApi (mkDeleteClientInfo . castPtrToFunPtr $ deleteclient)+                     (mkEnumSubnetClientsV4 . castPtrToFunPtr $ enumClients)+                     (mkEnumSubnetElementsV4 . castPtrToFunPtr $ enumElements)+                     (mkGetClientInfoV4 . castPtrToFunPtr $ getclient)+                     (mkRemoveSubnetElementV4 . castPtrToFunPtr $ remove)+                     (mkAddSubnetElementV4 . castPtrToFunPtr $ add)+                     (mkRpcFreeMemory . castPtrToFunPtr $ rpcfree)++-- |Free a block of memory created by DHCP's api.+--  MSDN only mensions this function in DhcpEnumSubnetElementsV5. The original,+--  v4, and v6 variants of the function don't say how memory should be freed.+rpcFreeMemory :: DhcpApi -> Ptr a -> IO ()+rpcFreeMemory api ptr = c_RpcFreeMemory api $ castPtr ptr
System/Win32/DHCP/LengthBuffer.hs view
@@ -1,51 +1,63 @@-module System.Win32.DHCP.LengthBuffer
-  ( LengthBuffer (..)
-  , lengthBuffer
-  ) where
-
-import Control.Applicative
-import Foreign.Ptr
-import Foreign.Storable
-import System.Win32.Types (DWORD)
-
-import System.Win32.DHCP.DhcpStructure
-
--- |A LengthBuffer is a list of items which can be marshalled in and out
--- of memory with a `DhcpArray` instance. 
-data LengthBuffer a = LengthBuffer
-    { lbLength :: Int
-    , buffer :: [a]
-    }
-
-lengthBuffer :: DhcpArray a -> DhcpStructure (LengthBuffer a)
-lengthBuffer dict = DhcpStructure
-    { peekDhcp = peekLb dict
-    , freeDhcp = freeLb dict
-    , withDhcp' = withLb' dict
-    , sizeDhcp = 8
-    }
-
-peekLb :: DhcpArray a -> Ptr (LengthBuffer a) -> IO (LengthBuffer a)
-peekLb dict ptr = do
-    len <- fromIntegral <$> peek (pNumElements ptr)
-    pElements <- peek $ ppElements ptr
-    LengthBuffer len <$> peekDhcpArray dict len pElements
-
-freeLb :: DhcpArray a -> (Ptr x -> IO ()) -> Ptr (LengthBuffer a) -> IO ()
-freeLb dict freefunc ptr = do
-    len <- fromIntegral <$> peek (pNumElements ptr)
-    pElements <- peek $ ppElements ptr
-    freeDhcpArray dict freefunc len pElements
-
-withLb' :: DhcpArray a -> LengthBuffer a -> Ptr (LengthBuffer a) -> IO r -> IO r
-withLb' dict (LengthBuffer _ elems) ptr f =
-    withDhcpArray dict elems $ \size pelems -> do
-    pokeByteOff ptr 0 (fromIntegral size :: DWORD)
-    poke (ppElements ptr) (castPtr pelems)
-    f
-
-pNumElements :: Ptr (LengthBuffer a) -> Ptr DWORD
-pNumElements ptr = castPtr ptr
-
-ppElements :: Ptr (LengthBuffer a) -> Ptr (Ptr a)
-ppElements ptr = castPtr ptr `plusPtr` 4
+{-# LANGUAGE RankNTypes #-}++module System.Win32.DHCP.LengthBuffer+  ( LengthBuffer (..)+  , lengthBuffer+  ) where++import Control.Applicative+import Foreign.Ptr+import Foreign.Storable+import System.Win32.Types (DWORD)++import System.Win32.DHCP.DhcpStructure++import Utils++-- |A LengthBuffer is a list of items which can be marshalled in and out+-- of memory with a `DhcpArray` instance. A C structure that can be used+-- with this looks something like the following:+-- +-- typedef struct Structure {+--   DWORD         NumElements;+--   LPElementData Elements;+-- } Structure, *LPStructure;+data LengthBuffer a = LengthBuffer+    { lbLength :: !Int+    , buffer :: [a]+    }++lengthBuffer :: DhcpArray a -> DhcpStructure (LengthBuffer a)+lengthBuffer dict = DhcpStructure+    { peekDhcp = peekLb dict+    , freeDhcpChildren = freeLb dict+    , withDhcp' = withLb' dict+    , sizeDhcp = 8+    }++peekLb :: DhcpArray a -> Ptr (LengthBuffer a) -> IO (LengthBuffer a)+peekLb dict ptr = do+    len <- fromIntegral <$> peek (pNumElements ptr)+    pElements <- peek $ ppElements ptr+    LengthBuffer len <$> peekDhcpArray dict len pElements++freeLb :: DhcpArray a -> (forall x. Ptr x -> IO ())+    -> Ptr (LengthBuffer a) -> IO ()+freeLb dict freefunc ptr = do+    len <- fromIntegral <$> peek (pNumElements ptr)+    -- A LengthBuffer contain a pointer to the buffer; not the start of the+    -- buffer itself.+    freeDhcpArray dict freefunc len `scrubbing_` ppElements ptr++withLb' :: DhcpArray a -> LengthBuffer a -> Ptr (LengthBuffer a) -> IO r -> IO r+withLb' dict (LengthBuffer _ elems) ptr f =+    withDhcpArray dict elems $ \size pelems -> do+    pokeByteOff ptr 0 (fromIntegral size :: DWORD)+    poke (ppElements ptr) (castPtr pelems)+    f++pNumElements :: Ptr (LengthBuffer a) -> Ptr DWORD+pNumElements ptr = castPtr ptr++ppElements :: Ptr (LengthBuffer a) -> Ptr (Ptr a)+ppElements ptr = castPtr ptr `plusPtr` 4
System/Win32/DHCP/Reservation.hs view
@@ -1,81 +1,81 @@-module System.Win32.DHCP.Reservation
-    ( Mapping (..)
-    , Reservation (..)
-    , reservation
-    ) where
-
-import Control.Applicative
-import Foreign
-
-import Data.Ip
-import Data.Mac
-import System.Win32.DHCP.CLIENT_UID
-import System.Win32.DHCP.DhcpStructure
-import System.Win32.DHCP.Types (ClientType)
-import Utils (freeptr)
-
--- | A Reservation guarantees that a device with a given Mac address will
--- always be assigned to a particular IP address. A reservation is not the
--- same thing as a lease, and there are separate calls to work with both
--- objects.
---
--- This type corresponds to MSDN's DHCP_IP_RESERVATION_V4 structure.
---
--- > typedef struct _DHCP_IP_RESERVATION_V4 {
--- >   DHCP_IP_ADDRESS ReservedIpAddress;
--- >   DHCP_CLIENT_UID *ReservedForClient;
--- >   BYTE            bAllowedClientTypes;
--- > } DHCP_IP_RESERVATION_V4, *LPDHCP_IP_RESERVATION_V4;
-data Reservation = Reservation
-    { reservationMapping :: !Mapping
-    , reservationType    :: !ClientType
-    } deriving (Eq)
-
--- | A mapping between an IP and a MAC address. Each IP number may map to
--- only one MAC address, and each MAC address may map to only one IP number.
---
--- This is a separate type from `Reservation` for practical reasons. When
--- writing software to work with a DHCP server, `Reservation`'s
--- `ClientType` field is often not important. Without the `Mapping` type
--- defined here it would often be necessary to define a custom type in
--- each project.
-data Mapping = Mapping
-    { mappingMac :: !Mac
-    , mappingIp  :: !Ip
-    } deriving (Eq, Ord)
-
-reservation :: DhcpStructure Reservation
-reservation = DhcpStructure
-    { peekDhcp = peekReservation
-    , freeDhcp = freeReservation
-    , withDhcp' = withReservation'
-    , sizeDhcp = 10
-    }
-
-peekReservation :: Ptr Reservation -> IO Reservation
-peekReservation ptr = do
-    pCuid <- peek $ ppCuid ptr
-    mac <- macCuidDrop5 <$> peekDhcp clientUid pCuid
-    addr <- peek pAddress
-    clientType <- peekByteOff (castPtr ptr) 8
-    return $ Reservation (Mapping mac addr) clientType
-  where
-    pAddress = castPtr ptr :: Ptr Ip
-
-freeReservation :: (Ptr a -> IO ()) -> Ptr Reservation -> IO ()
-freeReservation freefunc ptr = do
-    peek (ppCuid ptr) >>= freeDhcp clientUid freefunc
-    freeptr freefunc $ ppCuid ptr
-    freefunc $ castPtr ptr
-
-ppCuid :: Ptr Reservation -> Ptr (Ptr CLIENT_UID)
-ppCuid ptr = castPtr ptr `plusPtr` 4
-
-withReservation' :: Reservation -> Ptr Reservation
-    -> IO r -> IO r
-withReservation' (Reservation (Mapping mac address) clientType) ptr f =
-    withMac mac $ \pCuid -> do
-    poke (castPtr ptr) address
-    pokeByteOff (castPtr ptr) 4 pCuid
-    pokeByteOff (castPtr ptr) 8 clientType
-    f
+{-# LANGUAGE RankNTypes #-}++module System.Win32.DHCP.Reservation+    ( Mapping (..)+    , Reservation (..)+    , reservation+    ) where++import Control.Applicative+import Foreign++import Data.Ip+import Data.Mac+import System.Win32.DHCP.CLIENT_UID+import System.Win32.DHCP.DhcpStructure+import System.Win32.DHCP.Types (ClientType)+import Utils (scrubbing_)++-- | A Reservation guarantees that a device with a given Mac address will+-- always be assigned to a particular IP address. A reservation is not the+-- same thing as a lease, and there are separate calls to work with both+-- objects.+--+-- This type corresponds to MSDN's DHCP_IP_RESERVATION_V4 structure.+--+-- > typedef struct _DHCP_IP_RESERVATION_V4 {+-- >   DHCP_IP_ADDRESS ReservedIpAddress;+-- >   DHCP_CLIENT_UID *ReservedForClient;+-- >   BYTE            bAllowedClientTypes;+-- > } DHCP_IP_RESERVATION_V4, *LPDHCP_IP_RESERVATION_V4;+data Reservation = Reservation+    { reservationMapping :: !Mapping+    , reservationType    :: !ClientType+    } deriving (Eq)++-- | A mapping between an IP and a MAC address. Each IP number may map to+-- only one MAC address, and each MAC address may map to only one IP number.+--+-- This is a separate type from `Reservation` for practical reasons. When+-- writing software to work with a DHCP server, `Reservation`'s+-- `ClientType` field is often not important. Without the `Mapping` type+-- defined here it would often be necessary to define a custom type in+-- each project.+data Mapping = Mapping+    { mappingMac :: !Mac+    , mappingIp  :: !Ip+    } deriving (Eq, Ord)++reservation :: DhcpStructure Reservation+reservation = DhcpStructure+    { peekDhcp = peekReservation+    , freeDhcpChildren = freeReservation+    , withDhcp' = withReservation'+    , sizeDhcp = 10+    }++peekReservation :: Ptr Reservation -> IO Reservation+peekReservation ptr = do+    pCuid <- peek $ ppCuid ptr+    mac <- macCuidDrop5 <$> peekDhcp clientUid pCuid+    addr <- peek pAddress+    clientType <- peekByteOff (castPtr ptr) 8+    return $ Reservation (Mapping mac addr) clientType+  where+    pAddress = castPtr ptr :: Ptr Ip++freeReservation :: (forall x. Ptr x -> IO ()) -> Ptr Reservation -> IO ()+freeReservation freefunc ptr = do+    freeDhcp clientUid freefunc `scrubbing_` ppCuid ptr++ppCuid :: Ptr Reservation -> Ptr (Ptr CLIENT_UID)+ppCuid ptr = castPtr ptr `plusPtr` 4++withReservation' :: Reservation -> Ptr Reservation+    -> IO r -> IO r+withReservation' (Reservation (Mapping mac address) clientType) ptr f =+    withMac mac $ \pCuid -> do+    poke (castPtr ptr) address+    pokeByteOff (castPtr ptr) 4 pCuid+    pokeByteOff (castPtr ptr) 8 clientType+    f
System/Win32/DHCP/SEARCH_INFO.hs view
@@ -1,68 +1,68 @@-{-# LANGUAGE OverloadedStrings #-}
-
-module System.Win32.DHCP.SEARCH_INFO
-  ( SEARCH_INFO_TYPE
-  , SEARCH_INFO (..)
-  , withSearchInfo
-  ) where
-
-import Data.Monoid ((<>))
-import qualified Data.Text as T
-import Foreign
-import Foreign.C.Types
-
-import System.Win32.Types
-
-import Data.Ip
-import Data.Mac
-import System.Win32.DHCP.CLIENT_UID
-
--- typedef enum _DHCP_CLIENT_SEARCH_TYPE { 
---   DhcpClientIpAddress,
---   DhcpClientHardwareAddress,
---   DhcpClientName
--- } DHCP_SEARCH_INFO_TYPE, *LPDHCP_SEARCH_INFO_TYPE;
-type SEARCH_INFO_TYPE = CInt
-
--- | Filter criteria used in actions that look up reservation or lease
--- records.
---
--- > typedef struct _DHCP_CLIENT_SEARCH_INFO {
--- >   DHCP_SEARCH_INFO_TYPE SearchType;
--- >   union {
--- >     DHCP_IP_ADDRESS ClientIpAddress;
--- >     DHCP_CLIENT_UID ClientHardwareAddress;
--- >     LPWSTR          ClientName;
--- >   } SearchInfo;
--- > } DHCP_SEARCH_INFO, *LPDHCP_SEARCH_INFO;
-data SEARCH_INFO
-  -- | Search based on an IP address. All scopes are searched. It should
-  -- not be possible for multiple records to exist.
-  = ClientIpAddress       !Ip
-  -- | Search based on a subnet and MAC address. This method of searching
-  -- has not been tested.
-  | ClientHardwareAddress !Mac
-  -- | Search based on a client's name. Multiple records may exist, and
-  -- what happens in that case will depend on the function being called.
-  -- This method of searching has not been tested.
-  | ClientName            !String
-
-instance Show SEARCH_INFO where
-  show (ClientIpAddress ip) = T.unpack $ "ClientIpAddress " <> showIp ip
-  show (ClientHardwareAddress mac) = T.unpack $ "ClientHardwareAddress " <> showMac ":" mac
-  show (ClientName name) = "ClientName " ++ name
-
-siTypeOf :: SEARCH_INFO -> SEARCH_INFO_TYPE
-siTypeOf (ClientIpAddress _) = 0
-siTypeOf (ClientHardwareAddress _) = 1
-siTypeOf (ClientName _) = 2
-
--- Allocate 12 because a SEARCH_INFO.SearchInfo member's in-structure alignment is 4 with a size of 8.
-withSearchInfo :: SEARCH_INFO -> (Ptr SEARCH_INFO -> IO r) -> IO r
-withSearchInfo si f = allocaBytes 12 $ \ptr -> do
-    let pX = ptr `plusPtr` 4
-    poke (castPtr ptr) $ siTypeOf si
-    case si of
-      ClientIpAddress x -> poke (castPtr pX) x >> f ptr
-      ClientHardwareAddress m -> withMac m $ \pm -> copyBytes (castPtr pX) pm 8 >> f ptr
-      ClientName str -> withTString str $ \pstr -> copyBytes (castPtr pX) pstr 4 >> f ptr
+{-# LANGUAGE OverloadedStrings #-}++module System.Win32.DHCP.SEARCH_INFO+  ( SEARCH_INFO_TYPE+  , SEARCH_INFO (..)+  , withSearchInfo+  ) where++import Data.Monoid ((<>))+import qualified Data.Text as T+import Foreign+import Foreign.C.Types++import System.Win32.Types++import Data.Ip+import Data.Mac+import System.Win32.DHCP.CLIENT_UID++-- typedef enum _DHCP_CLIENT_SEARCH_TYPE { +--   DhcpClientIpAddress,+--   DhcpClientHardwareAddress,+--   DhcpClientName+-- } DHCP_SEARCH_INFO_TYPE, *LPDHCP_SEARCH_INFO_TYPE;+type SEARCH_INFO_TYPE = CInt++-- | Filter criteria used in actions that look up reservation or lease+-- records.+--+-- > typedef struct _DHCP_CLIENT_SEARCH_INFO {+-- >   DHCP_SEARCH_INFO_TYPE SearchType;+-- >   union {+-- >     DHCP_IP_ADDRESS ClientIpAddress;+-- >     DHCP_CLIENT_UID ClientHardwareAddress;+-- >     LPWSTR          ClientName;+-- >   } SearchInfo;+-- > } DHCP_SEARCH_INFO, *LPDHCP_SEARCH_INFO;+data SEARCH_INFO+  -- | Search based on an IP address. All scopes are searched. It should+  -- not be possible for multiple records to exist.+  = ClientIpAddress       !Ip+  -- | Search based on a subnet and MAC address. This method of searching+  -- has not been tested.+  | ClientHardwareAddress !Mac+  -- | Search based on a client's name. Multiple records may exist, and+  -- what happens in that case will depend on the function being called.+  -- This method of searching has not been tested.+  | ClientName            !String++instance Show SEARCH_INFO where+  show (ClientIpAddress ip) = T.unpack $ "ClientIpAddress " <> showIp ip+  show (ClientHardwareAddress mac) = T.unpack $ "ClientHardwareAddress " <> showMac ":" mac+  show (ClientName name) = "ClientName " ++ name++siTypeOf :: SEARCH_INFO -> SEARCH_INFO_TYPE+siTypeOf (ClientIpAddress _) = 0+siTypeOf (ClientHardwareAddress _) = 1+siTypeOf (ClientName _) = 2++-- Allocate 12 because a SEARCH_INFO.SearchInfo member's in-structure alignment is 4 with a size of 8.+withSearchInfo :: SEARCH_INFO -> (Ptr SEARCH_INFO -> IO r) -> IO r+withSearchInfo si f = allocaBytes 12 $ \ptr -> do+    let pX = ptr `plusPtr` 4+    poke (castPtr ptr) $ siTypeOf si+    case si of+      ClientIpAddress x -> poke (castPtr pX) x >> f ptr+      ClientHardwareAddress m -> withMac m $ \pm -> copyBytes (castPtr pX) pm 8 >> f ptr+      ClientName str -> withTString str $ \pstr -> copyBytes (castPtr pX) pstr 4 >> f ptr
System/Win32/DHCP/SUBNET_ELEMENT_DATA_V4.hs view
@@ -1,100 +1,102 @@-module System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4
-    ( SUBNET_ELEMENT_DATA_V4 (..)
-    , SUBNET_ELEMENT_TYPE
-    , elementTypeOf
-    , subnetElementData
-    ) where
-
-import Control.Applicative
-import Foreign
-import Foreign.C.Types
-
-import System.Win32.DHCP.DhcpStructure
-import System.Win32.DHCP.HOST_INFO
-import System.Win32.DHCP.IP_CLUSTER
-import System.Win32.DHCP.IP_RANGE
-import System.Win32.DHCP.Reservation
-
--- typedef struct _DHCP_SUBNET_ELEMENT_DATA_V4 {
---   DHCP_SUBNET_ELEMENT_TYPE ElementType;
---   union {
---     DHCP_IP_RANGE          *IpRange;
---     DHCP_HOST_INFO         *SecondaryHost;
---     DHCP_IP_RESERVATION_V4 *ReservedIp;
---     DHCP_IP_RANGE          *ExcludeIpRange;
---     DHCP_IP_CLUSTER        *IpUsedCluster;
---   } Element;
--- } DHCP_SUBNET_ELEMENT_DATA_V4, *LPDHCP_SUBNET_ELEMENT_DATA_V4;
-data SUBNET_ELEMENT_DATA_V4
-    = IpRanges         !IP_RANGE
-    | SecondaryHosts   !HOST_INFO
-    | ReservedIps      !Reservation
-    | ExcludedIpRanges !IP_RANGE
-    | IpUsedCluster    !IP_CLUSTER
-
--- typedef enum _DHCP_SUBNET_ELEMENT_TYPE_V5 {
---   DhcpIpRanges,
---   DhcpSecondaryHosts,
---   DhcpReservedIps,
---   DhcpExcludedIpRanges,
---   DhcpIpRangesDhcpOnly,
---   DhcpIpRangesDhcpBootp,
---   DhcpIpRangesBootpOnly
--- } DHCP_SUBNET_ELEMENT_TYPE, *LPDHCP_SUBNET_ELEMENT_TYPE;
-type SUBNET_ELEMENT_TYPE = CInt
-
-subnetElementData :: DhcpStructure SUBNET_ELEMENT_DATA_V4
-subnetElementData = DhcpStructure
-    { peekDhcp = peekSubnetElementData
-    , freeDhcp = freeSubnetElementData
-    , withDhcp' = withSubnetElementData'
-    , sizeDhcp = 8
-    }
-
-peekSubnetElementData :: Ptr SUBNET_ELEMENT_DATA_V4 -> IO SUBNET_ELEMENT_DATA_V4
-peekSubnetElementData ptr = do
-    elementType <- (peek . castPtr) ptr :: IO SUBNET_ELEMENT_TYPE
-    pElement <- peekByteOff ptr 4
-    case elementType of
-      0 -> IpRanges         <$> peek (castPtr pElement)
-      1 -> SecondaryHosts   <$> peekDhcp hostInfo (castPtr pElement)
-      2 -> ReservedIps      <$> peekDhcp reservation (castPtr pElement)
-      3 -> ExcludedIpRanges <$> peek (castPtr pElement)
-      4 -> IpUsedCluster    <$> peek (castPtr pElement)
-      _ -> error "Invalid element type found in SUBNET_ELEMENT_DATA_V4."
-
-freeSubnetElementData :: (Ptr a -> IO ()) -> Ptr SUBNET_ELEMENT_DATA_V4 -> IO ()
-freeSubnetElementData freefunc ptr = do
-    elementType <- (peek . castPtr) ptr :: IO SUBNET_ELEMENT_TYPE
-    pElement <- peek $ ppElement ptr
-    case elementType of
-      0 -> return ()
-      1 -> freeDhcp hostInfo freefunc $ castPtr pElement
-      2 -> freeDhcp reservation freefunc $ castPtr pElement
-      3 -> return ()
-      4 -> return ()
-      _ -> error "Invalid element type found in SUBNET_ELEMENT_DATA_V4."
-    poke (ppElement ptr) nullPtr
-    freefunc $ castPtr ptr
-
--- Also used by withSubnetElementDataArray.
-withSubnetElementData' :: SUBNET_ELEMENT_DATA_V4 -> Ptr SUBNET_ELEMENT_DATA_V4
-    -> IO r -> IO r
-withSubnetElementData' elementData ptr f = do
-    castPtr ptr `poke` elementTypeOf elementData
-    case elementData of
-      IpRanges x -> with x $ \pX -> poke (ppElement ptr) pX >> f
-      SecondaryHosts x -> withDhcp hostInfo x $ \pX -> poke (ppElement ptr) pX >> f
-      ReservedIps x -> withDhcp reservation x $ \pX -> poke (ppElement ptr) pX >> f
-      ExcludedIpRanges x -> with x $ \pX -> poke (ppElement ptr) pX >> f
-      IpUsedCluster x -> with x $ \pX -> poke (ppElement ptr) pX >> f
-
-ppElement :: Ptr SUBNET_ELEMENT_DATA_V4 -> Ptr a
-ppElement ptr = castPtr $ ptr `plusPtr` 4
-
-elementTypeOf :: SUBNET_ELEMENT_DATA_V4 -> SUBNET_ELEMENT_TYPE
-elementTypeOf (IpRanges _)         = 0
-elementTypeOf (SecondaryHosts _)   = 1
-elementTypeOf (ReservedIps _)      = 2
-elementTypeOf (ExcludedIpRanges _) = 3
-elementTypeOf (IpUsedCluster _)    = 4
+{-# LANGUAGE RankNTypes #-}++module System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4+    ( SUBNET_ELEMENT_DATA_V4 (..)+    , SUBNET_ELEMENT_TYPE+    , elementTypeOf+    , subnetElementData+    ) where++import Control.Applicative+import Foreign+import Foreign.C.Types++import System.Win32.DHCP.DhcpStructure+import System.Win32.DHCP.HOST_INFO+import System.Win32.DHCP.IP_CLUSTER+import System.Win32.DHCP.IP_RANGE+import System.Win32.DHCP.Reservation+import Utils++-- typedef struct _DHCP_SUBNET_ELEMENT_DATA_V4 {+--   DHCP_SUBNET_ELEMENT_TYPE ElementType;+--   union {+--     DHCP_IP_RANGE          *IpRange;+--     DHCP_HOST_INFO         *SecondaryHost;+--     DHCP_IP_RESERVATION_V4 *ReservedIp;+--     DHCP_IP_RANGE          *ExcludeIpRange;+--     DHCP_IP_CLUSTER        *IpUsedCluster;+--   } Element;+-- } DHCP_SUBNET_ELEMENT_DATA_V4, *LPDHCP_SUBNET_ELEMENT_DATA_V4;+data SUBNET_ELEMENT_DATA_V4+    = IpRanges         !IP_RANGE+    | SecondaryHosts   !HOST_INFO+    | ReservedIps      !Reservation+    | ExcludedIpRanges !IP_RANGE+    | IpUsedCluster    !IP_CLUSTER++-- typedef enum _DHCP_SUBNET_ELEMENT_TYPE_V5 {+--   DhcpIpRanges,+--   DhcpSecondaryHosts,+--   DhcpReservedIps,+--   DhcpExcludedIpRanges,+--   DhcpIpRangesDhcpOnly,+--   DhcpIpRangesDhcpBootp,+--   DhcpIpRangesBootpOnly+-- } DHCP_SUBNET_ELEMENT_TYPE, *LPDHCP_SUBNET_ELEMENT_TYPE;+type SUBNET_ELEMENT_TYPE = CInt++subnetElementData :: DhcpStructure SUBNET_ELEMENT_DATA_V4+subnetElementData = DhcpStructure+    { peekDhcp = peekSubnetElementData+    , freeDhcpChildren = freeSubnetElementData+    , withDhcp' = withSubnetElementData'+    , sizeDhcp = 8+    }++peekSubnetElementData :: Ptr SUBNET_ELEMENT_DATA_V4 -> IO SUBNET_ELEMENT_DATA_V4+peekSubnetElementData ptr = do+    elementType <- (peek . castPtr) ptr :: IO SUBNET_ELEMENT_TYPE+    pElement <- peekByteOff ptr 4+    case elementType of+      0 -> IpRanges         <$> peek (castPtr pElement)+      1 -> SecondaryHosts   <$> peekDhcp hostInfo (castPtr pElement)+      2 -> ReservedIps      <$> peekDhcp reservation (castPtr pElement)+      3 -> ExcludedIpRanges <$> peek (castPtr pElement)+      4 -> IpUsedCluster    <$> peek (castPtr pElement)+      _ -> error "Invalid element type found in SUBNET_ELEMENT_DATA_V4."++freeSubnetElementData :: (forall x. Ptr x -> IO ())+    -> Ptr SUBNET_ELEMENT_DATA_V4 -> IO ()+freeSubnetElementData freefunc ptr = do+    elementType <- (peek . castPtr) ptr :: IO SUBNET_ELEMENT_TYPE+    scrubWith_ (ppElement ptr) $ \pElement -> do+        case elementType of+          0 -> return ()+          1 -> freeDhcp hostInfo freefunc $ castPtr pElement+          2 -> freeDhcp reservation freefunc $ castPtr pElement+          3 -> return ()+          4 -> return ()+          _ -> error "Invalid element type found in SUBNET_ELEMENT_DATA_V4."++-- Also used by withSubnetElementDataArray.+withSubnetElementData' :: SUBNET_ELEMENT_DATA_V4 -> Ptr SUBNET_ELEMENT_DATA_V4+    -> IO r -> IO r+withSubnetElementData' elementData ptr f = do+    castPtr ptr `poke` elementTypeOf elementData+    case elementData of+      IpRanges x -> with x $ \pX -> poke (ppElement ptr) pX >> f+      SecondaryHosts x -> withDhcp hostInfo x $ \pX -> poke (ppElement ptr) pX >> f+      ReservedIps x -> withDhcp reservation x $ \pX -> poke (ppElement ptr) pX >> f+      ExcludedIpRanges x -> with x $ \pX -> poke (ppElement ptr) pX >> f+      IpUsedCluster x -> with x $ \pX -> poke (ppElement ptr) pX >> f++ppElement :: Ptr SUBNET_ELEMENT_DATA_V4 -> Ptr a+ppElement ptr = castPtr $ ptr `plusPtr` 4++elementTypeOf :: SUBNET_ELEMENT_DATA_V4 -> SUBNET_ELEMENT_TYPE+elementTypeOf (IpRanges _)         = 0+elementTypeOf (SecondaryHosts _)   = 1+elementTypeOf (ReservedIps _)      = 2+elementTypeOf (ExcludedIpRanges _) = 3+elementTypeOf (IpUsedCluster _)    = 4
System/Win32/DHCP/Types.hs view
@@ -1,76 +1,76 @@-module System.Win32.DHCP.Types
-  ( ClientType (..)
-  , DATE_TIME (..)
-  , FORCE_FLAG (..)
-  , RESUME_HANDLE
-  ) where
-
-import Control.Applicative
-import Foreign
-
-import System.Win32.Types
-
--- | Microsoft's DHCP server supports DHCP and BOOTP. Both protocols server
--- similar purposes, but DHCP is more widely used. Lease and reservation
--- records contain a flag field indicating which protocol the record is valid
--- for. In most cases this flag will be `Both`, because that is the default
--- behavior.
-data ClientType = Unspecified | Dhcp | Bootp | Both | ReservationFlag | None
-    deriving (Eq)
-
-instance Enum ClientType where
-  toEnum 0x00 = Unspecified
-  toEnum 0x01 = Dhcp
-  toEnum 0x02 = Bootp
-  toEnum 0x03 = Both
-  toEnum 0x04 = ReservationFlag
-  toEnum 0x64 = None
-  toEnum x    = error $ "invalid ClientType: " ++ show x
-  fromEnum Unspecified     = 0x00
-  fromEnum Dhcp            = 0x01
-  fromEnum Bootp           = 0x02
-  fromEnum Both            = 0x03
-  fromEnum ReservationFlag = 0x04
-  fromEnum None            = 0x64
-
-instance Storable ClientType where
-  sizeOf _ = sizeOf (undefined :: BYTE)
-  alignment _ = alignment (undefined :: BYTE)
-  peek ptr = toEnum . fromIntegral <$> peek (pbyte ptr)
-  poke ptr ct = poke (pbyte ptr) . fromIntegral . fromEnum $ ct
-
-pbyte :: Ptr ClientType -> Ptr BYTE
-pbyte = castPtr
-
--- | The number of ticks (100-nanosecond increments) since 12:00 midnight,
--- January 1, 1 C.E. in the Gregorian calendar.
---
--- Microsoft does not provide any functions I know of for converting this
--- value into something more convenient to work with.
---
--- > typedef struct _DATE_TIME {
--- >     DWORD dwLowDateTime;
--- >     DWORD dwHighDateTime;
--- > } DATE_TIME,*PDATE_TIME, *LPDATE_TIME;
-data DATE_TIME = DATE_TIME !DWORD !DWORD
-
-instance Storable DATE_TIME where
-  sizeOf _ = 8
-  alignment _ = 4
-  peek ptr = DATE_TIME <$> peekElemOff (pdword ptr) 0
-                       <*> peekElemOff (pdword ptr) 1
-  poke ptr (DATE_TIME l h) = do
-    pokeElemOff (pdword ptr) 0 l
-    pokeElemOff (pdword ptr) 1 h
-
-pdword :: Ptr DATE_TIME -> Ptr DWORD
-pdword ptr = castPtr ptr
-
--- typedef enum _DHCP_FORCE_FLAG {
---   DhcpFullForce,
---   DhcpNoForce
--- } DHCP_FORCE_FLAG, *LPDHCP_FORCE_FLAG;
-data FORCE_FLAG = FullForce | NoForce
-    deriving (Enum)
-
-type RESUME_HANDLE = DWORD
+module System.Win32.DHCP.Types+  ( ClientType (..)+  , DATE_TIME (..)+  , FORCE_FLAG (..)+  , RESUME_HANDLE+  ) where++import Control.Applicative+import Foreign++import System.Win32.Types++-- | Microsoft's DHCP server supports DHCP and BOOTP. Both protocols server+-- similar purposes, but DHCP is more widely used. Lease and reservation+-- records contain a flag field indicating which protocol the record is valid+-- for. In most cases this flag will be `Both`, because that is the default+-- behavior.+data ClientType = Unspecified | Dhcp | Bootp | Both | ReservationFlag | None+    deriving (Eq)++instance Enum ClientType where+  toEnum 0x00 = Unspecified+  toEnum 0x01 = Dhcp+  toEnum 0x02 = Bootp+  toEnum 0x03 = Both+  toEnum 0x04 = ReservationFlag+  toEnum 0x64 = None+  toEnum x    = error $ "invalid ClientType: " ++ show x+  fromEnum Unspecified     = 0x00+  fromEnum Dhcp            = 0x01+  fromEnum Bootp           = 0x02+  fromEnum Both            = 0x03+  fromEnum ReservationFlag = 0x04+  fromEnum None            = 0x64++instance Storable ClientType where+  sizeOf _ = sizeOf (undefined :: BYTE)+  alignment _ = alignment (undefined :: BYTE)+  peek ptr = toEnum . fromIntegral <$> peek (pbyte ptr)+  poke ptr ct = poke (pbyte ptr) . fromIntegral . fromEnum $ ct++pbyte :: Ptr ClientType -> Ptr BYTE+pbyte = castPtr++-- | The number of ticks (100-nanosecond increments) since 12:00 midnight,+-- January 1, 1 C.E. in the Gregorian calendar.+--+-- Microsoft does not provide any functions I know of for converting this+-- value into something more convenient to work with.+--+-- > typedef struct _DATE_TIME {+-- >     DWORD dwLowDateTime;+-- >     DWORD dwHighDateTime;+-- > } DATE_TIME,*PDATE_TIME, *LPDATE_TIME;+data DATE_TIME = DATE_TIME !DWORD !DWORD++instance Storable DATE_TIME where+  sizeOf _ = 8+  alignment _ = 4+  peek ptr = DATE_TIME <$> peekElemOff (pdword ptr) 0+                       <*> peekElemOff (pdword ptr) 1+  poke ptr (DATE_TIME l h) = do+    pokeElemOff (pdword ptr) 0 l+    pokeElemOff (pdword ptr) 1 h++pdword :: Ptr DATE_TIME -> Ptr DWORD+pdword ptr = castPtr ptr++-- typedef enum _DHCP_FORCE_FLAG {+--   DhcpFullForce,+--   DhcpNoForce+-- } DHCP_FORCE_FLAG, *LPDHCP_FORCE_FLAG;+data FORCE_FLAG = FullForce | NoForce+    deriving (Enum)++type RESUME_HANDLE = DWORD
Utils.hs view
@@ -1,38 +1,51 @@-module Utils where
-
-import Control.Applicative
-import Control.Monad (unless)
-import Foreign
-
-import System.Win32.Types
-
--- |Many DHCP functions return a large object composed of pointers to other
---  objects. When it comes time to clean up memory we need to walk through
---  the structure. This helper function calls an action to get a pointer
---  to free (usually a variant of peek), and then calls the supplied free
---  action on it. Finally, the pointer is zeroed out.
-freeptr :: (Ptr b -> IO ()) -> Ptr (Ptr a) -> IO ()
-freeptr rpcfree pptr =
-    peek pptr >>= \ptr ->
-    unless (ptr == nullPtr) $ do
-    rpcfree $ castPtr ptr
-    poke pptr nullPtr
-
--- |Peek a string that might be null. Null pointers become Nothing
-peekMaybeTString :: LPTSTR -> IO (Maybe String)
-peekMaybeTString ptr
-  | ptr == nullPtr = return Nothing
-  | otherwise      = Just <$> peekTString ptr
-
--- |Temporarily marshal a Maybe String. A nothing becomes a null pointer.
-withMaybeTString :: Maybe String -> (LPTSTR -> IO a) -> IO a
-withMaybeTString Nothing    f = f nullPtr
-withMaybeTString (Just str) f = withTString str f
-
-note :: e -> Maybe a -> Either e a
-note e Nothing = Left e
-note _ (Just x) = Right x
-
-fmapL :: (a -> b) -> Either a r -> Either b r
-fmapL f (Left x) = Left (f x)
-fmapL _ (Right x) = (Right x)
+module Utils where++import Control.Applicative+import Foreign++import System.Win32.Types++-- | Perform an action over a double pointer, and then zero it out. If the+-- double pointer is already zeroed out, do nothing and return 'Nothing'.+scrubbing :: (Ptr a -> IO b) -> Ptr (Ptr a) -> IO (Maybe b)+scrubbing f pptr = do+    ptr <- peek pptr+    if ptr == nullPtr+      then return Nothing+      else do+        ret <- f ptr+        poke pptr nullPtr+        return $ Just ret++-- |Perform a cleanup operation on memory.+scrubbing_ :: (Ptr a -> IO ()) -> Ptr (Ptr a) -> IO ()+scrubbing_ f pptr = do+    _ <- scrubbing f pptr+    return ()++scrubWith :: Ptr (Ptr a) -> (Ptr a -> IO b) -> IO (Maybe b)+scrubWith = flip scrubbing++scrubWith_ :: Ptr (Ptr a) -> (Ptr a -> IO ()) -> IO ()+scrubWith_ pptr f = do+    _ <-scrubWith pptr f+    return ()++-- |Peek a string that might be null. Null pointers become Nothing+peekMaybeTString :: LPTSTR -> IO (Maybe String)+peekMaybeTString ptr+  | ptr == nullPtr = return Nothing+  | otherwise      = Just <$> peekTString ptr++-- |Temporarily marshal a Maybe String. A nothing becomes a null pointer.+withMaybeTString :: Maybe String -> (LPTSTR -> IO a) -> IO a+withMaybeTString Nothing    f = f nullPtr+withMaybeTString (Just str) f = withTString str f++note :: e -> Maybe a -> Either e a+note e Nothing = Left e+note _ (Just x) = Right x++fmapL :: (a -> b) -> Either a r -> Either b r+fmapL f (Left x) = Left (f x)+fmapL _ (Right x) = (Right x)
Win32-dhcp-server.cabal view
@@ -1,79 +1,79 @@-Name:                Win32-dhcp-server
-Synopsis:            Win32 DHCP Server Management API.
-Version:             0.2
-License:             BSD3
-License-file:        LICENSE
-Author:              Michael Steele
-Maintainer:          mikesteele81@gmail.com
-Copyright:           2013 Michael Steele
-Homepage:            http://github.com/mikesteele81/win32-dhcp-server
-Bug-Reports:         http://github.com/mikesteele81/win32-dhcp-server/issues
-Category:            System
-Build-type:          Simple
-Cabal-version:       >=1.14
-Tested-With:         GHC == 7.6.3
-Stability:           provisional
-Description:
-  This package provides a partial binding to the Win32 DHCP Server Management
-  API. Its purpose is to query and control a Microsoft DHCP server. Enough
-  functionality is defined so so that Ipv4 client lease and reservation
-  records can be manipulated programmatically.
-  .
-  Here are a few notes on the required environment:
-  .
-  * Only 32-bit executables are supported. This is mainly because pointers
-    are assumed to be 4 bytes wide in a few places. Support for 64-bit
-    executables may be added in the future.
-  .
-  * All library calls should be supported on Windows 7 or above.
-  .
-  /Simple Example and Usage/
-  .
-  @
-  \-\- Print all MAC addresses with an active client lease
-  module Main where
-  .
-  import Data.Ip
-  import Data.Mac
-  import qualified Data.Text.IO as T
-  import System.Win32.DHCP
-  .
-  main :: IO ()
-  main = do
-  &#x20;   api <- loadDHCP
-  &#x20;   clients <- enumClients api context
-  &#x20;   let macs = map (showMac \":\" . clientHardwareAddress) clients
-  &#x20;   mapM_ T.putStrLn macs
-  &#x20; where
-  &#x20;   Right subnet = readIp \"192.168.1.0\"
-  &#x20;   context = Context \"192.168.1.5\" subnet
-  @
-extra-source-files:
-    ChangeLog
-    examples/*.hs
-
-Library
-  Build-depends: base         >= 4.6  && < 4.8
-               , text          > 0.11 && < 1.2
-               , Win32
-               , Win32-errors >= 0.2  && < 1.0
-  default-language: Haskell2010
-  Exposed-modules: Data.Ip
-                 , Data.Mac
-                 , System.Win32.DHCP
-  Ghc-Options: -funbox-strict-fields -Wall
-  other-modules: Utils
-               , System.Win32.DHCP.Internal
-               , System.Win32.DHCP.Client
-               , System.Win32.DHCP.CLIENT_UID
-               , System.Win32.DHCP.DhcpStructure
-               , System.Win32.DHCP.HOST_INFO
-               , System.Win32.DHCP.IP_CLUSTER
-               , System.Win32.DHCP.IP_RANGE
-               , System.Win32.DHCP.LengthBuffer
-               , System.Win32.DHCP.SEARCH_INFO
-               , System.Win32.DHCP.Reservation
-               , System.Win32.DHCP.SUBNET_CLIENT_INFO_ARRAY_V4
-               , System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4
-               , System.Win32.DHCP.SUBNET_ELEMENT_INFO_ARRAY_V4
-               , System.Win32.DHCP.Types
+Name:                Win32-dhcp-server+Synopsis:            Win32 DHCP Server Management API+Version:             0.2.1+License:             BSD3+License-file:        LICENSE+Author:              Michael Steele+Maintainer:          mikesteele81@gmail.com+Copyright:           2013 Michael Steele+Homepage:            http://github.com/mikesteele81/win32-dhcp-server+Bug-Reports:         http://github.com/mikesteele81/win32-dhcp-server/issues+Category:            System+Build-type:          Simple+Cabal-version:       >=1.14+Tested-With:         GHC == 7.8.3+Stability:           provisional+Description:+  This package provides a partial binding to the Win32 DHCP Server Management+  API. Its purpose is to query and control a Microsoft DHCP server. Enough+  functionality is defined so so that Ipv4 client lease and reservation+  records can be manipulated programmatically.+  .+  Here are a few notes on the required environment:+  .+  * Only 32-bit executables are supported. This is mainly because pointers+    are assumed to be 4 bytes wide in a few places. Support for 64-bit+    executables may be added in the future.+  .+  * All library calls should be supported on Windows 7 or above.+  .+  /Simple Example and Usage/+  .+  @+  \-\- Print all MAC addresses with an active client lease+  module Main where+  .+  import Data.Ip+  import Data.Mac+  import qualified Data.Text.IO as T+  import System.Win32.DHCP+  .+  main :: IO ()+  main = do+  &#x20;   api <- loadDHCP+  &#x20;   clients <- enumClients api context+  &#x20;   let macs = map (showMac \":\" . clientHardwareAddress) clients+  &#x20;   mapM_ T.putStrLn macs+  &#x20; where+  &#x20;   Right subnet = readIp \"192.168.1.0\"+  &#x20;   context = Context \"192.168.1.5\" subnet+  @+extra-source-files:+    ChangeLog+    examples/*.hs++Library+  Build-depends: base         >= 4.6  && < 4.8+               , text         >  0.11 && < 1.2+               , Win32        >= 2.2  && < 2.4+               , Win32-errors >= 0.2  && < 1.0+  default-language: Haskell2010+  Exposed-modules: Data.Ip+                 , Data.Mac+                 , System.Win32.DHCP+  Ghc-Options: -funbox-strict-fields -Wall+  other-modules: Utils+               , System.Win32.DHCP.Internal+               , System.Win32.DHCP.Client+               , System.Win32.DHCP.CLIENT_UID+               , System.Win32.DHCP.DhcpStructure+               , System.Win32.DHCP.HOST_INFO+               , System.Win32.DHCP.IP_CLUSTER+               , System.Win32.DHCP.IP_RANGE+               , System.Win32.DHCP.LengthBuffer+               , System.Win32.DHCP.SEARCH_INFO+               , System.Win32.DHCP.Reservation+               , System.Win32.DHCP.SUBNET_CLIENT_INFO_ARRAY_V4+               , System.Win32.DHCP.SUBNET_ELEMENT_DATA_V4+               , System.Win32.DHCP.SUBNET_ELEMENT_INFO_ARRAY_V4+               , System.Win32.DHCP.Types
examples/cabal.hs view
@@ -1,22 +1,22 @@--- This is the example used in the main .cabal file.
-
-{-# LANGUAGE OverloadedStrings #-}
-
--- Print all MAC addresses with an active client lease
-module Main where
-
-import Data.Ip
-import Data.Mac
-import qualified Data.Text.IO as T
-import System.Win32.DHCP
-
-main :: IO ()
-main = do
-    api <- loadDHCP
-    clients <- enumClients api context
-    let macs = map (showMac ":" . clientHardwareAddress) clients
-    mapM_ T.putStrLn macs
- where
-    Right subnet = readIp "192.168.1.0"
-    context = Context "192.168.1.5" subnet
-
+-- This is the example used in the main .cabal file.++{-# LANGUAGE OverloadedStrings #-}++-- Print all MAC addresses with an active client lease+module Main where++import Data.Ip+import Data.Mac+import qualified Data.Text.IO as T+import System.Win32.DHCP++main :: IO ()+main = do+    api <- loadDHCP+    clients <- enumClients api context+    let macs = map (showMac ":" . clientHardwareAddress) clients+    mapM_ T.putStrLn macs+ where+    Right subnet = readIp "192.168.1.0"+    context = Context "192.168.1.5" subnet+