Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

A C++ Wrapper for the WinSNMP Library

4.93/5 (23 votes)
16 Sep 20058 min read 1   10.7K  
A C++ library for developing SNMP managers (clients) on Windows.

SNMP walk console screenshot

Introduction

This article presents an example of how to build a C++ wrapper over a Windows API, in this case the WinSNMP library, in order to hide the interface complexity from the user. The wrapper, referred to as library in this article, intends to ease development of WinSNMP managers (clients) and makes use of the STL. Please note that it is not the intent of the article to present any help on SNMP or the WinSNMP API itself, nor to provide a full featured library. For that other sources are available, namely other CodeProject articles or open source projects.

Background and Motivation

The contents of this article are the result of some cleaning up around some code I had stashed away from some experiments on the STL and the WinSNMP library I performed some time ago. The whole idea of producing the article is just to share the work with the community.

Within the Example Files

The files include the complete source code (text format) and UML model (Visio format) under the GPL license, together with VS 2005 Beta 2 project files. Also included are compiled and running versions of the example tools: SNMPGET, SNMPSET, SNMPWALK, SNMPTRAP and SNMPSCAN.

Understanding the Model

The Big Picture

If you understand UML, you can build the big picture in your mind by examining the class diagram of figure 1.

UML Class Diagram

Figure 1 - UML Class Diagram.

In this diagram all the library classes are represented, including main relationships and public methods.

The SNMPManager Class

This class wraps the WinSNMP startup and shutdown procedures. To use the library, you need to invoke the startup method on the manager before any other operation. A pointer to the single instance of the manager can be obtained via the getManager method (by the way, this is a spot to see a working implementation of the singleton pattern). The WinSNMP and library clean up code is called automatically in the class destructor that is called just before your application terminates. The getDisplayInformation method will return a string with various information items on the installed WinSNMP library such as version, vendor and options.

The SNMPTracer Class

The SNMPTracer is a singleton class which is used to control the tracing of protocol messaging which may be used for testing/debugging purposes (to see it working, run any of the examples with the -T option). Should trace be enabled (via the enable method), a string representation of all exchanged SNMP protocol data units is sent to the console during library operation, including time tagging. You can browse the code to see how to handle the console via the Win32 API.

The SNMPObject Class

This is the most interesting and useful class in the library. An instance of this class represents an item (or variable) of SNMP data including addressing (OID) and/or value (to read or write).

OID

The object identifier of an SNMPObject can be accessed via getOID and setOID methods in string format. All of the parsing and conversion to binary format is handled internally. Helper methods (compareOID) for comparing OIDs with the OID of the object are also available. These map to the lexicographical OID comparison function of the WinSNMP library.

Syntax

The syntax methods (getSyntax, setSyntax and getSyntaxAsString) will return or set the internal type of the object in binary or string form. Available types are: SNMP_INT, SNMP_OCTETS, SNMP_OPAQUE, SNMP_BITS, SNMP_CNTR64, SNMP_CNTR32, SNMP_GAUGE32, SNMP_TIMETICKS, SNMP_UINT32, SNMP_OID and SNMP_IPADDR. Note that these constants are not necessarily the same as the corresponding constants of the WinSNMP library. The idea here is to build a wrapper library that does not enforce the user to be dependent on the WinSNMP library itself. This pattern may be rather useful if you plan on replacing the WinSNMP library with another implementation in the future and would like to avoid rewriting the user code.

Value

The value of the object can be set or read using the getValueXXX and setValueXXX methods. The string based methods are particularly helpful since they provide all the code to parse and produce values of any type. Check the internal code to see how this is achieved.

Display Information

As with any other class in the library, the getDisplayInformation returns a string with human-readable information on the object, in this case the OID, the value and the syntax.

The SNMPSession and SNMPRemoteAgent Classes

An object of the SNMPSession class holds information on one or more SNMP remote agents. This class is not that important at this point unless you understand the WinSNMP library. You don't even need to use it as you can see in the examples or the implementation of the SNMPRemoteAgent class. Objects of the SNMPRemoteAgent class hold information on SNMP remote agents. This includes name (host name or IP address of the manager machine), SNMP community name, and server remote port. These attributes are set in the class constructor. For each remote agent you need to exchange information with, an object of the SNMPRemoteAgent class must be instantiated.

SNMP Requests

All SNMP requests are performed using instances of the SNMPRequest abstract class. For each SNMP exchange type (Get, Set or Get Next), a concrete subclass exists that should be used according to the SNMP service you wish to access. To issue an SNMP request you must create an SNMPRequest object and call its execute method. Then you can wait on the object until the transaction terminates (via wait method) or periodically check if the operation is completed (via isTerminated method). The implementation is inherently asynchronous and performs no retries. Canceling and obtaining result information on the status of the last execution request is possible via the cancel and timedOut/succeeded/getErrorXXX methods, respectively. The resulting SNMP objects (which may be more than one) can be accessed via the getResultCount and getResult methods.

