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 +0/−75
- NetSNMP.cabal +6/−6
- README +0/−27
- src/Network/Protocol/NetSNMP.hsc +91/−29
− 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-