NetSNMP 0.1.2 → 0.1.6
raw patch · 4 files changed
+115/−112 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- BUGS +3/−4
- NetSNMP.cabal +1/−1
- README +2/−2
- src/Network/Protocol/NetSNMP.hsc +109/−105
BUGS view
@@ -32,7 +32,9 @@ 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.+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@@ -50,9 +52,6 @@ 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.--This binding's exception checking is spotty and amateurish. Some of this-is because I haven't found any decent API docs for the net-snmp library. There are too many things called 'session' in the library, and in the binding:
NetSNMP.cabal view
@@ -1,6 +1,6 @@ Name: NetSNMP-Version: 0.1.2+Version: 0.1.6 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
README view
@@ -20,8 +20,8 @@ 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 haven't-made the effort.+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
@@ -12,11 +12,6 @@ -- -- This is a binding to Net-SNMP version 5, <http://www.net-snmp.org/>. --- FIXME:--- issue better errors using info from snmpError and snmpSessError--- make better use of catchT--- write bracketT- {- License: BSD3. See included LICENSE and README files. -} module Network.Protocol.NetSNMP (@@ -32,31 +27,15 @@ snmpGet, snmpNext, snmpWalk, -- ** Miscellany showASNValue,- -- - -- Unexposed:- --- -- Trouble(..), SnmpSession, SnmpPDU, OIDPart, readyCommunitySession,- -- snmp_msg_get, rawvar2cstring, buildPDU, max_oid_len, snmp_stat_success,- -- snmp_err_noerror, extractVar,- -- c_init_snmp, c_snmp_sess_init, c_snmp_sess_open, c_snmp_sess_session,- -- c_snmp_pdu_create, c_get_node, c_read_objid, c_snmp_parse_oid,- -- c_snmp_add_null_var, c_snmp_sess_synch_response, c_snmp_free_pdu,- -- c_snmp_sess_close, c_snmp_sess_send, c_print_variable, c_snprint_by_type,- -- t_init_snmp, t_snmp_sess_init, t_snmp_sess_open, t_snmp_sess_session,- -- t_snmp_pdu_create, t_get_node, t_read_objid, t_snmp_parse_oid,- -- t_snmp_add_null_var, t_snmp_sess_synch_response, t_snmp_free_pdu,- -- t_snmp_sess_close, t_snmp_sess_send, t_print_variable, t_snprint_by_type, ) where -import Control.Applicative-import Control.Monad-import Data.List-import Foreign-import Foreign.C.String-import Foreign.C.Types-import Foreign.ForeignPtr--- import System.IO+import Control.Applicative+import Control.Monad+import Data.List+import Foreign+import Foreign.C.String+import Foreign.C.Types #include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-includes.h>@@ -73,11 +52,8 @@ -- and session parameters. Meeting place for managing session memory. data Session = Session { getVersion :: SnmpVersion- , getSession :: ForeignPtr SnmpSession , getSessp :: Ptr SnmpSession , getSptr :: Ptr SnmpSession- , sessionHostname :: ForeignPtr CChar- , sessionCommunity :: ForeignPtr CChar } data SnmpSession -- C struct snmp_session@@ -237,6 +213,8 @@ #{enum CInt, , snmp_stat_success = STAT_SUCCESS+ , snmp_stat_error = STAT_ERROR+ , snmp_stat_timeout = STAT_TIMEOUT , snmp_err_noerror = SNMP_ERR_NOERROR } @@ -250,40 +228,38 @@ -- other NetSNMP functions, and before starting extra threads, as the -- mib compiler is not thread-safe. initialize :: IO ()-initialize = withCString "Haskell bindings" c_init_snmp+initialize = do+ withCString "Haskell bindings" c_init_snmp+ withCString "127.0.0.1" $ \localhost -> do+ withCString "public" $ \public -> do+ alloca $ \session -> runTrouble $+ (readyCommunitySession snmp_version_2c localhost public session)+ >>= closeSession+ return () -- |Create an abstract session, suitable for reuse, responsible -- for freeing the string components readyCommunitySession :: SnmpVersion- -> Hostname- -> Community+ -> CString -- Hostname+ -> CString -- Community+ -> Ptr SnmpSession -- "session" -> Trouble Session -- return abstract session-readyCommunitySession version hostname community = do- session <- mallocT- hostname_cstr <- hoistT $ newCString hostname- community_cstr <- hoistT $ newCString community- community_len <- t_strlen community_cstr- session_foreign <- hoistT $ newForeignPtr finalizerFree session- hostname_foreign <- hoistT $ newForeignPtr finalizerFree hostname_cstr- community_foreign <- hoistT $ newForeignPtr finalizerFree community_cstr- t_snmp_sess_init session- pokeSessPeername session hostname_cstr+readyCommunitySession version hostname community session = do+ community_len <- t_strlen community+ t_snmp_sess_init session+ pokeSessPeername session hostname pokeSessVersion session (unSnmpVersion version)- pokeSessCommunity session community_cstr+ pokeSessCommunity session community pokeSessCommLen session community_len- sessp <- t_snmp_sess_open session- sptr <- t_snmp_sess_session sessp- return $ Session version session_foreign sessp sptr- hostname_foreign community_foreign+ sessp <- t_snmp_sess_open session+ sptr <- t_snmp_sess_session sessp+ return $ Session version sessp sptr -- |Immediately destroy/free the Session. closeSession :: Session -> Trouble ()-closeSession session = hoistT $ do- c_snmp_sess_close (getSessp session)- finalizeForeignPtr (sessionCommunity session)- finalizeForeignPtr (sessionHostname session)- finalizeForeignPtr (getSession session)+-- frees the SnmpSession object allocated by readyCommunitySession+closeSession session = hoistT (c_snmp_sess_close (getSessp session)) -- |Simple community-authenticated SNMP get. Returns the object -- queried, or a descriptive error message.@@ -300,10 +276,14 @@ -> Community -- ^SNMP community (password) -> String -- ^OID to be queried -> IO (Either String SnmpResult)-snmpGet version hostname community oid = runTrouble $ bracketT- (readyCommunitySession version hostname community) -- session- closeSession- (flip (mkSnmpGet snmp_msg_get) oid)+snmpGet version hostname community oid =+ withCString hostname $ \cshost ->+ withCString community $ \cscomm ->+ alloca $ \session ->+ runTrouble $ bracketT+ (readyCommunitySession version cshost cscomm session)+ closeSession+ (flip (mkSnmpGet snmp_msg_get) oid) -- |Simple community-authenticated SNMP getnext. Returns the first object -- after the OID queried, or a descriptive error message.@@ -320,10 +300,14 @@ -> Community -- ^SNMP community (password) -> String -- ^OID to be queried -> IO (Either String SnmpResult)-snmpNext version hostname community oid = runTrouble $ bracketT- (readyCommunitySession version hostname community) -- session- closeSession- (flip (mkSnmpGet snmp_msg_getnext) oid)+snmpNext version hostname community oid =+ withCString hostname $ \cshost ->+ withCString community $ \cscomm ->+ alloca $ \session ->+ runTrouble $ bracketT+ (readyCommunitySession version cshost cscomm session)+ closeSession+ (flip (mkSnmpGet snmp_msg_getnext) oid) -- |Simple community-authenticated SNMP walk. Returns a list of objects, -- starting with the object after the OID queried, and continuing@@ -345,10 +329,14 @@ -> Community -- ^SNMP community (password) -> String -- ^OID to be queried -> IO (Either String [SnmpResult])-snmpWalk version hostname community walkoid = runTrouble $ bracketT- (readyCommunitySession version hostname community) -- session- closeSession- (go walkoid . mkSnmpGet snmp_msg_getnext)+snmpWalk version hostname community walkoid =+ withCString hostname $ \cshost ->+ withCString community $ \cscomm ->+ alloca $ \session ->+ runTrouble $ bracketT+ (readyCommunitySession version cshost cscomm session)+ closeSession+ (go walkoid . mkSnmpGet snmp_msg_getnext) where go :: String -> (String -> Trouble SnmpResult) -> Trouble [SnmpResult] go oid next = do@@ -370,16 +358,22 @@ let version = getVersion session let sessp = getSessp session let sptr = getSptr session- pokeT response_ptr nullPtr -- probably not needed pdu_req <- buildPDU pdutype oid oids version- t_snmp_sess_synch_response sessp sptr pdu_req response_ptr- pdu_resp <- peekT response_ptr- errstat <- peekPDUErrstat pdu_resp- when (errstat /= snmp_err_noerror) (throwT "response PDU error")- rawvars <- peekPDUVariables pdu_resp- vars <- extractVar rawvars- unless (pdu_resp == nullPtr) $ t_snmp_free_pdu pdu_resp >> return ()- return vars+ pokeT response_ptr nullPtr+ handleT+ (\s -> do+ pdu_resp <- peekT response_ptr+ unless (pdu_resp == nullPtr) $ t_snmp_free_pdu pdu_resp+ throwT s)+ (do+ t_snmp_sess_synch_response sessp sptr pdu_req response_ptr+ pdu_resp <- peekT response_ptr+ errstat <- peekPDUErrstat pdu_resp+ when (errstat /= snmp_err_noerror) (throwT "response PDU error")+ rawvars <- peekPDUVariables pdu_resp+ vars <- extractVar rawvars+ unless (pdu_resp == nullPtr) $ t_snmp_free_pdu pdu_resp+ return vars) -- caller is obliged to ensure rv is valid and non-null vlist2oid :: Ptr CVarList -> Trouble String@@ -487,6 +481,8 @@ Null -> "ASN_NULL" Unsupported t s -> "Unknown type " ++ show t ++ ": " ++ s +-- allocates space for the pdu request; snmp_sess_synch_response+-- appears to free the request pdu buildPDU :: SnmpPDUType -- eg. snmp_msg_get -> String -- eg. ".1.3.6.1.2.1.1.1.0"@@ -495,7 +491,7 @@ -> Trouble (Ptr SnmpPDU) -- returns pdu and oid length buildPDU pdutype oid oids version = withCStringT oid $ \oid_cstr ->- allocaT $ \oidlen_ptr -> do+ allocaT $ \oidlen_ptr -> do pdu_req <- t_snmp_pdu_create pdutype pokePDUVersion pdu_req (unSnmpVersion version) pokePDUCommand pdu_req (unSnmpPDUType pdutype)@@ -527,16 +523,6 @@ peekCStringT = hoistT1 peekCString peekCStringLenT = hoistT1 peekCStringLen --- Is it worth putting the error check here? No, for now.-mallocT :: (Storable a) => Trouble (Ptr a)-mallocT = hoistT malloc--mallocArrayT :: (Storable a) => Int -> Trouble (Ptr a)-mallocArrayT = hoistT1 mallocArray--mallocArray0T :: (Storable a) => Int -> Trouble (Ptr a)-mallocArray0T = hoistT1 mallocArray0- peekT :: (Storable a) => Ptr a -> Trouble a peekT = hoistT1 peek @@ -611,8 +597,6 @@ foreign import ccall unsafe "net-snmp/net-snmp-includes.h init_snmp" c_init_snmp :: CString -> IO () --- t_init_snmp = hoistT1 c_init_snmp- -- "init session" -- JD: Apparently this only sets parameters in the struct snmp_session. foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_sess_init"@@ -632,6 +616,9 @@ -- Third and final session initialization routine. -- JD: This seems to be used to coordinate asynchronous queries in the -- (thread safe) session, and in error tracking/reporting.+--+-- from session_api.h: Do NOT free memory returned by snmp_sess_session+-- foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_sess_session" c_snmp_sess_session :: Ptr SnmpSession -> IO (Ptr SnmpSession) @@ -680,23 +667,26 @@ t_snmp_add_null_var = hoistT3 c_snmp_add_null_var -- Send request PDU and wait for response.-foreign import ccall unsafe+foreign import ccall safe "net-snmp/net-snmp-includes.h snmp_sess_synch_response" c_snmp_sess_synch_response :: Ptr SnmpSession -> Ptr SnmpPDU -> Ptr (Ptr SnmpPDU) -> IO CInt --- t_snmp_sess_synch_response = hoistTE3--- (predToMaybe (/= snmp_stat_success) "snmp_sess_synch_response failed")--- c_snmp_sess_synch_response- -- improved (?) version with fuller error handling t_snmp_sess_synch_response :: Ptr SnmpSession -> Ptr SnmpSession -> Ptr SnmpPDU -> Ptr (Ptr SnmpPDU) -> Trouble () t_snmp_sess_synch_response sessp sptr pdu_req response_ptr = Trouble $ do success <- c_snmp_sess_synch_response sessp pdu_req response_ptr- if (success == snmp_stat_success)- then return (Right ())- else Left <$> snmpSessError sptr+ -- 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) -- Deallocate PDU struct foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_free_pdu"@@ -715,7 +705,7 @@ -- c_snmp_sess_send :: Ptr SnmpSession -> Ptr SnmpPDU -> IO CInt -- Print result value to stdout-foreign import ccall unsafe "net-snmp/net-snmp-includes.h print_variable"+foreign import ccall safe "net-snmp/net-snmp-includes.h print_variable" c_print_variable :: Ptr OIDpart -> CSize -> Ptr CVarList -> IO () t_print_variable = hoistT3 c_print_variable@@ -752,18 +742,25 @@ -- snmp_sess_open failure. -- library/snmp_api.h -- void snmp_sess_error(void *, int *, int *, char **);-foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_error"+foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_sess_error" c_snmp_sess_error :: Ptr SnmpSession -> Ptr CInt -> Ptr CInt -> Ptr CString -> IO () +-- Using abbreviated error routine because th c_snmp_sess_error+-- call appears to be giving bus errors on my x86_64 test machine. snmpSessError :: Ptr SnmpSession -> IO String-snmpSessError p = do+snmpSessError sptr =+ return ("disabled snmp_sess_error check; sptr=" ++ show sptr)++_snmpSessError :: Ptr SnmpSession -> IO String+_snmpSessError sptr | sptr == nullPtr = return "snmp error; null sptr"+_snmpSessError sptr = do alloca $ \libp -> do -- pointer to library error code alloca $ \sysp -> do -- pointer to system error code alloca $ \errp -> do -- pointer to error CString- c_snmp_sess_error p libp sysp errp- liberr <- peek libp- syserr <- peek sysp+ c_snmp_sess_error sptr libp sysp errp+ liberr <- peek libp :: IO CInt+ syserr <- peek sysp :: IO CInt cserr <- peek errp err <- peekCString cserr free cserr@@ -821,13 +818,20 @@ handleT :: (String -> Trouble a) -> Trouble a -> Trouble a handleT = flip catchT +-- _bracketT :: Trouble a -> (a -> Trouble b) -> (a -> Trouble c) -> Trouble c+-- _bracketT before after thing = do+-- a <- before+-- handleT (\s -> after a >> throwT s) $ do+-- result <- thing a+-- after a+-- return result+ bracketT :: Trouble a -> (a -> Trouble b) -> (a -> Trouble c) -> Trouble c bracketT before after thing = do a <- before- handleT (\s -> after a >> throwT s) $ do- result <- thing a- after a- return result+ result <- handleT (\s -> after a >> throwT s) (thing a)+ after a+ return result -- Routines to 'hoist' anything with IO return type into the -- equivalent with (Trouble a) ie. IO (Either String a) return type.