diff options
Diffstat (limited to 'agent/mibgroup/winExtDLL.c')
-rwxr-xr-x | agent/mibgroup/winExtDLL.c | 1694 |
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(¬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(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(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 */ |