Get Requests

Get requests can be created by instantiating the SNMPRemoteAgentGetRequest class. The constructor receives the OIDs to retrieve from and the remote agent to request the objects from. To learn more, check the SNMPGET example.

Set Requests

Set requests can be created by instantiating the SNMPRemoteAgentSetRequest class. In this case, the input SNMPObjects include both the OID and the value to set in the service request. To learn more, check the SNMPSET example.

Get Next Requests

Similar to the Get requests, the Get Next requests (SNMPRemoteAgentGetNextRequest) return the objects with adjacent OIDs. To learn more, check the SNMPWALK example.

The SNMPTrap Class

The SNMPTrap class handling is very similar to the request classes but allows you to receive indication of SNMP traps. To use it, create an instance of the class passing the OID you wish to listen to and the agent that provides it. Then call the enable method to start receiving indications. You can also disable the indications via the disable method. Processing indications takes a little bit more work because you need to derive SNMPSession and override the processTrap method. A usage example, taken from the SNMPTRAP example, is shown below:

class MySession : public SNMPSession {
public: 
    virtual void processTrap(const SNMPObject & obj) {
        std::cout << obj.getDisplayInformation() << "\n";
    }
};

MySession session;
SNMPRemoteAgent ragent(host, community, 0, &session);
SNMPTrap trap(oid, &ragent);
trap.enable();

Please note that the trap handling has not been tested, so it is probably broken. Feel free to check if it works!

Other Classes

Exceptions

Most library methods you can call (virtually all of them) may throw an exception of class SNMPException or SNMPErrorException (actually only SNMPErrorException is thrown). Each exception will give you detailed information on the error, including a message regarding the cause and an error ID. To print this information use toString (or toStringStack if you need the entire exception stack). To help with the coding of the library a small exception framework was set up using macros (see SNMPException.h).

Sets

The set classes (SNMPRequestSet and SNMPRemoteAgent) are pretty straightforward and allow both storage and set operations.

Using the Code

For those of you that believe code and commenting is the most accurate documentation, the full code of the SNMPGET example, with additional comments, is shown below:

int main(int argc, char* argv[]) {
    try {   
        // startup
        SNMPManager::getManager()->startup(); 
        
        // holds the host name or address of the agent
        std::string host = "172.18.200.90";
        // holds the community name
        std::string community = "public";
        // holds the response time out value for requests
        int tmo = 1000;
        // holds the agent port 
        unsigned int port = 0; // zero means the SNMP default port
        // holds the number of OIDs to request
        int oidcount = 0;
        // holds the OIDs to request
        std::string oidarray[512];
        
        // (...) here the command line arguments are parsed
        // to fill in the previous variables
        
        // create the remote agent
        SNMPRemoteAgent ragent(host, community, port);    
        // create the SNMP objects containing the OIDs to request
        SNMPObject * oids = new SNMPObject[oidcount];
        for(int i=0; i<oidcount; i++) {            
            oids[i].setOID(oidarray[i]);
        }
        // create the get request
        SNMPRemoteAgentGetRequest getReq(oids, oidcount);
        // perform the request
        getReq.execute(&ragent);
        // wait for the response
        getReq.wait(tmo);
        // destroy request OIDs
        delete [] oids;    
        // process result    
        if(!getReq.succeeded()) {
            if(!getReq.timedOut()) {
                std::cout << getReq.getErrorAsString();
            } else {
                std::cout << "timeout";
            }
        } else {
            for(int i=0; i<getReq.getResultCount(); i++) {    
                std::cout << getReq.getResult(i)->getDisplayInformation() << "\n";
            }
        }
        std::cout << "\n";
        return 0;
    } catch (SNMPException * pe) {
        // Process any errors
        std::cout << "\n\n";
        std::cout << pe->toStringStack();
        std::cout << "\n";
        delete pe;
    }
    return 0;
}

Exploring the Code and the Examples

All of the code is included in a set of *.h and *.cpp files. Finding the class you want is pretty straightforward given the file names. The examples are included in the main.cpp file. To compile each example, you need to define the appropriate macro as explained in the beginning of the file.

All of the examples are command line tools that accept a set of arguments to perform various operations. Common to each one are some options such as: printing help (-h or -?), enabling tracing (-T), setting the timeout (t:<n>) or setting the remote port of the agent (p:<n>).

  • SNMPGET: Reads a set of object values given their OIDs.
  • SNMPSET: Writes one SNMP object given its OID, value, and optionally, its syntax.
  • SNMPWALK: Displays all objects in the agent from given OID.
  • SNMPSCAN: Scans your network for the availability of SNMP agents.
  • SNMPTRAP: If it works, processes traps from the agent.

Last Things Last

I hope this is a useful article, please comment if you think it is. There is, of course, a lot of room for improvement, mainly regarding design. Although I have no plans to do any updates please come forward with any comments as well as design or coding alternatives.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here