/** * @brief winExtDLL Net-SNMP agent extension module. * * Copyright (c) 2006-2009 Alex Burger. * Copyright (c) 2009-2010 Bart Van Assche . * * 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 SNMP Functions * 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 by #include 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 #include #include #ifdef USING_WINEXTDLL_MODULE #include #include #include #include #include #include #include #include "../../win32/Snmp-winExtDLL.h" #include #include #include #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)(uintptr_t) 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)(uintptr_t) GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionInit"); ext_dll_info->pfSnmpExtensionInitEx = (PFNSNMPEXTENSIONINITEX)(uintptr_t) GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionInitEx"); ext_dll_info->pfSnmpExtensionClose = (PFNSNMPEXTENSIONCLOSE) GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionClose"); ext_dll_info->pfSnmpExtensionQuery = (PFNSNMPEXTENSIONQUERY)(uintptr_t) GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionQuery"); ext_dll_info->pfSnmpExtensionQueryEx = (PFNSNMPEXTENSIONQUERYEX)(uintptr_t) GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionQueryEx"); ext_dll_info->pfSnmpExtensionTrap = (PFNSNMPEXTENSIONTRAP)(uintptr_t) 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); DEBUGMSG(("winExtDLL", "init_winExtDLL: DLL %s initialization result %d\n", ext_dll_info->dll_name, result)); 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; DEBUGMSG(("winExtDLL", "init_winExtDLL: DLL %s view length %d\n", ext_dll_info->dll_name, view.idLength)); if (view.idLength) { /* * 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)); } else { 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)) { DEBUGMSG(("winExtDLL", "init_winExtDLL: DLL %s view length %d\n", ext_dll_info->dll_name, view.idLength)); 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): * * 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. * * * 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 . 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 . 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 */