summaryrefslogtreecommitdiff
path: root/agent/mibgroup/winExtDLL.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/winExtDLL.c')
-rwxr-xr-xagent/mibgroup/winExtDLL.c1694
1 files changed, 1694 insertions, 0 deletions
diff --git a/agent/mibgroup/winExtDLL.c b/agent/mibgroup/winExtDLL.c
new file mode 100755
index 0000000..b460175
--- /dev/null
+++ b/agent/mibgroup/winExtDLL.c
@@ -0,0 +1,1694 @@
+/**
+ * @brief winExtDLL Net-SNMP agent extension module.
+ *
+ * Copyright (c) 2006-2009 Alex Burger.
+ * Copyright (c) 2009-2010 Bart Van Assche <bart.vanassche@gmail.com>.
+ *
+ * This Net-SNMP agent extension module loads Windows SNMP Extension Agent
+ * DLLs in the Net-SNMP agent. Not only extension DLLs provided with Windows
+ * (e.g. hostmib.dll) but also third-party extension DLLs are supported. This
+ * allows Net-SNMP to be a replacement for the Windows SNMP service, and makes
+ * it possible to use the SNMPv3 protocol.
+ *
+ * @see See also <a href="http://msdn.microsoft.com/en-us/library/aa378988(VS.85).aspx">SNMP Functions</a>
+ * for more information about Microsoft's SNMP Extension Agent API.
+ *
+ * @note In order to use this agent extension module, the Windows SNMP service
+ * must be installed first and must be disabled. Installing the Windows SNMP
+ * service is the only way to install the Windows Extension DLLs and to make
+ * sure that information about these DLLs is present in the registry.
+ *
+ * @note All Windows extension DLLs are loaded during startup of the Net-SNMP
+ * service. The Net-SNMP service must be restarted to load new modules. This
+ * extension is NOT for dynamically loading Net-SNMP extensions.
+ *
+ *
+ * History:
+ * - 2010/03/19:
+ * * Multi-varbind set request PDUs are now handled correctly.
+ * * If loading an extension DLL fails, the reason why this failed is now
+ * logged.
+ * * Fixed a memory leak that occurred when SnmpExtensionQuery() or
+ * SnmpExtensionQueryEx() failed while processing an SNMP PDU. Note:
+ * occurrence of an SNMP error does not make these functions fail, and
+ * it is not yet known whether or not it was possible to trigger this
+ * memory leak.
+ * - 2010/03/17: Fixed bug 2971257. Multi-varbind getNext requests with OIDs
+ * in reverse lexicographical order are again processed correctly.
+ * - 2010/01/22: Compiles now with MinGW too.
+ * - 2009/12/11:
+ * * The value of sysUpTime.0 reported by inetmib1.dll is now correct.
+ * * A linkUp or linkDown trap is now sent after the status of a network
+ * interface has changed.
+ * - 2009/03/26:
+ * * Removed several artificial limits. Result: more than 100 SNMP extension
+ * DLLs can now be loaded simultaneously and more than 100 OID ranges can
+ * now be registered. Loading e.g. the Dell OpenManage SNMP extension DLL
+ * does no longer crash Net-SNMP.
+ * * Number of OID ranges registered during startup is now logged.
+ * * It is no longer attempted to free the Broadcom SNMP extension DLLs
+ * bcmif.dll and baspmgnt.dll since doing so triggers a deadlock.
+ * * Added support for reregistration of an OID prefix. As an example, both
+ * both Microsoft's inetmib1.dll and the Eicon Diva divasnmpx.dll register
+ * the OID prefix iso.org.dod.internet.mgmt.mib-2.interfaces
+ * (.1.3.6.1.2.1.2). WinExtDLL will process OIDs with this prefix by using
+ * the handler that was registered last for the OID prefix. A message will
+ * be logged indicating that a handler has been replaced.
+ * - 2009/03/10:
+ * * Fixed several bugs in var_winExtDLL(): looking up extension DLL info
+ * based on the OID in a varbind is wrong. It does happen during GetNext
+ * processing that Net-SNMP passes intentionally varbinds to a handler
+ * with OIDs that are outside the range registered by the handler. Fixed
+ * this by filling in a pointer to the extension DLL info in
+ * netsnmp_mib_handler::myvoid and by using that information in the
+ * var_winExtDLL() handler function.
+ * * SetRequest PDUs are now passed once to an extension DLL instead of
+ * four times.
+ * * The error status and error index of a multi-varbind set request is now
+ * filled in correctly.
+ * * Added support for the SNMP extension DLL three-phase SNMP set.
+ * * Made traps SNMPv2 compliant by adding the sysUpTime.0 varbind.
+ * * The varbind list generated by extension DLLs for e.g. linkUp and
+ * linkDown traps is now passed to Net-SNMP. Previously this varbind list
+ * was discarded for generic traps.
+ * * Fixed memory leaks triggered by Get and GetNext PDU processing.
+ * * Added missing RegCloseKey() calls.
+ * * Added shutdown function shutdown_winExtDLL().
+ * * Replaced #include <cstdio> by #include <stdio.h> such that this source
+ * file compiles with Visual Studio 2005.
+ * * Removed many unused local variables.
+ * * Fixed several other compiler warnings.
+ * - 2006/09/09: creation of this file.
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/agent/mib_module_config.h>
+
+#ifdef USING_WINEXTDLL_MODULE
+
+#include <net-snmp/types.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+#include "../../win32/Snmp-winExtDLL.h"
+#include "../../win32/MgmtApi-winExtDLL.h"
+
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include "util_funcs.h"
+#include "winExtDLL.h"
+
+
+#define MAX_VALUE_NAME 16383
+#define MS_ASN_UINTEGER32 MS_ASN_UNSIGNED32
+
+
+typedef BOOL(WINAPI *
+ PFNSNMPEXTENSIONINIT) (DWORD dwUpTimeReference,
+ HANDLE * phSubagentTrapEvent,
+ AsnObjectIdentifier *
+ pFirstSupportedRegion);
+
+typedef BOOL(WINAPI *
+ PFNSNMPEXTENSIONINITEX) (AsnObjectIdentifier *
+ pNextSupportedRegion);
+
+typedef BOOL(WINAPI *
+ PFNSNMPEXTENSIONMONITOR) (LPVOID pAgentMgmtData);
+
+typedef BOOL(WINAPI * PFNSNMPEXTENSIONQUERY) (BYTE bPduType,
+ SnmpVarBindList *
+ pVarBindList,
+ AsnInteger32 *
+ pErrorStatus,
+ AsnInteger32 *
+ pErrorIndex);
+
+typedef BOOL(WINAPI * PFNSNMPEXTENSIONQUERYEX) (UINT nRequestType,
+ UINT
+ nTransactionId,
+ SnmpVarBindList *
+ pVarBindList,
+ AsnOctetString *
+ pContextInfo,
+ AsnInteger32 *
+ pErrorStatus,
+ AsnInteger32 *
+ pErrorIndex);
+
+typedef BOOL(WINAPI * PFNSNMPEXTENSIONTRAP) (AsnObjectIdentifier *
+ pEnterpriseOid,
+ AsnInteger32 *
+ pGenericTrapId,
+ AsnInteger32 *
+ pSpecificTrapId,
+ AsnTimeticks *
+ pTimeStamp,
+ SnmpVarBindList *
+ pVarBindList);
+
+typedef VOID(WINAPI * PFNSNMPEXTENSIONCLOSE) (void);
+
+/**
+ * Extensible array, a data structure similar to the C++ STL class
+ * std::vector<>.
+ */
+typedef struct {
+ /** Pointer to the memory allocated for the array. */
+ void *p;
+ /** Number of bytes occupied by a single element. */
+ size_t elem_size;
+ /** Number of elements that have been allocated. */
+ int reserved;
+ /** Number of elements currently in use. */
+ int size;
+} xarray;
+
+/**
+ * Information managed by winExtDLL about Windows SNMP extension DLL's.
+ */
+typedef struct {
+ char *dll_name; /**< Dynamically allocated DLL name. */
+ HANDLE dll_handle; /**< DLL handle. */
+ PFNSNMPEXTENSIONINIT pfSnmpExtensionInit;
+ PFNSNMPEXTENSIONINITEX pfSnmpExtensionInitEx;
+ PFNSNMPEXTENSIONQUERY pfSnmpExtensionQuery;
+ PFNSNMPEXTENSIONQUERYEX pfSnmpExtensionQueryEx;
+ PFNSNMPEXTENSIONTRAP pfSnmpExtensionTrap;
+ HANDLE subagentTrapEvent;
+} winextdll;
+
+/**
+ * Information managed by winExtDLL about a single view of a Windows SNMP
+ * extension DLL.
+ */
+typedef struct {
+ winextdll *winextdll_info;
+ netsnmp_handler_registration *my_handler;
+ oid name[MAX_OID_LEN]; /**< OID of this view. */
+ size_t name_length;
+} winextdll_view;
+
+/**
+ * Per varbind SNMP extension DLL context information for SNMP set operations.
+ */
+typedef struct context_info_s {
+ struct context_info_s *next;
+ int index;
+ AsnOctetString context_info;
+} context_info;
+
+
+/*
+ * External function declarations.
+ */
+void __declspec(dllimport) WINAPI SnmpSvcInitUptime(void);
+
+
+/*
+ * Local functions declarations.
+ */
+static int basename_equals(const char *path, const char *basename);
+static int register_netsnmp_handler(winextdll_view *
+ const ext_dll_view_info);
+static void read_extension_dlls_from_registry(void);
+static void read_extension_dlls_from_registry_at(const char *const subkey);
+static char *read_extension_dll_path_from_registry(const TCHAR *);
+static void subagentTrapCheck(unsigned int clientreg, void *clientarg);
+static int var_winExtDLL(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int append_windows_varbind_list(netsnmp_variable_list **
+ const net_snmp_varbinds,
+ const SnmpVarBindList *
+ const win_varbinds);
+static int append_windows_varbind(netsnmp_variable_list **
+ const net_snmp_varbinds,
+ const SnmpVarBind *
+ const win_varbind);
+static int convert_to_windows_varbind_list(SnmpVarBindList *
+ pVarBindList,
+ netsnmp_variable_list *
+ netsnmp_varbinds);
+static int convert_win_snmp_err(const int win_snmp_err);
+static winextdll_view *lookup_view_by_oid(oid * const name,
+ const size_t name_len);
+static void copy_oid(oid * const to_name, size_t * const to_name_len,
+ const oid * const from_name,
+ const size_t from_name_len);
+static UINT *copy_oid_to_new_windows_oid(AsnObjectIdentifier *
+ const windows_oid,
+ const oid * const name,
+ const size_t name_len);
+static void send_trap(const AsnObjectIdentifier * const,
+ const AsnInteger, const AsnInteger,
+ const AsnTimeticks,
+ const SnmpVarBindList * const);
+static u_char *winsnmp_memdup(const void *src, const size_t len);
+#if 0
+static void xarray_init(xarray * a, size_t elem_size);
+#endif
+static void xarray_destroy(xarray * a);
+static void *xarray_push_back(xarray * a, const void *elem);
+#if 0
+static void xarray_erase(xarray * a, void *const elem);
+#endif
+static void *xarray_reserve(xarray * a, int reserved);
+
+
+/*
+ * Local variable definitions.
+ */
+#define WINEXTDLL(i) ((winextdll*)s_winextdll.p)[i]
+#define WINEXTDLL_VIEW(i) ((winextdll_view*)s_winextdll_view.p)[i]
+#define TRAPEVENT(i) ((HANDLE*)s_trapevent.p)[i]
+#define TRAPEVENT_TO_DLLINFO(i) ((winextdll**)s_trapevent_to_dllinfo.p)[i]
+static xarray s_winextdll = { 0, sizeof(winextdll) };
+static xarray s_winextdll_view = { 0, sizeof(winextdll_view) };
+static xarray s_trapevent = { 0, sizeof(HANDLE) };
+static xarray s_trapevent_to_dllinfo = { 0, sizeof(winextdll *) };
+static context_info *context_info_head;
+
+
+/*
+ * Function definitions.
+ */
+
+/** Initialize the winExtDLL extension agent. */
+void
+init_winExtDLL(void)
+{
+ BOOL result;
+ int i;
+
+ DEBUGMSG(("winExtDLL", "init_winExtDLL started.\n"));
+
+ SnmpSvcInitUptime();
+
+ read_extension_dlls_from_registry();
+
+ DEBUGMSG(("winExtDLL",
+ "init_winExtDLL: found %d extension DLLs in the registry.\n",
+ s_winextdll.size));
+
+ xarray_reserve(&s_winextdll, 128);
+
+ /*
+ * Load all the DLLs
+ */
+ for (i = 0; i < s_winextdll.size; i++) {
+ winextdll *const ext_dll_info = &WINEXTDLL(i);
+ AsnObjectIdentifier view;
+ winextdll_view ext_dll_view_info;
+
+ assert(ext_dll_info);
+ if (!ext_dll_info->dll_name)
+ continue;
+
+ DEBUGMSG(("winExtDLL", "loading DLL %s.\n",
+ ext_dll_info->dll_name));
+ ext_dll_info->dll_handle = LoadLibrary(ext_dll_info->dll_name);
+
+ if (ext_dll_info->dll_handle == NULL) {
+ const DWORD dwErrorcode = GetLastError();
+ LPTSTR lpMsgBuf;
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrorcode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpMsgBuf, 0, NULL);
+ if (lpMsgBuf) {
+ LPTSTR p;
+
+ /*
+ * Remove trailing "\r\n".
+ */
+ p = strchr(lpMsgBuf, '\r');
+ if (p)
+ *p = '\0';
+ }
+ snmp_log(LOG_ERR,
+ "init_winExtDLL: could not load SNMP extension"
+ " DLL %s: %s\n",
+ ext_dll_info->dll_name, lpMsgBuf ? lpMsgBuf : "(?)");
+ if (lpMsgBuf)
+ LocalFree(lpMsgBuf);
+ continue;
+ }
+
+ /*
+ * Store DLL name and functions in s_extension_dll_info array.
+ */
+ ext_dll_info->pfSnmpExtensionInit = (PFNSNMPEXTENSIONINIT)
+ GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionInit");
+ ext_dll_info->pfSnmpExtensionInitEx = (PFNSNMPEXTENSIONINITEX)
+ GetProcAddress(ext_dll_info->dll_handle,
+ "SnmpExtensionInitEx");
+ ext_dll_info->pfSnmpExtensionQuery = (PFNSNMPEXTENSIONQUERY)
+ GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionQuery");
+ ext_dll_info->pfSnmpExtensionQueryEx = (PFNSNMPEXTENSIONQUERYEX)
+ GetProcAddress(ext_dll_info->dll_handle,
+ "SnmpExtensionQueryEx");
+ ext_dll_info->pfSnmpExtensionTrap = (PFNSNMPEXTENSIONTRAP)
+ GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionTrap");
+
+
+ if (ext_dll_info->pfSnmpExtensionQuery == NULL
+ && ext_dll_info->pfSnmpExtensionQueryEx == NULL) {
+ snmp_log(LOG_ERR,
+ "error in extension DLL %s: SNMP query function missing.\n",
+ ext_dll_info->dll_name);
+ }
+
+ /*
+ * Init and get first supported view from Windows SNMP extension DLL.
+ * Note: although according to the documentation of SnmpExtensionInit()
+ * the first argument of this function should be ignored by extension
+ * DLLs, passing the value GetTickCount() / 10 is necessary to make
+ * inetmib1.dll work correctly. Passing zero as the first argument
+ * causes inetmib1.dll to report an incorrect value for sysUpTime.0
+ * and also causes the same DLL not to send linkUp or linkDown traps.
+ */
+ ext_dll_info->subagentTrapEvent = NULL;
+ view.idLength = 0;
+ view.ids = 0;
+ result =
+ ext_dll_info->pfSnmpExtensionInit(GetTickCount() / 10,
+ &ext_dll_info->
+ subagentTrapEvent, &view);
+
+ if (!result) {
+ snmp_log(LOG_ERR,
+ "init_winExtDLL: initialization of DLL %s failed.\n",
+ ext_dll_info->dll_name);
+ FreeLibrary(ext_dll_info->dll_handle);
+ ext_dll_info->dll_handle = 0;
+ continue;
+ }
+
+ if (ext_dll_info->subagentTrapEvent != NULL) {
+ xarray_push_back(&s_trapevent,
+ &ext_dll_info->subagentTrapEvent);
+ xarray_push_back(&s_trapevent_to_dllinfo, &ext_dll_info);
+ }
+
+ memset(&ext_dll_view_info, 0, sizeof(ext_dll_view_info));
+ ext_dll_view_info.winextdll_info = ext_dll_info;
+ copy_oid(ext_dll_view_info.name, &ext_dll_view_info.name_length,
+ view.ids, view.idLength);
+ xarray_push_back(&s_winextdll_view, &ext_dll_view_info);
+
+ /*
+ * Loop looking for more supported views.
+ */
+ while (ext_dll_info->pfSnmpExtensionInitEx
+ && ext_dll_info->pfSnmpExtensionInitEx(&view)) {
+ memset(&ext_dll_view_info, 0, sizeof(ext_dll_view_info));
+ ext_dll_view_info.winextdll_info = ext_dll_info;
+ copy_oid(ext_dll_view_info.name,
+ &ext_dll_view_info.name_length, view.ids,
+ view.idLength);
+ xarray_push_back(&s_winextdll_view, &ext_dll_view_info);
+ }
+ }
+
+ /*
+ * Note: since register_netsnmp_handler() writes a pointer to the
+ * winextdll_view in one of the Net-SNMP data structures, it is not
+ * allowed to move winextdll_view data structures in memory after
+ * registration with Net-SNMP. Or: register_snmp_handler() must be called
+ * only once it is sure that the size of array s_winextdll_view won't change
+ * anymore.
+ */
+ for (i = 0; i < s_winextdll_view.size; i++)
+ register_netsnmp_handler(&WINEXTDLL_VIEW(i));
+
+ DEBUGMSG(("winExtDLL",
+ "init_winExtDLL: registered %d OID ranges.\n",
+ s_winextdll_view.size));
+
+ /*
+ * Let Net-SNMP call subagentTrapCheck() once per second.
+ */
+ if (s_trapevent.size)
+ snmp_alarm_register(1, SA_REPEAT, subagentTrapCheck, NULL);
+
+ DEBUGMSG(("winExtDLL", "init_winExtDLL finished.\n"));
+}
+
+void
+shutdown_winExtDLL(void)
+{
+ int i;
+
+ DEBUGMSG(("winExtDLL", "shutdown_winExtDLL() started.\n"));
+
+ for (i = s_winextdll_view.size - 1; i >= 0; i--) {
+ winextdll_view *const v = &WINEXTDLL_VIEW(i);
+ if (v && v->my_handler) {
+ DEBUGIF("winExtDLL") {
+ DEBUGMSG(("winExtDLL",
+ "unregistering handler for DLL %s and OID prefix ",
+ v->winextdll_info->dll_name));
+ DEBUGMSGOID(("winExtDLL", v->name, v->name_length));
+ DEBUGMSG(("winExtDLL", " ("));
+ DEBUGMSGSUBOID(("winExtDLL", v->name, v->name_length));
+ DEBUGMSG(("winExtDLL", ").\n"));
+ }
+ netsnmp_unregister_handler(v->my_handler);
+ }
+ }
+ xarray_destroy(&s_winextdll_view);
+
+ for (i = s_winextdll.size - 1; i >= 0; i--) {
+ winextdll *const ext_dll_info = &WINEXTDLL(i);
+ /*
+ * Freeing the Broadcom SNMP extension libraries triggers a deadlock,
+ * so skip bcmif.dll and baspmgnt.dll.
+ */
+ if (ext_dll_info->dll_handle != 0
+ && !basename_equals(ext_dll_info->dll_name, "bcmif.dll")
+ && !basename_equals(ext_dll_info->dll_name, "baspmgnt.dll")) {
+ DEBUGMSG(("winExtDLL", "unloading %s.\n",
+ ext_dll_info->dll_name));
+ FreeLibrary(ext_dll_info->dll_handle);
+ }
+ free(ext_dll_info->dll_name);
+ }
+ xarray_destroy(&s_winextdll);
+
+ xarray_destroy(&s_trapevent_to_dllinfo);
+
+ xarray_destroy(&s_trapevent);
+
+ DEBUGMSG(("winExtDLL", "shutdown_winExtDLL() finished.\n"));
+}
+
+/**
+ * Compare the basename of a path with a given string.
+ *
+ * @return 1 if the basename matches, 0 if not.
+ */
+static int
+basename_equals(const char *path, const char *basename)
+{
+ const size_t path_len = strlen(path);
+ const size_t basename_len = strlen(basename);
+
+ assert(strchr(path, '/') == 0);
+ assert(strchr(basename, '/') == 0);
+ assert(strchr(basename, '\\') == 0);
+
+ return path_len >= basename_len + 1
+ && path[path_len - basename_len - 1] == '\\'
+ && stricmp(path + path_len - basename_len, basename) == 0;
+}
+
+/**
+ * Register a single OID subtree with Net-SNMP.
+ *
+ * @return 1 if successful, 0 if not.
+ */
+int
+register_netsnmp_handler(winextdll_view * const ext_dll_view_info)
+{
+ winextdll *ext_dll_info;
+ winextdll_view *previously_registered_view;
+
+ ext_dll_info = ext_dll_view_info->winextdll_info;
+
+ previously_registered_view
+ = lookup_view_by_oid(ext_dll_view_info->name,
+ ext_dll_view_info->name_length);
+
+ if (previously_registered_view) {
+ size_t oid_namelen, outlen;
+ char *oid_name;
+ int buffer_large_enough;
+
+ oid_namelen = 0;
+ outlen = 0;
+ oid_name = NULL;
+ buffer_large_enough =
+ sprint_realloc_objid((u_char **) & oid_name, &oid_namelen,
+ &outlen, 1, ext_dll_view_info->name,
+ ext_dll_view_info->name_length);
+ snmp_log(LOG_INFO, "OID range %s%s: replacing handler %s by %s.\n",
+ oid_name ? oid_name : "",
+ buffer_large_enough ? "" : " [TRUNCATED]",
+ previously_registered_view->winextdll_info->dll_name,
+ ext_dll_view_info->winextdll_info->dll_name);
+ if (oid_name)
+ free(oid_name);
+
+ previously_registered_view->winextdll_info = ext_dll_info;
+ memset(ext_dll_view_info, 0, sizeof(*ext_dll_view_info));
+ return 1;
+ } else {
+ // Create handler registration
+ ext_dll_view_info->my_handler
+ = netsnmp_create_handler_registration(ext_dll_info->dll_name,
+ var_winExtDLL,
+ ext_dll_view_info->name,
+ ext_dll_view_info->
+ name_length,
+ HANDLER_CAN_RWRITE);
+
+ if (ext_dll_view_info->my_handler) {
+ ext_dll_view_info->my_handler->handler->myvoid =
+ ext_dll_view_info;
+ if (netsnmp_register_handler(ext_dll_view_info->my_handler)
+ == MIB_REGISTERED_OK) {
+ DEBUGIF("winExtDLL") {
+ DEBUGMSG(("winExtDLL",
+ "registering handler for DLL %s and OID prefix ",
+ ext_dll_info->dll_name));
+ DEBUGMSGOID(("winExtDLL", ext_dll_view_info->name,
+ ext_dll_view_info->name_length));
+ DEBUGMSG(("winExtDLL", " ("));
+ DEBUGMSGSUBOID(("winExtDLL", ext_dll_view_info->name,
+ ext_dll_view_info->name_length));
+ DEBUGMSG(("winExtDLL", ").\n"));
+ }
+ return 1;
+ } else {
+ snmp_log(LOG_ERR, "handler registration failed.\n");
+ ext_dll_view_info->my_handler = 0;
+ }
+ } else {
+ snmp_log(LOG_ERR, "handler creation failed.\n");
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Allocate SNMP extension DLL context information. Such context information
+ * is necessary to allow an extension DLL to process a set request.
+ *
+ * @param[in] index Varbind index in original PDU.
+ *
+ * @return NULL if context information for the specified index was already
+ * allocated, and otherwise a pointer to the newly allocated context
+ * information.
+ */
+static context_info *
+alloc_context_info(const int index)
+{
+ context_info *p;
+
+ DEBUGMSG(("winExtDLL:context_info", "alloc_context_info(%d)\n",
+ index));
+
+ for (p = context_info_head; p; p = p->next) {
+ if (p->index == index) {
+ assert(FALSE);
+ return NULL;
+ }
+ }
+
+ p = calloc(1, sizeof(context_info));
+ p->next = context_info_head;
+ context_info_head = p;
+ p->index = index;
+
+ return p;
+}
+
+/**
+ * Deallocate SNMP extension DLL context information.
+ *
+ * @param[in] index Varbind index in original PDU.
+ */
+static void
+free_context_info(const int index)
+{
+ context_info **pprev = &context_info_head;
+ context_info *p;
+
+ DEBUGMSG(("winExtDLL:context_info", "free_context_info(%d)\n", index));
+
+ for (p = context_info_head; p; p = p->next) {
+ if (p->index == index) {
+ *pprev = p->next;
+ free(p);
+ break;
+ }
+ pprev = &p->next;
+ }
+}
+
+/**
+ * Look up SNMP extension DLL context information.
+ *
+ * @param[in] index Varbind index in original PDU.
+ */
+static AsnOctetString *
+get_context_info(const int index)
+{
+ context_info *p;
+
+ DEBUGMSG(("winExtDLL:context_info", "get_context_info(%d)\n", index));
+
+ for (p = context_info_head; p; p = p->next)
+ if (p->index == index)
+ return &p->context_info;
+
+ assert(FALSE);
+ return NULL;
+}
+
+static int
+var_winExtDLL(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ winextdll_view *const ext_dll_view_info = handler->myvoid;
+ winextdll *ext_dll_info;
+ netsnmp_request_info *request;
+ UINT nRequestType;
+ const char *mode_name;
+ int rc;
+
+ assert(ext_dll_view_info);
+ ext_dll_info = ext_dll_view_info->winextdll_info;
+#if ! defined(NDEBUG)
+ assert(ext_dll_view_info ==
+ lookup_view_by_oid(reginfo->rootoid, reginfo->rootoid_len));
+#endif
+
+ if (ext_dll_info == 0) {
+ DEBUGMSG(("winExtDLL",
+ "internal error: no matching extension DLL found.\n"));
+ assert(0);
+ return SNMP_ERR_GENERR;
+ }
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ mode_name = "GET";
+ nRequestType = SNMP_EXTENSION_GET;
+ assert(!context_info_head);
+ break;
+ case MODE_GETNEXT:
+ mode_name = "GETNEXT";
+ nRequestType = SNMP_EXTENSION_GET_NEXT;
+ assert(!context_info_head);
+ break;
+ case MODE_SET_RESERVE1:
+ mode_name = "SET_RESERVE1";
+ nRequestType = SNMP_EXTENSION_SET_TEST;
+ break;
+ case MODE_SET_RESERVE2:
+ mode_name = "SET_RESERVE2";
+ return SNMP_ERR_NOERROR;
+ case MODE_SET_ACTION:
+ mode_name = "SET_ACTION";
+ return SNMP_ERR_NOERROR;
+ case MODE_SET_UNDO:
+ mode_name = "SET_UNDO";
+ nRequestType = SNMP_EXTENSION_SET_UNDO;
+ break;
+ case MODE_SET_COMMIT:
+ mode_name = "SET_COMMIT";
+ nRequestType = SNMP_EXTENSION_SET_COMMIT;
+ break;
+ case MODE_SET_FREE:
+ mode_name = "SET_FREE";
+ nRequestType = SNMP_EXTENSION_SET_CLEANUP;
+ break;
+ default:
+ DEBUGMSG(("winExtDLL",
+ "internal error: invalid mode %d.\n", reqinfo->mode));
+ assert(0);
+ return SNMP_ERR_NOERROR;
+ }
+
+ rc = SNMP_ERR_NOERROR;
+
+ for (request = requests; request; request = request->next) {
+ netsnmp_variable_list *varbind;
+ SnmpVarBindList win_varbinds;
+ AsnInteger32 ErrorStatus;
+ AsnInteger32 ErrorIndex;
+ BOOL result;
+ BOOL copy_value;
+
+ memset(&win_varbinds, 0, sizeof(win_varbinds));
+
+ if (request->processed || rc != SNMP_ERR_NOERROR)
+ goto free_win_varbinds;
+
+ if (reqinfo->mode == MODE_SET_RESERVE1)
+ alloc_context_info(request->index);
+
+ varbind = request->requestvb;
+ assert(varbind);
+
+ /*
+ * Convert the Net-SNMP varbind to a Windows SNMP varbind list.
+ */
+ rc = convert_to_windows_varbind_list(&win_varbinds, varbind);
+ if (rc != SNMP_ERR_NOERROR) {
+ DEBUGMSG(("winExtDLL",
+ "converting varbind list to Windows format failed with"
+ " error code %d.\n", request->status));
+ netsnmp_request_set_error(requests, rc);
+ goto free_win_varbinds;
+ }
+
+ assert(win_varbinds.len == 1);
+
+ /*
+ * For a GetNext PDU, if the varbind OID comes lexicographically
+ * before the root OID of this handler, replace it by the root OID.
+ */
+ if (reqinfo->mode == MODE_GETNEXT
+ && snmp_oid_compare(win_varbinds.list[0].name.ids,
+ win_varbinds.list[0].name.idLength,
+ reginfo->rootoid,
+ reginfo->rootoid_len) < 0) {
+ AsnObjectIdentifier Root =
+ { reginfo->rootoid_len, reginfo->rootoid };
+ SnmpUtilOidCpy(&win_varbinds.list[0].name, &Root);
+ }
+
+ if (ext_dll_info->pfSnmpExtensionQueryEx) {
+ result = ext_dll_info->pfSnmpExtensionQueryEx(nRequestType,
+ 1,
+ &win_varbinds,
+ get_context_info(request->index),
+ &ErrorStatus,
+ &ErrorIndex);
+ } else if (ext_dll_info->pfSnmpExtensionQuery) {
+ result =
+ ext_dll_info->pfSnmpExtensionQuery((BYTE) nRequestType,
+ &win_varbinds,
+ &ErrorStatus,
+ &ErrorIndex);
+ } else {
+ snmp_log(LOG_ERR,
+ "error in extension DLL %s: SNMP query function missing.\n",
+ ext_dll_info->dll_name);
+ result = FALSE;
+ }
+
+ if (!result) {
+ snmp_log(LOG_ERR,
+ "extension DLL %s: SNMP query function failed.\n",
+ ext_dll_info->dll_name);
+ rc = SNMP_ERR_GENERR;
+ goto free_win_varbinds;
+ }
+
+ rc = convert_win_snmp_err(ErrorStatus);
+ if (rc != SNMP_ERR_NOERROR) {
+ DEBUGMSG(("winExtDLL",
+ "extension DLL %s: SNMP query function returned error code %lu (Windows) / %d (Net-SNMP).\n",
+ ext_dll_info->dll_name, ErrorStatus, rc));
+ assert(ErrorIndex == 1);
+ netsnmp_request_set_error(requests, rc);
+ if (rc == SNMP_NOSUCHOBJECT || rc == SNMP_NOSUCHINSTANCE
+ || rc == SNMP_ERR_NOSUCHNAME)
+ rc = SNMP_ERR_NOERROR;
+ goto free_win_varbinds;
+ }
+
+ copy_value = FALSE;
+ if (reqinfo->mode == MODE_GET)
+ copy_value = TRUE;
+ else if (reqinfo->mode == MODE_GETNEXT) {
+ const SnmpVarBind *win_varbind;
+
+ win_varbind = &win_varbinds.list[0];
+
+ /*
+ * Verify whether the OID returned by the extension DLL fits
+ * inside the OID range this handler has been registered
+ * with. Also compare the OID passed to the extension DLL with
+ * the OID returned by the same DLL. If the DLL returned a
+ * lexicographically earlier OID, this means that there is no
+ * next OID in the MIB implemented by the DLL.
+ *
+ * Note: for some GetNext requests BoundsChecker will report
+ * that the code below accesses a dangling pointer. This is
+ * a limitation of BoundsChecker: apparently BoundsChecker is
+ * not able to cope with reallocation of memory for
+ * win_varbind by an SNMP extension DLL that has not been
+ * instrumented by BoundsChecker.
+ */
+ if (netsnmp_oid_is_subtree(ext_dll_view_info->name,
+ ext_dll_view_info->name_length,
+ win_varbind->name.ids,
+ win_varbind->name.idLength) == 0
+ && snmp_oid_compare(varbind->name, varbind->name_length,
+ win_varbind->name.ids,
+ win_varbind->name.idLength) < 0) {
+ /*
+ * Copy the OID returned by the extension DLL to the
+ * Net-SNMP varbind.
+ */
+ snmp_set_var_objid(varbind,
+ win_varbind->name.ids,
+ win_varbind->name.idLength);
+ copy_value = TRUE;
+ }
+ }
+ if (copy_value) {
+ netsnmp_variable_list *result_vb;
+
+ /*
+ * Copy the value returned by the extension DLL to the Net-SNMP
+ * varbind.
+ */
+ result_vb = NULL;
+ rc = append_windows_varbind(&result_vb, &win_varbinds.list[0]);
+ assert(result_vb || rc != SNMP_ERR_NOERROR);
+ if (result_vb) {
+ snmp_set_var_typed_value(varbind,
+ result_vb->type,
+ result_vb->val.string,
+ result_vb->val_len);
+ snmp_free_varbind(result_vb);
+ } else {
+ netsnmp_request_set_error(requests, rc);
+ goto free_win_varbinds;
+ }
+ }
+
+free_win_varbinds:
+ if (reqinfo->mode == MODE_SET_COMMIT
+ || reqinfo->mode == MODE_SET_UNDO
+ || reqinfo->mode == MODE_SET_FREE)
+ free_context_info(request->index);
+ if (win_varbinds.list)
+ SnmpUtilVarBindListFree(&win_varbinds);
+ }
+
+ return rc;
+}
+
+/**
+ * Iterate over the SNMP extension DLL information in the registry and store
+ * the retrieved information in s_winextdll[].
+ *
+ * At the time an SNMP extension DLL is installed, some information about the
+ * DLL is written to the registry at one of the two following locations:
+ * HKLM\SYSTEM\CurrentControlSet\Control\SNMP\Parameters\ExtensionAgents for
+ * Windows Vista, Windows 7 and Windows 2008 or
+ * HKLM\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ExtensionAgents for
+ * earlier Windows versions. Under this key zero or more REG_SZ values are
+ * stored with the names of registry keys containing the DLL path.
+ */
+void
+read_extension_dlls_from_registry()
+{
+ DEBUGMSGTL(("winExtDLL",
+ "read_extension_dlls_from_registry called\n"));
+
+ read_extension_dlls_from_registry_at
+ ("SYSTEM\\CurrentControlSet\\Services\\SNMP\\Parameters\\ExtensionAgents");
+ read_extension_dlls_from_registry_at
+ ("SYSTEM\\CurrentControlSet\\Control\\SNMP\\Parameters\\ExtensionAgents");
+}
+
+void
+read_extension_dlls_from_registry_at(const char *const subkey)
+{
+ DWORD retCode;
+ HKEY hKey;
+ int i;
+ DWORD valueSize;
+ TCHAR valueName[MAX_VALUE_NAME];
+ DWORD dataType;
+ TCHAR data[MAX_VALUE_NAME];
+ DWORD dataSize;
+
+ retCode = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey,
+ 0, KEY_QUERY_VALUE, &hKey);
+
+ if (retCode == ERROR_SUCCESS) {
+ for (i = 0; ; i++) {
+ valueSize = sizeof(valueName);
+ dataSize = sizeof(data);
+ retCode = RegEnumValue(hKey, i, valueName, &valueSize, NULL,
+ &dataType, (BYTE *) data, &dataSize);
+
+ if (retCode != ERROR_SUCCESS)
+ break;
+ if (dataType == REG_SZ) {
+ winextdll ext_dll_info;
+
+ memset(&ext_dll_info, 0, sizeof(ext_dll_info));
+ ext_dll_info.dll_name =
+ read_extension_dll_path_from_registry(data);
+ if (ext_dll_info.dll_name) {
+ xarray_push_back(&s_winextdll, &ext_dll_info);
+ DEBUGMSG(("winExtDLL", "registry key %s: DLL %s.\n",
+ data, ext_dll_info.dll_name));
+ }
+ }
+ }
+ RegCloseKey(hKey);
+ }
+}
+
+/** Store the DLL path in dynamically allocated memory. */
+char *
+read_extension_dll_path_from_registry(const TCHAR * keyName)
+{
+ HKEY hKey;
+ DWORD key_value_type = 0;
+ TCHAR valueName[MAX_VALUE_NAME];
+ DWORD key_value_size = MAX_VALUE_NAME;
+ TCHAR valueNameExpanded[MAX_VALUE_NAME];
+ DWORD retCode;
+ char *result = 0;
+
+ retCode = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+ keyName, 0, KEY_QUERY_VALUE, &hKey);
+
+ if (retCode != ERROR_SUCCESS)
+ return 0;
+
+ retCode = RegQueryValueExA(hKey,
+ "Pathname",
+ NULL,
+ &key_value_type,
+ (BYTE *) valueName, &key_value_size);
+
+ if (retCode != ERROR_SUCCESS) {
+ RegCloseKey(hKey);
+ return 0;
+ }
+
+ if (key_value_type == REG_EXPAND_SZ) {
+ if (ExpandEnvironmentStrings
+ (valueName, valueNameExpanded, MAX_VALUE_NAME))
+ result = strdup(valueNameExpanded);
+ } else if (key_value_type == REG_SZ)
+ result = strdup(valueName);
+
+ RegCloseKey(hKey);
+ return result;
+}
+
+/**
+ * Callback function called by the Net-SNMP agent to check for traps waiting
+ * to be processed.
+ */
+void
+subagentTrapCheck(unsigned int clientreg, void *clientarg)
+{
+ while (1) {
+ DWORD dwWaitResult;
+ BOOL bResult;
+ int i;
+ int j;
+ const winextdll *ext_dll_info;
+
+ dwWaitResult = WaitForMultipleObjects(s_trapevent.size,
+ &TRAPEVENT(0), FALSE, 0);
+
+ i = dwWaitResult - WAIT_OBJECT_0;
+ if (i < 0 || i >= s_trapevent.size) {
+ assert(dwWaitResult == WAIT_TIMEOUT);
+ return;
+ }
+
+ assert(s_trapevent.size == s_trapevent_to_dllinfo.size);
+ ext_dll_info = TRAPEVENT_TO_DLLINFO(i);
+ assert(ext_dll_info->subagentTrapEvent == TRAPEVENT(i));
+
+ /*
+ * Reset the signalled event just in case the extension DLL erroneously
+ * allocated a manual-reset event instead of an auto-reset event. It is
+ * important to reset the event BEFORE traps are processed, otherwise a
+ * race condition is triggered between the extension DLL setting the
+ * event and this code resetting the event.
+ */
+ ResetEvent(TRAPEVENT(i));
+
+ if (!ext_dll_info->pfSnmpExtensionTrap) {
+ snmp_log(LOG_ERR,
+ "internal error in SNMP extension DLL %s: a trap is ready"
+ " but the function SnmpExtensionTrap() is missing.\n",
+ ext_dll_info->dll_name);
+ return;
+ }
+
+ /*
+ * Process at most hundred traps per extension DLL. If the extension DLL
+ * has more traps waiting, that's probably a bug in the extension DLL.
+ */
+ for (j = 0; j < 100; j++) {
+ AsnObjectIdentifier Enterprise = { 0, NULL };
+ AsnInteger GenericTrap = 0;
+ AsnInteger SpecificTrap = 0;
+ AsnTimeticks TimeStamp = 0;
+ SnmpVarBindList TrapVarbinds = { NULL, 0 };
+
+ bResult = ext_dll_info->pfSnmpExtensionTrap(&Enterprise,
+ &GenericTrap,
+ &SpecificTrap,
+ &TimeStamp,
+ &TrapVarbinds);
+
+ if (!bResult)
+ break;
+
+ send_trap(&Enterprise, GenericTrap, SpecificTrap, TimeStamp,
+ &TrapVarbinds);
+
+ SnmpUtilVarBindListFree(&TrapVarbinds);
+ }
+ }
+}
+
+void
+send_trap(const AsnObjectIdentifier * const pEnterprise,
+ const AsnInteger GenericTrap,
+ const AsnInteger SpecificTrap,
+ const AsnTimeticks TimeStamp,
+ const SnmpVarBindList * const pTrapVarbinds)
+{
+ /*
+ * A quote from the paragraph in RFC 1908 about SNMPv1 to SNMPv2c
+ * trap translation (http://www.ietf.org/rfc/rfc1908.txt):
+ * <quote>
+ * If a Trap-PDU is received, then it is mapped into a SNMPv2-Trap-
+ * PDU. This is done by prepending onto the variable-bindings field
+ * two new bindings: sysUpTime.0 [6], which takes its value from the
+ * timestamp field of the Trap-PDU; and, snmpTrapOID.0 [6], which is
+ * calculated thusly: if the value of generic-trap field is
+ * `enterpriseSpecific', then the value used is the concatenation of
+ * the enterprise field from the Trap-PDU with two additional sub-
+ * identifiers, `0', and the value of the specific-trap field;
+ * otherwise, the value of the corresponding trap defined in [6] is
+ * used.
+ * </quote>
+ *
+ * Reference [6] refers to RFC 1907 (http://www.ietf.org/rfc/rfc1907.txt),
+ * where the generic trap OIDs have been defined as follows:
+ * coldStart ::= { snmpTraps 1 }
+ * warmStart ::= { snmpTraps 2 }
+ * linkDown ::= { snmpTraps 3 }
+ * linkUp ::= { snmpTraps 4 }
+ * authenticationFailure ::= { snmpTraps 5 }
+ * egpNeighborLoss ::= { snmpTraps 6 }
+ */
+ static const oid sysuptime_oid[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
+ static const size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid);
+
+ static const oid snmptrap_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
+ static const size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid);
+
+ static const oid snmptraps_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 };
+ static const size_t snmptraps_oid_len = OID_LENGTH(snmptraps_oid);
+
+ oid vb2_oid[MAX_OID_LEN];
+ size_t vb2_oid_len;
+
+ netsnmp_variable_list *notification_vars = NULL;
+
+
+ /*
+ * Append the varbind (sysUpTime.0, TimeStamp).
+ */
+ snmp_varlist_add_variable(&notification_vars,
+ sysuptime_oid, sysuptime_oid_len,
+ ASN_TIMETICKS,
+ (const u_char *) &TimeStamp,
+ sizeof(TimeStamp));
+
+ if (GenericTrap == SNMP_GENERICTRAP_ENTERSPECIFIC) {
+ /*
+ * Enterprise specific trap: compute the OID
+ * *pEnterprise + ".0." + SpecificTrap.
+ */
+ copy_oid(vb2_oid, &vb2_oid_len,
+ pEnterprise->ids, pEnterprise->idLength);
+ vb2_oid[vb2_oid_len++] = 0;
+ vb2_oid[vb2_oid_len++] = SpecificTrap;
+ } else {
+ /*
+ * Generic trap: compute the OID snmpTraps + "." + GenericTrap.
+ * Since the GenericTrap values are those defined in SNMPv1, since
+ * these values start at zero, and since the corresponding values in
+ * SNMPv2 start at one, translate the GenericTrap value accordingly.
+ * See also http://www.ietf.org/rfc/rfc1214.txt and
+ * http://www.ietf.org/rfc/rfc3418.txt.
+ */
+ copy_oid(vb2_oid, &vb2_oid_len, snmptraps_oid, snmptraps_oid_len);
+ vb2_oid[vb2_oid_len++] = GenericTrap + 1;
+ }
+
+ /*
+ * Append the varbind (snmpTrap, vb2_oid).
+ */
+ snmp_varlist_add_variable(&notification_vars,
+ snmptrap_oid, snmptrap_oid_len,
+ ASN_OBJECT_ID,
+ (u_char *) vb2_oid,
+ vb2_oid_len * sizeof(vb2_oid[0]));
+
+ /*
+ * Append all the varbinds in pTrapVarbinds.
+ */
+ append_windows_varbind_list(&notification_vars, pTrapVarbinds);
+
+ /*
+ * Send trap.
+ */
+ send_v2trap(notification_vars);
+
+ /*
+ * Free the memory allocated for notification_vars.
+ */
+ snmp_free_varbind(notification_vars);
+}
+
+/**
+ * Convert a Windows varbind to a Net-SNMP varbind and add it to the list of
+ * varbinds 'net_snmp_varbinds'.
+ *
+ * @note The memory allocated inside this function must be freed by the caller
+ * as follows: snmp_free_varbind(*net_snmp_varbinds).
+ */
+static int
+append_windows_varbind_list(netsnmp_variable_list **
+ const net_snmp_varbinds,
+ const SnmpVarBindList * const win_varbinds)
+{
+ int i, status = SNMP_ERR_NOERROR;
+
+ for (i = 0; i < win_varbinds->len; i++) {
+ status =
+ append_windows_varbind(net_snmp_varbinds,
+ &win_varbinds->list[i]);
+ if (status != SNMP_ERR_NOERROR)
+ break;
+ }
+ return status;
+}
+
+static int
+append_windows_varbind(netsnmp_variable_list ** const net_snmp_varbinds,
+ const SnmpVarBind * const win_varbind)
+{
+ switch (win_varbind->value.asnType) {
+ case MS_ASN_INTEGER:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_INTEGER,
+ (const u_char *) &win_varbind->value.
+ asnValue.number,
+ sizeof(win_varbind->value.asnValue.
+ number));
+ break;
+ case MS_ASN_BITS:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_BIT_STR,
+ win_varbind->value.asnValue.bits.stream,
+ win_varbind->value.asnValue.bits.length);
+ break;
+ case MS_ASN_OCTETSTRING:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_OCTET_STR,
+ win_varbind->value.asnValue.string.
+ stream,
+ win_varbind->value.asnValue.string.
+ length);
+ break;
+ case MS_ASN_NULL:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_NULL, 0, 0);
+ break;
+ case MS_ASN_OBJECTIDENTIFIER:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_OBJECT_ID,
+ (u_char *) win_varbind->value.asnValue.
+ object.ids,
+ win_varbind->value.asnValue.object.
+ idLength * sizeof(oid));
+ break;
+
+ /*
+ * MS_ASN_INTEGER32: synonym for MS_ASN_INTEGER.
+ */
+
+ case MS_ASN_SEQUENCE:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_SEQUENCE,
+ win_varbind->value.asnValue.sequence.
+ stream,
+ win_varbind->value.asnValue.sequence.
+ length);
+ break;
+ case MS_ASN_IPADDRESS:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_IPADDRESS,
+ win_varbind->value.asnValue.address.
+ stream,
+ win_varbind->value.asnValue.address.
+ length);
+ break;
+ case MS_ASN_COUNTER32:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_COUNTER,
+ (const u_char *) &win_varbind->value.
+ asnValue.counter,
+ sizeof(win_varbind->value.asnValue.
+ counter));
+ break;
+ case MS_ASN_GAUGE32:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_GAUGE,
+ (const u_char *) &win_varbind->value.
+ asnValue.gauge,
+ sizeof(win_varbind->value.asnValue.
+ gauge));
+ break;
+ case MS_ASN_TIMETICKS:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_TIMETICKS,
+ (const u_char *) &win_varbind->value.
+ asnValue.ticks,
+ sizeof(win_varbind->value.asnValue.
+ ticks));
+ break;
+ case MS_ASN_OPAQUE: // AsnOctetString
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_OPAQUE,
+ win_varbind->value.asnValue.arbitrary.
+ stream,
+ win_varbind->value.asnValue.arbitrary.
+ length);
+ break;
+ case MS_ASN_COUNTER64:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_COUNTER64,
+ (const u_char *) &win_varbind->value.
+ asnValue.counter64,
+ sizeof(win_varbind->value.asnValue.
+ counter64));
+ break;
+ case MS_ASN_UINTEGER32:
+ snmp_varlist_add_variable(net_snmp_varbinds, win_varbind->name.ids,
+ win_varbind->name.idLength,
+ ASN_UNSIGNED,
+ (const u_char *) &win_varbind->value.
+ asnValue.unsigned32,
+ sizeof(win_varbind->value.asnValue.
+ unsigned32));
+ break;
+ default:
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Convert a Net-SNMP varbind to a WinSNMP varbind list.
+ *
+ * @param[out] pVarBindList WinSNMP varbind list, initialized by this
+ * function.
+ * @param[in] varbind Net-SNMP varbind.
+ */
+int
+convert_to_windows_varbind_list(SnmpVarBindList * pVarBindList,
+ netsnmp_variable_list * varbind)
+{
+ SnmpVarBind *win_varbind;
+
+ assert(pVarBindList);
+ assert(varbind);
+
+ pVarBindList->len = 1;
+ pVarBindList->list
+ = (SnmpVarBind *) SnmpUtilMemAlloc(pVarBindList->len
+ *
+ sizeof(pVarBindList->list[0]));
+ if (pVarBindList->list == 0)
+ goto generr;
+
+ memset(&pVarBindList->list[0], 0, sizeof(pVarBindList->list[0]));
+
+ win_varbind = &pVarBindList->list[0];
+
+ if (varbind->name
+ && !copy_oid_to_new_windows_oid(&win_varbind->name,
+ varbind->name,
+ varbind->name_length))
+ goto generr;
+
+ switch (varbind->type) {
+ case ASN_BOOLEAN:
+ // There is no equivalent type in Microsoft's <snmp.h>.
+ assert(0);
+ win_varbind->value.asnType = MS_ASN_INTEGER;
+ win_varbind->value.asnValue.number = *(varbind->val.integer);
+ break;
+ case ASN_INTEGER:
+ win_varbind->value.asnType = MS_ASN_INTEGER;
+ win_varbind->value.asnValue.number = *(varbind->val.integer);
+ break;
+ case ASN_BIT_STR:
+ win_varbind->value.asnType = MS_ASN_BITS;
+ win_varbind->value.asnValue.string.stream
+ = winsnmp_memdup(varbind->val.string, varbind->val_len);
+ win_varbind->value.asnValue.string.length =
+ (UINT) (varbind->val_len);
+ win_varbind->value.asnValue.string.dynamic = TRUE;
+ break;
+ case ASN_OCTET_STR:
+ win_varbind->value.asnType = MS_ASN_OCTETSTRING;
+ win_varbind->value.asnValue.string.stream
+ = winsnmp_memdup(varbind->val.string, varbind->val_len);
+ win_varbind->value.asnValue.string.length =
+ (UINT) (varbind->val_len);
+ win_varbind->value.asnValue.string.dynamic = TRUE;
+ break;
+ case ASN_NULL:
+ win_varbind->value.asnType = MS_ASN_NULL;
+ memset(&win_varbind->value, 0, sizeof(win_varbind->value));
+ break;
+ case ASN_OBJECT_ID:
+ win_varbind->value.asnType = MS_ASN_OBJECTIDENTIFIER;
+ if (!copy_oid_to_new_windows_oid
+ (&win_varbind->value.asnValue.object, varbind->val.objid,
+ varbind->val_len / sizeof(varbind->val.objid[0])))
+ return SNMP_ERR_GENERR;
+ break;
+ case ASN_SEQUENCE:
+ win_varbind->value.asnType = MS_ASN_SEQUENCE;
+ win_varbind->value.asnValue.string.stream
+ = winsnmp_memdup(varbind->val.string, varbind->val_len);
+ win_varbind->value.asnValue.string.length =
+ (UINT) (varbind->val_len);
+ win_varbind->value.asnValue.string.dynamic = TRUE;
+ break;
+ case ASN_SET:
+ // There is no equivalent type in Microsoft's <snmp.h>.
+ assert(0);
+ win_varbind->value.asnType = MS_ASN_INTEGER;
+ win_varbind->value.asnValue.number = *(varbind->val.integer);
+ break;
+ case ASN_IPADDRESS:
+ win_varbind->value.asnType = MS_ASN_IPADDRESS;
+ win_varbind->value.asnValue.string.stream
+ = winsnmp_memdup(varbind->val.string, varbind->val_len);
+ win_varbind->value.asnValue.string.length =
+ (UINT) (varbind->val_len);
+ win_varbind->value.asnValue.string.dynamic = TRUE;
+ break;
+ case ASN_COUNTER:
+ win_varbind->value.asnType = MS_ASN_COUNTER32;
+ win_varbind->value.asnValue.counter = *(varbind->val.integer);
+ break;
+ /*
+ * ASN_GAUGE == ASN_UNSIGNED
+ */
+ case ASN_UNSIGNED:
+ win_varbind->value.asnType = MS_ASN_UNSIGNED32;
+ win_varbind->value.asnValue.unsigned32 = *(varbind->val.integer);
+ break;
+ case ASN_TIMETICKS:
+ win_varbind->value.asnType = MS_ASN_TIMETICKS;
+ win_varbind->value.asnValue.ticks = *(varbind->val.integer);
+ break;
+ case ASN_OPAQUE:
+ win_varbind->value.asnType = MS_ASN_OPAQUE;
+ win_varbind->value.asnValue.string.stream
+ = winsnmp_memdup(varbind->val.string, varbind->val_len);
+ win_varbind->value.asnValue.string.length =
+ (UINT) (varbind->val_len);
+ win_varbind->value.asnValue.string.dynamic = TRUE;
+ break;
+ case ASN_COUNTER64:
+ win_varbind->value.asnType = MS_ASN_COUNTER64;
+ win_varbind->value.asnValue.counter64.HighPart
+ = varbind->val.counter64->high;
+ win_varbind->value.asnValue.counter64.LowPart
+ = varbind->val.counter64->low;
+ break;
+ default:
+ assert(0);
+ goto generr;
+ }
+
+ return SNMP_ERR_NOERROR;
+
+ generr:
+ SnmpUtilVarBindListFree(pVarBindList);
+ memset(pVarBindList, 0, sizeof(*pVarBindList));
+ return SNMP_ERR_GENERR;
+}
+
+/** Convert a Windows SNMP error code to the equivalent Net-SNMP error code. */
+int
+convert_win_snmp_err(const int win_snmp_err)
+{
+ switch (win_snmp_err) {
+ case SNMP_ERRORSTATUS_NOERROR:
+ return SNMP_ERR_NOERROR;
+ case SNMP_ERRORSTATUS_TOOBIG:
+ return SNMP_ERR_TOOBIG;
+ case SNMP_ERRORSTATUS_NOSUCHNAME:
+ /*
+ * Note: SNMP extension DLLs return SNMP_ERRORSTATUS_NOSUCHNAME
+ * when either noSuchObject or noSuchInstance should be returned to
+ * the SNMP manager (assuming SNMPv2c or SNMPv3). Unfortunately it
+ * is not possible without consulting the MIB to find out whether
+ * either SNMP_NOSUCHINSTANCE or SNMP_NOSUCHOBJECT should be returned.
+ * See also RFC 1448.
+ */
+ return SNMP_NOSUCHINSTANCE;
+ case SNMP_ERRORSTATUS_BADVALUE:
+ return SNMP_ERR_BADVALUE;
+ case SNMP_ERRORSTATUS_READONLY:
+ return SNMP_ERR_READONLY;
+ case SNMP_ERRORSTATUS_GENERR:
+ return SNMP_ERR_GENERR;
+ case SNMP_ERRORSTATUS_NOACCESS:
+ return SNMP_ERR_NOACCESS;
+ case SNMP_ERRORSTATUS_WRONGTYPE:
+ return SNMP_ERR_WRONGTYPE;
+ case SNMP_ERRORSTATUS_WRONGLENGTH:
+ return SNMP_ERR_WRONGLENGTH;
+ case SNMP_ERRORSTATUS_WRONGENCODING:
+ return SNMP_ERR_WRONGENCODING;
+ case SNMP_ERRORSTATUS_WRONGVALUE:
+ return SNMP_ERR_WRONGVALUE;
+ case SNMP_ERRORSTATUS_NOCREATION:
+ return SNMP_ERR_NOCREATION;
+ case SNMP_ERRORSTATUS_INCONSISTENTVALUE:
+ return SNMP_ERR_INCONSISTENTVALUE;
+ case SNMP_ERRORSTATUS_RESOURCEUNAVAILABLE:
+ return SNMP_ERR_RESOURCEUNAVAILABLE;
+ case SNMP_ERRORSTATUS_COMMITFAILED:
+ return SNMP_ERR_COMMITFAILED;
+ case SNMP_ERRORSTATUS_UNDOFAILED:
+ return SNMP_ERR_UNDOFAILED;
+ case SNMP_ERRORSTATUS_AUTHORIZATIONERROR:
+ return SNMP_ERR_AUTHORIZATIONERROR;
+ case SNMP_ERRORSTATUS_NOTWRITABLE:
+ return SNMP_ERR_NOTWRITABLE;
+ case SNMP_ERRORSTATUS_INCONSISTENTNAME:
+ return SNMP_ERR_INCONSISTENTNAME;
+ }
+ assert(0);
+ return SNMP_ERR_GENERR;
+}
+
+/**
+ * Look up the extension DLL view that was registered with the given OID.
+ */
+static winextdll_view *
+lookup_view_by_oid(oid * const name, const size_t name_len)
+{
+ int i;
+
+ for (i = 0; i < s_winextdll_view.size; i++) {
+ if (netsnmp_oid_equals(WINEXTDLL_VIEW(i).name,
+ WINEXTDLL_VIEW(i).name_length,
+ name, name_len) == 0
+ && WINEXTDLL_VIEW(i).my_handler) {
+ return &WINEXTDLL_VIEW(i);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Copy an OID.
+ *
+ * @param[out] to_name Number of elements written to destination OID.
+ * @param[out] to_name_len Length of destination OID. Must have at least
+ * min(from_name_len, MAX_OID_LEN) elements.
+ * @param[in] from_name Original OID.
+ * @param[in] from_name_len Length of original OID.
+ */
+static void
+copy_oid(oid * const to_name, size_t * const to_name_len,
+ const oid * const from_name, const size_t from_name_len)
+{
+ int j;
+
+ assert(to_name);
+ assert(to_name_len);
+ assert(from_name);
+
+ for (j = 0; j < from_name_len && j < MAX_OID_LEN; j++)
+ to_name[j] = from_name[j];
+
+ *to_name_len = j;
+}
+
+/**
+ * Convert a Net-SNMP OID into a Windows OID and allocate memory for the
+ * Windows OID.
+ *
+ * @param[out] windows_oid Pointer to a AsnObjectIdentifier.
+ * @param[in] name Pointer to an array with elements of type oid
+ * and length name_len.
+ * @param[in] name_len Number of elements of input and output OID.
+ */
+static UINT *
+copy_oid_to_new_windows_oid(AsnObjectIdentifier * const windows_oid,
+ const oid * const name, const size_t name_len)
+{
+ assert(windows_oid);
+ assert(windows_oid->ids == 0);
+ assert(windows_oid->idLength == 0);
+ assert(name);
+
+ windows_oid->ids
+ =
+ (UINT *) winsnmp_memdup(name,
+ sizeof(windows_oid->ids[0]) * name_len);
+ windows_oid->idLength = (UINT) name_len;
+ return windows_oid->ids;
+}
+
+static u_char *
+winsnmp_memdup(const void *src, const size_t len)
+{
+ u_char *p;
+
+ assert(len == (UINT) len);
+
+ p = SnmpUtilMemAlloc((UINT) len);
+ if (p)
+ memcpy(p, src, len);
+ return p;
+}
+
+#if 0
+/** Initialize array 'a'. */
+static void
+xarray_init(xarray * a, size_t elem_size)
+{
+ assert(a);
+
+ memset(a, 0, sizeof(*a));
+ a->elem_size = elem_size;
+}
+#endif
+
+/** Deallocate any memory that was dynamically allocated for 'a'. */
+static void
+xarray_destroy(xarray * a)
+{
+ assert(a);
+
+ xarray_reserve(a, 0);
+}
+
+/**
+ * Append the contents of the address range [ elem, elem + a->elem_size [ to a.
+ *
+ * Resize a if necessary.
+ *
+ * @return A pointer to the address where the data has been copied upon success,
+ * or NULL upon failure.
+ */
+static void *
+xarray_push_back(xarray * a, const void *elem)
+{
+ assert(a);
+ assert(elem);
+ assert(a->size <= a->reserved);
+
+ if (a->size == a->reserved)
+ xarray_reserve(a, a->reserved == 0 ? 16 : 2 * a->reserved);
+ if (a->size < a->reserved) {
+ assert(a->size < a->reserved);
+ return memcpy((char *) (a->p) + a->elem_size * a->size++, elem,
+ a->elem_size);
+ }
+ return NULL;
+}
+
+#if 0
+/** Erase [ elem, elem + a->elem_size [ from a. */
+static void
+xarray_erase(xarray * a, void *const elem)
+{
+ assert(a);
+ assert(a->size >= 1);
+ assert(a->p <= elem);
+ assert((const char *) elem + a->elem_size <=
+ (char *) a->p + a->size * a->elem_size);
+ assert(((const char *) elem - (char *) a->p) % a->elem_size == 0);
+
+ a->size--;
+ memmove((char *) elem, (char *) elem + a->elem_size,
+ a->size - ((const char *) elem -
+ (char *) a->p) / a->elem_size);
+}
+#endif
+
+/**
+ * Change the number of allocated elements to 'reserved'.
+ *
+ * Can be used either for enlarging or for shrinking the memory allocated for
+ * 'a'. Does not modify 'a' if memory allocation fails. Newly allocted memory
+ * is not initialized.
+ *
+ * @return != NULL upon success, NULL upon failure.
+ */
+static void *
+xarray_reserve(xarray * a, int reserved)
+{
+ assert(a);
+ assert(a->size <= a->reserved);
+
+ if ((a->p = realloc(a->p, a->elem_size * reserved)))
+ a->reserved = reserved;
+ else
+ a->reserved = 0;
+ return a->p;
+}
+
+#endif /* USING_WINEXTDLL_MODULE */