1926 lines
72 KiB
C
1926 lines
72 KiB
C
/**
|
|
* @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/net-snmp-features.h>
|
|
#include <net-snmp/agent/mib_module_config.h>
|
|
|
|
#ifdef USING_WINEXTDLL_MODULE
|
|
|
|
#include <net-snmp/types.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <windows.h>
|
|
#include <winerror.h>
|
|
#include "../../win32/Snmp-winExtDLL.h"
|
|
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <net-snmp/library/snmp_assert.h>
|
|
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
|
#include "util_funcs.h"
|
|
#include "winExtDLL.h"
|
|
|
|
netsnmp_feature_require(oid_is_subtree)
|
|
|
|
|
|
#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);
|
|
|
|
typedef BOOL (WINAPI *pfIsWow64Process)(HANDLE hProcess, BOOL *Wow64Process);
|
|
|
|
|
|
/**
|
|
* 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;
|
|
PFNSNMPEXTENSIONCLOSE pfSnmpExtensionClose;
|
|
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 int snmp_oid_compare_n_w(const oid * name1, size_t len1,
|
|
const UINT * name2, UINT len2);
|
|
static int snmp_oid_compare_w_n(const UINT * name1, UINT len1,
|
|
const oid * name2, size_t len2);
|
|
static int netsnmp_oid_is_subtree_n_w(const oid * name1, size_t len1,
|
|
const UINT * name2, UINT len2);
|
|
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 void copy_oid_n_w(oid * const to_name, size_t * const to_name_len,
|
|
const UINT * const from_name,
|
|
const UINT 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 int snmp_set_var_objid_w(netsnmp_variable_list * var,
|
|
const UINT * name, UINT name_length);
|
|
static netsnmp_variable_list *
|
|
snmp_varlist_add_variable_w(netsnmp_variable_list ** varlist,
|
|
const UINT * name, UINT name_length,
|
|
u_char type, const void * value, size_t 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 const oid mibii_system_mib[] = { 1, 3, 6, 1, 2, 1, 1 };
|
|
static OSVERSIONINFO s_versioninfo = { sizeof(s_versioninfo) };
|
|
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, is_wow64_process = FALSE;
|
|
int i;
|
|
uint32_t uptime_reference;
|
|
pfIsWow64Process IsWow64Process;
|
|
|
|
DEBUGMSG(("winExtDLL", "init_winExtDLL started.\n"));
|
|
|
|
GetVersionEx(&s_versioninfo);
|
|
|
|
IsWow64Process =
|
|
(pfIsWow64Process)GetProcAddress(GetModuleHandle("kernel32"),
|
|
"IsWow64Process");
|
|
if (IsWow64Process)
|
|
(*IsWow64Process)(GetCurrentProcess(), &is_wow64_process);
|
|
|
|
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;
|
|
|
|
netsnmp_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->pfSnmpExtensionClose = (PFNSNMPEXTENSIONCLOSE)
|
|
GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionClose");
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* At least on a 64-bit Windows 7 system invoking SnmpExtensionInit()
|
|
* in the 32-bit version of evntagnt.dll hangs. Also, all queries in
|
|
* lmmib2.dll fail with "generic error" on a 64-bit Windows 7 system.
|
|
* So skip these two DLLs.
|
|
*/
|
|
if (s_versioninfo.dwMajorVersion >= 6
|
|
&& ((is_wow64_process
|
|
&& basename_equals(ext_dll_info->dll_name, "evntagnt.dll"))
|
|
|| basename_equals(ext_dll_info->dll_name, "lmmib2.dll"))) {
|
|
DEBUGMSG(("winExtDLL", "init_winExtDLL: skipped DLL %s.\n",
|
|
ext_dll_info->dll_name));
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* 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 a correct value for this first argument 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 = NULL;
|
|
if (!is_wow64_process && s_versioninfo.dwMajorVersion >= 6)
|
|
uptime_reference = GetTickCount() - 10 * SnmpSvcGetUptime();
|
|
else
|
|
uptime_reference = GetTickCount() / 10;
|
|
result =
|
|
ext_dll_info->pfSnmpExtensionInit(uptime_reference,
|
|
&ext_dll_info->
|
|
subagentTrapEvent, &view);
|
|
|
|
if (!result) {
|
|
DEBUGMSG(("winExtDLL",
|
|
"init_winExtDLL: initialization of DLL %s failed.\n",
|
|
ext_dll_info->dll_name));
|
|
/*
|
|
* At least on Windows 7 SnmpExtensionInit() in some extension
|
|
* agent DLLs returns "FALSE" although initialization
|
|
* succeeded. Hence ignore the SnmpExtensionInit() return value on
|
|
* Windows Vista and later.
|
|
*/
|
|
if (s_versioninfo.dwMajorVersion < 6) {
|
|
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;
|
|
if (view.idLength == 0) {
|
|
DEBUGMSG(("winExtDLL",
|
|
"init_winExtDLL: DLL %s did not register an OID range.\n",
|
|
ext_dll_info->dll_name));
|
|
continue;
|
|
}
|
|
/*
|
|
* Skip the mib-2 system section on Windows Vista and later because
|
|
* at least on a 64-bit Windows 7 system all queries in that section
|
|
* fail with status "generic error".
|
|
*/
|
|
if (s_versioninfo.dwMajorVersion >= 6
|
|
&& snmp_oid_compare_w_n(view.ids, view.idLength, mibii_system_mib,
|
|
sizeof(mibii_system_mib) /
|
|
sizeof(mibii_system_mib[0])) == 0) {
|
|
DEBUGMSG(("winExtDLL",
|
|
"init_winExtDLL: skipping system section of DLL %s.\n",
|
|
ext_dll_info->dll_name));
|
|
continue;
|
|
}
|
|
copy_oid_n_w(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_n_w(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);
|
|
if (ext_dll_info->dll_handle) {
|
|
if (ext_dll_info->pfSnmpExtensionClose) {
|
|
DEBUGMSG(("winExtDLL", "closing %s.\n",
|
|
ext_dll_info->dll_name));
|
|
ext_dll_info->pfSnmpExtensionClose();
|
|
}
|
|
/*
|
|
* Freeing the Broadcom SNMP extension libraries triggers
|
|
* a deadlock, so skip bcmif.dll and baspmgnt.dll.
|
|
*/
|
|
if (!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);
|
|
|
|
netsnmp_assert(strchr(path, '/') == 0);
|
|
netsnmp_assert(strchr(basename, '/') == 0);
|
|
netsnmp_assert(strchr(basename, '\\') == 0);
|
|
|
|
return path_len >= basename_len + 1
|
|
&& path[path_len - basename_len - 1] == '\\'
|
|
&& strcasecmp(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) {
|
|
netsnmp_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;
|
|
|
|
netsnmp_assert(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Translate Net-SNMP request mode into an SnmpExtensionQuery() PDU type
|
|
* or into an SnmpExtensionQueryEx() request type.
|
|
*/
|
|
static int
|
|
get_request_type(int mode, int request_type, UINT *nRequestType)
|
|
{
|
|
switch (request_type) {
|
|
case 0:
|
|
/* SnmpExtensionQuery() PDU type */
|
|
switch (mode) {
|
|
case MODE_GET:
|
|
*nRequestType = SNMP_PDU_GET;
|
|
return 1;
|
|
case MODE_GETNEXT:
|
|
*nRequestType = SNMP_PDU_GETNEXT;
|
|
return 1;
|
|
case MODE_SET_RESERVE1:
|
|
return 0;
|
|
case MODE_SET_RESERVE2:
|
|
return 0;
|
|
case MODE_SET_ACTION:
|
|
return 0;
|
|
case MODE_SET_UNDO:
|
|
return 0;
|
|
case MODE_SET_COMMIT:
|
|
*nRequestType = SNMP_PDU_SET;
|
|
return 1;
|
|
case MODE_SET_FREE:
|
|
return 0;
|
|
default:
|
|
DEBUGMSG(("winExtDLL", "internal error: invalid mode %d.\n", mode));
|
|
netsnmp_assert(0);
|
|
return 0;
|
|
}
|
|
case 1:
|
|
/* SnmpExtensionQueryEx() request type */
|
|
switch (mode) {
|
|
case MODE_GET:
|
|
*nRequestType = SNMP_EXTENSION_GET;
|
|
return 1;
|
|
case MODE_GETNEXT:
|
|
*nRequestType = SNMP_EXTENSION_GET_NEXT;
|
|
return 1;
|
|
case MODE_SET_RESERVE1:
|
|
*nRequestType = SNMP_EXTENSION_SET_TEST;
|
|
return 1;
|
|
case MODE_SET_RESERVE2:
|
|
return 0;
|
|
case MODE_SET_ACTION:
|
|
return 0;
|
|
case MODE_SET_UNDO:
|
|
*nRequestType = SNMP_EXTENSION_SET_UNDO;
|
|
return 1;
|
|
case MODE_SET_COMMIT:
|
|
*nRequestType = SNMP_EXTENSION_SET_COMMIT;
|
|
return 1;
|
|
case MODE_SET_FREE:
|
|
*nRequestType = SNMP_EXTENSION_SET_CLEANUP;
|
|
return 1;
|
|
default:
|
|
DEBUGMSG(("winExtDLL", "internal error: invalid mode %d.\n", mode));
|
|
netsnmp_assert(0);
|
|
return 0;
|
|
}
|
|
default:
|
|
DEBUGMSG(("winExtDLL", "internal error: invalid argument %d.\n",
|
|
request_type));
|
|
netsnmp_assert(0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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;
|
|
int rc;
|
|
|
|
netsnmp_assert(ext_dll_view_info);
|
|
ext_dll_info = ext_dll_view_info->winextdll_info;
|
|
#if ! defined(NDEBUG)
|
|
netsnmp_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"));
|
|
netsnmp_assert(0);
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
if (!get_request_type(reqinfo->mode, !!ext_dll_info->pfSnmpExtensionQueryEx,
|
|
&nRequestType)) {
|
|
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;
|
|
netsnmp_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;
|
|
}
|
|
|
|
netsnmp_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_w_n(win_varbinds.list[0].name.ids,
|
|
win_varbinds.list[0].name.idLength,
|
|
reginfo->rootoid,
|
|
reginfo->rootoid_len) < 0) {
|
|
DEBUGIF("winExtDLL") {
|
|
size_t oid1_namelen = 0, oid2_namelen = 0, outlen1 = 0,
|
|
outlen2 = 0;
|
|
char *oid1_name = NULL, *oid2_name = NULL;
|
|
int overflow1 = 0, overflow2 = 0;
|
|
|
|
netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
|
|
netsnmp_sprint_realloc_objid((u_char **) & oid1_name,
|
|
&oid1_namelen, &outlen1, 1,
|
|
&overflow1, (const oid *)
|
|
win_varbinds.list[0].name.ids,
|
|
win_varbinds.list[0].name.idLength);
|
|
netsnmp_sprint_realloc_objid((u_char **) & oid2_name,
|
|
&oid2_namelen, &outlen2, 1,
|
|
&overflow2, reginfo->rootoid,
|
|
reginfo->rootoid_len);
|
|
DEBUGMSG(("winExtDLL",
|
|
"extension DLL %s: replacing OID %s%s by OID %s%s.\n",
|
|
ext_dll_info->dll_name,
|
|
oid1_name, overflow1 ? " [TRUNCATED]" : "",
|
|
oid2_name, overflow2 ? " [TRUNCATED]" : ""));
|
|
free(oid2_name);
|
|
free(oid1_name);
|
|
}
|
|
|
|
SnmpUtilOidFree(&win_varbinds.list[0].name);
|
|
memset(&win_varbinds.list[0].name, 0,
|
|
sizeof(win_varbinds.list[0].name));
|
|
copy_oid_to_new_windows_oid(&win_varbinds.list[0].name,
|
|
reginfo->rootoid,
|
|
reginfo->rootoid_len);
|
|
}
|
|
|
|
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) {
|
|
DEBUGIF("winExtDLL") {
|
|
size_t oid_namelen = 0, outlen = 0;
|
|
char *oid_name = NULL;
|
|
int overflow = 0;
|
|
|
|
netsnmp_sprint_realloc_objid((u_char **) & oid_name,
|
|
&oid_namelen,
|
|
&outlen, 1, &overflow,
|
|
ext_dll_view_info->name,
|
|
ext_dll_view_info->name_length);
|
|
DEBUGMSG(("winExtDLL", "extension DLL %s: SNMP query function"
|
|
" returned error code %lu (Windows) / %d (Net-SNMP)"
|
|
" for request type %d, OID %s%s, ASN type %d and"
|
|
" value %ld.\n",
|
|
ext_dll_info->dll_name, ErrorStatus, rc, nRequestType,
|
|
oid_name, overflow ? " [TRUNCATED]" : "",
|
|
win_varbinds.list[0].value.asnType,
|
|
win_varbinds.list[0].value.asnValue.number));
|
|
free(oid_name);
|
|
}
|
|
netsnmp_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_n_w(ext_dll_view_info->name,
|
|
ext_dll_view_info->name_length,
|
|
win_varbind->name.ids,
|
|
win_varbind->name.idLength) == 0
|
|
&& snmp_oid_compare_n_w(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_w(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]);
|
|
netsnmp_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;
|
|
|
|
if (s_trapevent.size == 0)
|
|
return;
|
|
|
|
dwWaitResult = WaitForMultipleObjects(s_trapevent.size,
|
|
&TRAPEVENT(0), FALSE, 0);
|
|
|
|
i = dwWaitResult - WAIT_OBJECT_0;
|
|
if (i < 0 || i >= s_trapevent.size) {
|
|
netsnmp_assert(dwWaitResult == WAIT_TIMEOUT);
|
|
return;
|
|
}
|
|
|
|
netsnmp_assert(s_trapevent.size == s_trapevent_to_dllinfo.size);
|
|
ext_dll_info = TRAPEVENT_TO_DLLINFO(i);
|
|
netsnmp_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(¬ification_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_n_w(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(¬ification_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(¬ification_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_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_INTEGER,
|
|
&win_varbind->value.asnValue.number,
|
|
sizeof(win_varbind->value.asnValue.
|
|
number));
|
|
break;
|
|
case MS_ASN_BITS:
|
|
snmp_varlist_add_variable_w(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_w(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_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_NULL, 0, 0);
|
|
break;
|
|
case MS_ASN_OBJECTIDENTIFIER:
|
|
snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_OBJECT_ID,
|
|
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_w(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_w(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_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_COUNTER,
|
|
&win_varbind->value.asnValue.counter,
|
|
sizeof(win_varbind->value.asnValue.
|
|
counter));
|
|
break;
|
|
case MS_ASN_GAUGE32:
|
|
snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_GAUGE,
|
|
&win_varbind->value.asnValue.gauge,
|
|
sizeof(win_varbind->value.asnValue.
|
|
gauge));
|
|
break;
|
|
case MS_ASN_TIMETICKS:
|
|
snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_TIMETICKS,
|
|
&win_varbind->value.asnValue.ticks,
|
|
sizeof(win_varbind->value.asnValue.
|
|
ticks));
|
|
break;
|
|
case MS_ASN_OPAQUE: // AsnOctetString
|
|
snmp_varlist_add_variable_w(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_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_COUNTER64,
|
|
&win_varbind->value.asnValue.counter64,
|
|
sizeof(win_varbind->value.asnValue.
|
|
counter64));
|
|
break;
|
|
case MS_ASN_UINTEGER32:
|
|
snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
|
|
win_varbind->name.idLength,
|
|
ASN_UNSIGNED,
|
|
&win_varbind->value.asnValue.unsigned32,
|
|
sizeof(win_varbind->value.asnValue.
|
|
unsigned32));
|
|
break;
|
|
default:
|
|
return SNMP_ERR_GENERR;
|
|
}
|
|
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
static int
|
|
snmp_set_var_objid_w(netsnmp_variable_list * var, const UINT * name,
|
|
UINT name_length)
|
|
{
|
|
netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
|
|
return snmp_set_var_objid(var, (const oid *) name, name_length);
|
|
}
|
|
|
|
static netsnmp_variable_list *
|
|
snmp_varlist_add_variable_w(netsnmp_variable_list ** varlist, const UINT * name,
|
|
UINT name_length, u_char type, const void * value,
|
|
size_t len)
|
|
{
|
|
netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
|
|
return snmp_varlist_add_variable(varlist, (const oid *) name, name_length, type,
|
|
value, len);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
netsnmp_assert(pVarBindList);
|
|
netsnmp_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>.
|
|
netsnmp_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>.
|
|
netsnmp_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:
|
|
netsnmp_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;
|
|
}
|
|
netsnmp_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;
|
|
}
|
|
|
|
static int
|
|
snmp_oid_compare_n_w(const oid * name1, size_t len1, const UINT * name2,
|
|
UINT len2)
|
|
{
|
|
netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
|
|
return snmp_oid_compare(name1, len1, (const oid *) name2, len2);
|
|
}
|
|
|
|
static int
|
|
snmp_oid_compare_w_n(const UINT * name1, UINT len1, const oid * name2,
|
|
size_t len2)
|
|
{
|
|
netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
|
|
return snmp_oid_compare((const oid *) name1, len1, name2, len2);
|
|
}
|
|
|
|
static int
|
|
netsnmp_oid_is_subtree_n_w(const oid * name1, size_t len1, const UINT * name2,
|
|
UINT len2)
|
|
{
|
|
netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
|
|
return netsnmp_oid_is_subtree(name1, len1, (const oid *) name2, len2);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
netsnmp_assert(to_name);
|
|
netsnmp_assert(to_name_len);
|
|
netsnmp_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;
|
|
}
|
|
|
|
/**
|
|
* 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_n_w(oid * const to_name, size_t * const to_name_len,
|
|
const UINT * const from_name, const UINT from_name_len)
|
|
{
|
|
netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
|
|
copy_oid(to_name, to_name_len, (const oid *) from_name, from_name_len);
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
netsnmp_assert(windows_oid);
|
|
netsnmp_assert(windows_oid->ids == 0);
|
|
netsnmp_assert(windows_oid->idLength == 0);
|
|
netsnmp_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;
|
|
|
|
netsnmp_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)
|
|
{
|
|
netsnmp_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)
|
|
{
|
|
netsnmp_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)
|
|
{
|
|
netsnmp_assert(a);
|
|
netsnmp_assert(elem);
|
|
netsnmp_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) {
|
|
netsnmp_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)
|
|
{
|
|
netsnmp_assert(a);
|
|
netsnmp_assert(a->size >= 1);
|
|
netsnmp_assert(a->p <= elem);
|
|
netsnmp_assert((const char *) elem + a->elem_size <=
|
|
(char *) a->p + a->size * a->elem_size);
|
|
netsnmp_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)
|
|
{
|
|
netsnmp_assert(a);
|
|
netsnmp_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 */
|