packages feed

NetSNMP 0.1.6 → 0.2.0.2

raw patch · 4 files changed

+97/−137 lines, 4 filesnew-uploaderPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Network.Protocol.NetSNMP: snmpBulkWalk :: Hostname -> Community -> String -> IO (Either String [SnmpResult])

Files

− BUGS
@@ -1,75 +0,0 @@--**-** BUGS file for the Haskell binding to net-snmp-**--The binding is new and largely untested; there are sure to be bugs and-many omissions.--** -** SECURITY CONSIDERATIONS-** --This code has not undergone any security review, and is very immature.-It would be inappropriate to use this binding where security is a-priority, without taking appropriate measures.--** -** MISSING FEATURES-** --This binding only supports a subset of the NetSNMP library's-"single-session" api.  Only synchronous queries are supported, and only-the get, next, and walk operations.--No support for bulk get requests.--No support (and none planned) for snmp traps.--Many SNMP / ASN.1 data types are unsupported as yet.--This binding only addresses use of the library for an snmp client.  No-effort is made to support snmp agent or proxy operations.--No support for snmpv3; currently only snmpv1 and snmpv2c with admittedly-insecure SNMP community strings.  The documentation for the thread-safe API-in the net-snmp C library suggests that the snmpv3 support is probably-not thread-safe.--This binding doesn't do anything with the library's log handling, and-the library's default action is to spam stderr.  There should at least-be a way to prevent this, and it should possibly suppress the logs by-default.--This code should be publicly hosted, perhaps on code.haskell.org.  It-should use a public trac.--** -** BROKENNESS-** --Looking up OIDs by name doesn't seem to work yet.  Once it's fixed-snmpwalk probably needs to be fixed as well.  It uses `isPrefixOf` to-determine when the walk is over, which won't work if the original-oid isn't dotted-decimal.--There are too many things called 'session' in the library, and in the-binding:--  the C struct snmp_session.  The 'single session' examples use *3* of-   these, with subtly different purposes, never fully explained.--  data SnmpSession, the binding's representation of 'struct snmp_session'--  data Session, the binding's abstract session container, holding-   the above and managing C memory (de)allocation--snmpWalk doesn't check for cyclical responses from an agent; reportedly-there are agents with this misfeature.  Currently if it happens snmpWalk-will happily follow the cycle until it runs out of memory or stack.--I don't know how to intelligently set the alignment parameter for data-types representing C structs in their Storable instances.  I'm using 16-bytes as a (hopefully) conservative setting, but isn't there a way to-detect and mimic what C uses?--The binding is currently one monolithic .hsc file.  It should probably-be organized into a few modules.-
NetSNMP.cabal view
@@ -1,6 +1,6 @@  Name:               NetSNMP-Version:            0.1.6+Version:            0.2.0.2 Synopsis:           Bindings for net-snmp's C API for clients Description:        Network.Protocol.NetSNMP is a partial binding to the                     library portion of the net-snmp package.  It uses the@@ -10,15 +10,15 @@                     expanded functionality, and especially if you would                     like to contribute. Category:           Network-Copyright:          (c) John Dorsey 2009 Cabal-Version:      >= 1.2 License:            BSD3 License-file:       LICENSE-Author:             John Dorsey <haskell@colquitt.org>-Maintainer:         John Dorsey <haskell@colquitt.org>+Author:             Pavlo Kerestey and Humane Software for Global Access Internet Services GmbH+Maintainer:         Pavlo Kerestey <pavlo@kerestey.net> Stability:          alpha Build-type:         Simple-Bug-reports:        mailto:haskell@colquitt.org+Homepage:           https://github.com/ptek/netsnmp+Bug-reports:        https://github.com/ptek/netsnmp/issues  library   hs-source-dirs:   src@@ -27,4 +27,4 @@   exposed-modules:  Network.Protocol.NetSNMP   extensions:       ForeignFunctionInterface, CPP, EmptyDataDecls   extra-libraries:  netsnmp-+  ghc-options:      -static -optc-static -optl-static -optl-pthread
− README
@@ -1,27 +0,0 @@--The (Haskell) NetSNMP package is a binding for a subset of the libsnmp-api provided by the NetSNMP project.  It is initially being developed-against NetSNMP version 5.4.1, but probably works with most or all 5.*-versions.--Currently supported protocol versions:-  SNMPv1  -- original SNMP-  SNMPv2c -- v2 protocol with old community-based security--Not yet supported:-  SNMPv3  -- v2 protocol plus new v3 party-based security--Other versions are not supported by the underlying library, and are-unlikely to ever be supported by this package.-  SNMPsec -- first attempt to add strong security to SNMPv1-  SNMPv2p -- initial SNMPv2, with v2 party-based security-  SNMPv2u -- v2 protocol with user-based security-  SNMPv2* -- proprietary hybrid variant of v2c and v2p--This binding requires that net-snmp be built without-NETSNMP_DISABLE_SNMPV1 or NETSNMP_DISABLE_SNMPV2C.  I'd love to support-those options, since they may encourage better security, but I won't be able-to prioritize this in the forseeable future.--See also: BUGS-
src/Network/Protocol/NetSNMP.hsc view
@@ -1,5 +1,5 @@ -{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}+{-# LANGUAGE CPP, DoAndIfThenElse, ForeignFunctionInterface, EmptyDataDecls #-}  ---------------------------------------------------------------------- -- |@@ -24,7 +24,7 @@   -- ** Library Initialization   initialize,   -- ** Queries-  snmpGet, snmpNext, snmpWalk,+  snmpGet, snmpNext, snmpWalk, snmpBulkWalk,   -- ** Miscellany   showASNValue,   )@@ -88,7 +88,7 @@   | Integer32   Int32          -- ^@ASN_INTEGER@  32bit signed   | Integer64   Int64          -- ^@ASN_INTEGER64@  64bit signed   | Counter32   Word32         -- ^@ASN_COUNTER@ 32bit nondecreasing-  | Counter64   Word64         -- ^@ASN_COUNTER64@ 64bit nondecreasing+  | Counter64   Word64          -- ^@ASN_COUNTER64@ 64bit nondecreasing   | Unsigned32  Word32         -- ^@ASN_UNSIGNED@ 32bit unsigned   | Unsigned64  Word64         -- ^@ASN_UNSIGNED64@ 64bit unsigned   | Gauge32     Word32         -- ^@ASN_GAUGE@ 32bit signed with min and max@@ -314,14 +314,14 @@ --     through all objects underneath that OID in the mib tree. --     On failure, returns a descriptive error message. ----- The current implementation uses a series of next operations, but an--- implementation using bulk requests would be more efficient.+-- This implementation uses a series of next operations and is not very+-- ressource friendly. Consider using snmpBulkWalk for better performance -- -- Examples: ----- * snmpWalk \"localhost\" \"public\" \".1.3.6.1.2.1.1\"+-- * snmpWalk snmp_version_2c \"localhost\" \"public\" \".1.3.6.1.2.1.1\" ----- * snmpWalk \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1\"+-- * snmpWalk snmp_version_2c \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1\" snmpWalk   :: SnmpVersion -- ^'snmp_version_1' or 'snmp_version_2c'   -> Hostname    -- ^IP or hostname of the agent to be queried.  May have@@ -348,17 +348,72 @@             return (v:vs)           | otherwise -> return [] -- throwT "end of walk" -- return [] ++-- |Same as snmpWalk but implemented with bulk requests+--+-- Examples:+--+-- * snmpBulkWalk \"localhost\" \"public\" \".1.3.6.1.2.1.1\"+--+-- * snmpBulkWalk \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1\"+snmpBulkWalk +  :: Hostname    -- ^IP or hostname of the agent to be queried.  May have+                 --     prefix of @tcp:@ or suffix of @:port@+  -> Community   -- ^SNMP community (password)+  -> String      -- ^OID to be queried+  -> IO (Either String [SnmpResult])+snmpBulkWalk hostname community walkoid =+    withCString hostname  $ \cshost  ->+    withCString community $ \cscomm  ->+    alloca                $ \session ->+    runTrouble $ bracketT+      (readyCommunitySession snmp_version_2c cshost cscomm session)+      closeSession+      (bulkWalk walkoid walkoid)+  where+    bulkWalk :: String -> String -> Session -> Trouble [SnmpResult]+    bulkWalk rootoid startoid session = do+      vals <- filter (\r -> (oid r) `isSubIdOf` rootoid) <$> mkSnmpBulkGet 0 50 startoid session+      case vals of+        [] -> return []+        rs -> (vals ++) <$> bulkWalk rootoid (oid (last rs)) session+    isSubIdOf :: String -> String -> Bool+    isSubIdOf oa ob = (splitAt '.' ob) `isPrefixOf` (splitAt '.' oa)+    splitAt :: Char -> String -> [String]+    splitAt c s = case dropWhile (c ==) s of+                      "" -> []+                      s' -> w : splitAt c s''+                            where (w, s'') = break (c ==) s'+  +-- getbulk, using session info from a 'data Session' and+-- the supplied oid+-- It is the caller's obligation to ensure the session's validity.  +mkSnmpBulkGet :: CLong -> CLong -> String -> Session -> Trouble [SnmpResult]+mkSnmpBulkGet non_repeaters max_repetitions oid session =+  allocaArrayT (fromIntegral max_oid_len) $ \oids -> do+  let version = getVersion session+  pdu_req <- buildPDU snmp_msg_getbulk oid oids version+  pokePDUNonRepeaters pdu_req non_repeaters+  pokePDUMaxRepetitions pdu_req max_repetitions+  dispatchSnmpReq pdu_req session+ -- get or getnext, using session info from a 'data Session' and -- the supplied oid -- It is the caller's obligation to ensure the session's validity. mkSnmpGet :: SnmpPDUType -> Session -> String -> Trouble SnmpResult-mkSnmpGet pdutype session oid =+mkSnmpGet pdutype session oid = do+  res <- (allocaArrayT (fromIntegral max_oid_len) $ \oids -> do+         let version = getVersion session+         pdu_req <- buildPDU pdutype oid oids version+         dispatchSnmpReq pdu_req session)+  if (res == []) then throwT ("Could not get the snmp value at " ++ oid)+  else return $ head res++dispatchSnmpReq :: Ptr SnmpPDU -> Session -> Trouble [SnmpResult]+dispatchSnmpReq pdu_req session = do   allocaT $ \response_ptr -> do-  allocaArrayT (fromIntegral max_oid_len) $ \oids -> do-  let version = getVersion session   let sessp = getSessp session   let sptr = getSptr session-  pdu_req <- buildPDU pdutype oid oids version   pokeT response_ptr nullPtr   handleT     (\s -> do@@ -371,9 +426,9 @@       errstat <- peekPDUErrstat pdu_resp       when (errstat /= snmp_err_noerror) (throwT "response PDU error")       rawvars <- peekPDUVariables pdu_resp-      vars <- extractVar rawvars+      vars <- extractVars rawvars       unless (pdu_resp == nullPtr) $ t_snmp_free_pdu pdu_resp-      return vars)+      return vars)    -- caller is obliged to ensure rv is valid and non-null vlist2oid :: Ptr CVarList -> Trouble String@@ -405,13 +460,10 @@     _ | t == asn_opaque       -> extractOpaque       rv     _ | t == asn_integer      -> extractIntegralType rv Integer32     _ | t == asn_unsigned     -> extractIntegralType rv Unsigned32-    _ | t == asn_counter64    -> extractIntegralType rv Counter64-    _ | t == asn_integer64    -> extractIntegralType rv Integer64-    _ | t == asn_unsigned64   -> extractIntegralType rv Unsigned64+    _ | t == asn_counter64    -> extractIntegral64Type rv Counter64+    _ | t == asn_integer64    -> extractIntegral64Type rv Integer64+    _ | t == asn_unsigned64   -> extractIntegral64Type rv Unsigned64     _ | t == asn_object_id    -> extractOID          rv-    -- _ | t == asn_boolean      -> extractBoolean      rv-    -- _ | t == asn_double       -> extractDouble       rv-    -- _ | t == asn_float        -> extractFloat        rv     _ | t == asn_null         -> return Null     _ -> do           descr <- rawvar2cstring rv@@ -436,6 +488,11 @@   n <- fromIntegral <$> peekT intptr   return (constructor n) +extractIntegral64Type rv constructor = do+  ptr <- peekVariableValInt rv+  (high:low:[]) <- peekArrayT 2 (castPtr ptr) :: Trouble [Word64]+  return (constructor (fromIntegral ((high * (2 ^ 32) + low) :: Word64)))+ extractIpAddress rv = do   ptr <- peekVariableValInt rv   octets <- peekArrayT 4 (castPtr ptr) :: Trouble [Word8]@@ -583,6 +640,12 @@ pokePDUCommand :: Ptr SnmpPDU -> CInt -> Trouble () pokePDUCommand p t = hoistT $ #{poke struct snmp_pdu , command} p t +pokePDUNonRepeaters :: Ptr SnmpPDU -> CLong -> Trouble ()+pokePDUNonRepeaters p n = hoistT $ #{poke struct snmp_pdu , non_repeaters} p n++pokePDUMaxRepetitions :: Ptr SnmpPDU -> CLong -> Trouble ()+pokePDUMaxRepetitions p r = hoistT $ #{poke struct snmp_pdu , max_repetitions} p r+ -- -- The C library layer --@@ -678,15 +741,15 @@ t_snmp_sess_synch_response sessp sptr pdu_req response_ptr = Trouble $ do   success <- c_snmp_sess_synch_response sessp pdu_req response_ptr   -- snmpSessError was giving bus errors on x86_64-  -- if (success == snmp_stat_success)-  --   then return (Right ())-  --   else Left <$> snmpSessError sptr-  return $ case () of-    _ | success == snmp_stat_success -> Right ()-      | success == snmp_stat_error   -> Left "snmp_sess_synch_response error"-      | success == snmp_stat_timeout -> Left "snmp_sess_synch_response timeout"-      | otherwise -> Left-          ("snmp_sess_synch_response unknown error code " ++ show success)+  if (success == snmp_stat_success)+    then return (Right ())+    else Left <$> snmpError sptr+  -- return $ case () of+  --   _ | success == snmp_stat_success -> Right ()+  --     | success == snmp_stat_error   -> Left "snmp_sess_synch_response error"+  --     | success == snmp_stat_timeout -> Left "snmp_sess_synch_response timeout"+  --     | otherwise -> Left+  --         ("snmp_sess_synch_response unknown error code " ++ show success)  -- Deallocate PDU struct foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_free_pdu"@@ -862,4 +925,3 @@  predToMaybe :: (a -> Bool) -> b -> a -> Maybe b predToMaybe p b a = if (p a) then Just b else Nothing-