| /** |
| * @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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <windows.h> |
| #include "../../win32/Snmp-winExtDLL.h" |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/library/snmp_assert.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| #include "util_funcs.h" |
| #include "winExtDLL.h" |
| |
| |
| #define MAX_VALUE_NAME 16383 |
| #define MS_ASN_UINTEGER32 MS_ASN_UNSIGNED32 |
| |
| |
| typedef BOOL(WINAPI * |
| PFNSNMPEXTENSIONINIT) (DWORD dwUpTimeReference, |
| HANDLE * phSubagentTrapEvent, |
| AsnObjectIdentifier * |
| pFirstSupportedRegion); |
| |
| typedef BOOL(WINAPI * |
| PFNSNMPEXTENSIONINITEX) (AsnObjectIdentifier * |
| pNextSupportedRegion); |
| |
| typedef BOOL(WINAPI * |
| PFNSNMPEXTENSIONMONITOR) (LPVOID pAgentMgmtData); |
| |
| typedef BOOL(WINAPI * PFNSNMPEXTENSIONQUERY) (BYTE bPduType, |
| SnmpVarBindList * |
| pVarBindList, |
| AsnInteger32 * |
| pErrorStatus, |
| AsnInteger32 * |
| pErrorIndex); |
| |
| typedef BOOL(WINAPI * PFNSNMPEXTENSIONQUERYEX) (UINT nRequestType, |
| UINT |
| nTransactionId, |
| SnmpVarBindList * |
| pVarBindList, |
| AsnOctetString * |
| pContextInfo, |
| AsnInteger32 * |
| pErrorStatus, |
| AsnInteger32 * |
| pErrorIndex); |
| |
| typedef BOOL(WINAPI * PFNSNMPEXTENSIONTRAP) (AsnObjectIdentifier * |
| pEnterpriseOid, |
| AsnInteger32 * |
| pGenericTrapId, |
| AsnInteger32 * |
| pSpecificTrapId, |
| AsnTimeticks * |
| pTimeStamp, |
| SnmpVarBindList * |
| pVarBindList); |
| |
| typedef VOID(WINAPI * PFNSNMPEXTENSIONCLOSE) (void); |
| |
| typedef BOOL (WINAPI *pfIsWow64Process)(HANDLE hProcess, BOOL *Wow64Process); |
| |
| |
| /** |
| * Extensible array, a data structure similar to the C++ STL class |
| * std::vector<>. |
| */ |
| typedef struct { |
| /** Pointer to the memory allocated for the array. */ |
| void *p; |
| /** Number of bytes occupied by a single element. */ |
| size_t elem_size; |
| /** Number of elements that have been allocated. */ |
| int reserved; |
| /** Number of elements currently in use. */ |
| int size; |
| } xarray; |
| |
| /** |
| * Information managed by winExtDLL about Windows SNMP extension DLL's. |
| */ |
| typedef struct { |
| char *dll_name; /**< Dynamically allocated DLL name. */ |
| HANDLE dll_handle; /**< DLL handle. */ |
| PFNSNMPEXTENSIONINIT pfSnmpExtensionInit; |
| PFNSNMPEXTENSIONINITEX pfSnmpExtensionInitEx; |
| PFNSNMPEXTENSIONCLOSE pfSnmpExtensionClose; |
| PFNSNMPEXTENSIONQUERY pfSnmpExtensionQuery; |
| PFNSNMPEXTENSIONQUERYEX pfSnmpExtensionQueryEx; |
| PFNSNMPEXTENSIONTRAP pfSnmpExtensionTrap; |
| HANDLE subagentTrapEvent; |
| } winextdll; |
| |
| /** |
| * Information managed by winExtDLL about a single view of a Windows SNMP |
| * extension DLL. |
| */ |
| typedef struct { |
| winextdll *winextdll_info; |
| netsnmp_handler_registration *my_handler; |
| oid name[MAX_OID_LEN]; /**< OID of this view. */ |
| size_t name_length; |
| } winextdll_view; |
| |
| /** |
| * Per varbind SNMP extension DLL context information for SNMP set operations. |
| */ |
| typedef struct context_info_s { |
| struct context_info_s *next; |
| int index; |
| AsnOctetString context_info; |
| } context_info; |
| |
| |
| /* |
| * External function declarations. |
| */ |
| void __declspec(dllimport) WINAPI SnmpSvcInitUptime(void); |
| |
| |
| /* |
| * Local functions declarations. |
| */ |
| static int basename_equals(const char *path, const char *basename); |
| static int register_netsnmp_handler(winextdll_view * |
| const ext_dll_view_info); |
| static void read_extension_dlls_from_registry(void); |
| static void read_extension_dlls_from_registry_at(const char *const subkey); |
| static char *read_extension_dll_path_from_registry(const TCHAR *); |
| static void subagentTrapCheck(unsigned int clientreg, void *clientarg); |
| static int var_winExtDLL(netsnmp_mib_handler *handler, |
| netsnmp_handler_registration *reginfo, |
| netsnmp_agent_request_info *reqinfo, |
| netsnmp_request_info *requests); |
| static int append_windows_varbind_list(netsnmp_variable_list ** |
| const net_snmp_varbinds, |
| const SnmpVarBindList * |
| const win_varbinds); |
| static int append_windows_varbind(netsnmp_variable_list ** |
| const net_snmp_varbinds, |
| const SnmpVarBind * |
| const win_varbind); |
| static int convert_to_windows_varbind_list(SnmpVarBindList * |
| pVarBindList, |
| netsnmp_variable_list * |
| netsnmp_varbinds); |
| static int convert_win_snmp_err(const int win_snmp_err); |
| static winextdll_view *lookup_view_by_oid(oid * const name, |
| const size_t name_len); |
| static int snmp_oid_compare_n_w(const oid * name1, size_t len1, |
| const UINT * name2, UINT len2); |
| static int snmp_oid_compare_w_n(const UINT * name1, UINT len1, |
| const oid * name2, size_t len2); |
| static int netsnmp_oid_is_subtree_n_w(const oid * name1, size_t len1, |
| const UINT * name2, UINT len2); |
| static void copy_oid(oid * const to_name, size_t * const to_name_len, |
| const oid * const from_name, |
| const size_t from_name_len); |
| static void copy_oid_n_w(oid * const to_name, size_t * const to_name_len, |
| const UINT * const from_name, |
| const UINT from_name_len); |
| static UINT *copy_oid_to_new_windows_oid(AsnObjectIdentifier * |
| const windows_oid, |
| const oid * const name, |
| const size_t name_len); |
| static int snmp_set_var_objid_w(netsnmp_variable_list * var, |
| const UINT * name, UINT name_length); |
| static netsnmp_variable_list * |
| snmp_varlist_add_variable_w(netsnmp_variable_list ** varlist, |
| const UINT * name, UINT name_length, |
| u_char type, const void * value, size_t len); |
| static void send_trap(const AsnObjectIdentifier * const, |
| const AsnInteger, const AsnInteger, |
| const AsnTimeticks, |
| const SnmpVarBindList * const); |
| static u_char *winsnmp_memdup(const void *src, const size_t len); |
| #if 0 |
| static void xarray_init(xarray * a, size_t elem_size); |
| #endif |
| static void xarray_destroy(xarray * a); |
| static void *xarray_push_back(xarray * a, const void *elem); |
| #if 0 |
| static void xarray_erase(xarray * a, void *const elem); |
| #endif |
| static void *xarray_reserve(xarray * a, int reserved); |
| |
| |
| /* |
| * Local variable definitions. |
| */ |
| #define WINEXTDLL(i) ((winextdll*)s_winextdll.p)[i] |
| #define WINEXTDLL_VIEW(i) ((winextdll_view*)s_winextdll_view.p)[i] |
| #define TRAPEVENT(i) ((HANDLE*)s_trapevent.p)[i] |
| #define TRAPEVENT_TO_DLLINFO(i) ((winextdll**)s_trapevent_to_dllinfo.p)[i] |
| static const oid mibii_system_mib[] = { 1, 3, 6, 1, 2, 1, 1 }; |
| static OSVERSIONINFO s_versioninfo = { sizeof(s_versioninfo) }; |
| static xarray s_winextdll = { 0, sizeof(winextdll) }; |
| static xarray s_winextdll_view = { 0, sizeof(winextdll_view) }; |
| static xarray s_trapevent = { 0, sizeof(HANDLE) }; |
| static xarray s_trapevent_to_dllinfo = { 0, sizeof(winextdll *) }; |
| static context_info *context_info_head; |
| |
| |
| /* |
| * Function definitions. |
| */ |
| |
| /** Initialize the winExtDLL extension agent. */ |
| void |
| init_winExtDLL(void) |
| { |
| BOOL result, is_wow64_process = FALSE; |
| int i; |
| uint32_t uptime_reference; |
| pfIsWow64Process IsWow64Process; |
| |
| DEBUGMSG(("winExtDLL", "init_winExtDLL started.\n")); |
| |
| GetVersionEx(&s_versioninfo); |
| |
| IsWow64Process = |
| (pfIsWow64Process)GetProcAddress(GetModuleHandle("kernel32"), |
| "IsWow64Process"); |
| if (IsWow64Process) |
| (*IsWow64Process)(GetCurrentProcess(), &is_wow64_process); |
| |
| SnmpSvcInitUptime(); |
| |
| read_extension_dlls_from_registry(); |
| |
| DEBUGMSG(("winExtDLL", |
| "init_winExtDLL: found %d extension DLLs in the registry.\n", |
| s_winextdll.size)); |
| |
| xarray_reserve(&s_winextdll, 128); |
| |
| /* |
| * Load all the DLLs |
| */ |
| for (i = 0; i < s_winextdll.size; i++) { |
| winextdll *const ext_dll_info = &WINEXTDLL(i); |
| AsnObjectIdentifier view; |
| winextdll_view ext_dll_view_info; |
| |
| netsnmp_assert(ext_dll_info); |
| if (!ext_dll_info->dll_name) |
| continue; |
| |
| DEBUGMSG(("winExtDLL", "loading DLL %s.\n", |
| ext_dll_info->dll_name)); |
| ext_dll_info->dll_handle = LoadLibrary(ext_dll_info->dll_name); |
| |
| if (ext_dll_info->dll_handle == NULL) { |
| const DWORD dwErrorcode = GetLastError(); |
| LPTSTR lpMsgBuf; |
| |
| FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrorcode, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPTSTR) & lpMsgBuf, 0, NULL); |
| if (lpMsgBuf) { |
| LPTSTR p; |
| |
| /* |
| * Remove trailing "\r\n". |
| */ |
| p = strchr(lpMsgBuf, '\r'); |
| if (p) |
| *p = '\0'; |
| } |
| snmp_log(LOG_ERR, |
| "init_winExtDLL: could not load SNMP extension" |
| " DLL %s: %s\n", |
| ext_dll_info->dll_name, lpMsgBuf ? lpMsgBuf : "(?)"); |
| if (lpMsgBuf) |
| LocalFree(lpMsgBuf); |
| continue; |
| } |
| |
| /* |
| * Store DLL name and functions in s_extension_dll_info array. |
| */ |
| ext_dll_info->pfSnmpExtensionInit = (PFNSNMPEXTENSIONINIT) |
| GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionInit"); |
| ext_dll_info->pfSnmpExtensionInitEx = (PFNSNMPEXTENSIONINITEX) |
| GetProcAddress(ext_dll_info->dll_handle, |
| "SnmpExtensionInitEx"); |
| ext_dll_info->pfSnmpExtensionClose = (PFNSNMPEXTENSIONCLOSE) |
| GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionClose"); |
| ext_dll_info->pfSnmpExtensionQuery = (PFNSNMPEXTENSIONQUERY) |
| GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionQuery"); |
| ext_dll_info->pfSnmpExtensionQueryEx = (PFNSNMPEXTENSIONQUERYEX) |
| GetProcAddress(ext_dll_info->dll_handle, |
| "SnmpExtensionQueryEx"); |
| ext_dll_info->pfSnmpExtensionTrap = (PFNSNMPEXTENSIONTRAP) |
| GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionTrap"); |
| |
| |
| if (ext_dll_info->pfSnmpExtensionQuery == NULL |
| && ext_dll_info->pfSnmpExtensionQueryEx == NULL) { |
| snmp_log(LOG_ERR, |
| "error in extension DLL %s: SNMP query function missing.\n", |
| ext_dll_info->dll_name); |
| } |
| |
| /* |
| * At least on a 64-bit Windows 7 system invoking SnmpExtensionInit() |
| * in the 32-bit version of evntagnt.dll hangs. Also, all queries in |
| * lmmib2.dll fail with "generic error" on a 64-bit Windows 7 system. |
| * So skip these two DLLs. |
| */ |
| if (s_versioninfo.dwMajorVersion >= 6 |
| && ((is_wow64_process |
| && basename_equals(ext_dll_info->dll_name, "evntagnt.dll")) |
| || basename_equals(ext_dll_info->dll_name, "lmmib2.dll"))) { |
| DEBUGMSG(("winExtDLL", "init_winExtDLL: skipped DLL %s.\n", |
| ext_dll_info->dll_name)); |
| continue; |
| } |
| |
| /* |
| * Init and get first supported view from Windows SNMP extension DLL. |
| * Note: although according to the documentation of SnmpExtensionInit() |
| * the first argument of this function should be ignored by extension |
| * DLLs, passing a correct value for this first argument is necessary |
| * to make inetmib1.dll work correctly. Passing zero as the first |
| * argument causes inetmib1.dll to report an incorrect value for |
| * sysUpTime.0 and also causes the same DLL not to send linkUp or |
| * linkDown traps. |
| */ |
| ext_dll_info->subagentTrapEvent = NULL; |
| view.idLength = 0; |
| view.ids = NULL; |
| if (!is_wow64_process && s_versioninfo.dwMajorVersion >= 6) |
| uptime_reference = GetTickCount() - 10 * SnmpSvcGetUptime(); |
| else |
| uptime_reference = GetTickCount() / 10; |
| result = |
| ext_dll_info->pfSnmpExtensionInit(uptime_reference, |
| &ext_dll_info-> |
| subagentTrapEvent, &view); |
| |
| if (!result) { |
| DEBUGMSG(("winExtDLL", |
| "init_winExtDLL: initialization of DLL %s failed.\n", |
| ext_dll_info->dll_name)); |
| /* |
| * At least on Windows 7 SnmpExtensionInit() in some extension |
| * agent DLLs returns "FALSE" although initialization |
| * succeeded. Hence ignore the SnmpExtensionInit() return value on |
| * Windows Vista and later. |
| */ |
| if (s_versioninfo.dwMajorVersion < 6) { |
| snmp_log(LOG_ERR, |
| "init_winExtDLL: initialization of DLL %s failed.\n", |
| ext_dll_info->dll_name); |
| FreeLibrary(ext_dll_info->dll_handle); |
| ext_dll_info->dll_handle = 0; |
| continue; |
| } |
| } |
| |
| if (ext_dll_info->subagentTrapEvent != NULL) { |
| xarray_push_back(&s_trapevent, |
| &ext_dll_info->subagentTrapEvent); |
| xarray_push_back(&s_trapevent_to_dllinfo, &ext_dll_info); |
| } |
| |
| memset(&ext_dll_view_info, 0, sizeof(ext_dll_view_info)); |
| ext_dll_view_info.winextdll_info = ext_dll_info; |
| if (view.idLength == 0) { |
| DEBUGMSG(("winExtDLL", |
| "init_winExtDLL: DLL %s did not register an OID range.\n", |
| ext_dll_info->dll_name)); |
| continue; |
| } |
| /* |
| * Skip the mib-2 system section on Windows Vista and later because |
| * at least on a 64-bit Windows 7 system all queries in that section |
| * fail with status "generic error". |
| */ |
| if (s_versioninfo.dwMajorVersion >= 6 |
| && snmp_oid_compare_w_n(view.ids, view.idLength, mibii_system_mib, |
| sizeof(mibii_system_mib) / |
| sizeof(mibii_system_mib[0])) == 0) { |
| DEBUGMSG(("winExtDLL", |
| "init_winExtDLL: skipping system section of DLL %s.\n", |
| ext_dll_info->dll_name)); |
| continue; |
| } |
| copy_oid_n_w(ext_dll_view_info.name, &ext_dll_view_info.name_length, |
| view.ids, view.idLength); |
| xarray_push_back(&s_winextdll_view, &ext_dll_view_info); |
| |
| /* |
| * Loop looking for more supported views. |
| */ |
| while (ext_dll_info->pfSnmpExtensionInitEx |
| && ext_dll_info->pfSnmpExtensionInitEx(&view)) { |
| memset(&ext_dll_view_info, 0, sizeof(ext_dll_view_info)); |
| ext_dll_view_info.winextdll_info = ext_dll_info; |
| copy_oid_n_w(ext_dll_view_info.name, |
| &ext_dll_view_info.name_length, view.ids, |
| view.idLength); |
| xarray_push_back(&s_winextdll_view, &ext_dll_view_info); |
| } |
| } |
| |
| /* |
| * Note: since register_netsnmp_handler() writes a pointer to the |
| * winextdll_view in one of the Net-SNMP data structures, it is not |
| * allowed to move winextdll_view data structures in memory after |
| * registration with Net-SNMP. Or: register_snmp_handler() must be called |
| * only once it is sure that the size of array s_winextdll_view won't change |
| * anymore. |
| */ |
| for (i = 0; i < s_winextdll_view.size; i++) |
| register_netsnmp_handler(&WINEXTDLL_VIEW(i)); |
| |
| DEBUGMSG(("winExtDLL", |
| "init_winExtDLL: registered %d OID ranges.\n", |
| s_winextdll_view.size)); |
| |
| /* |
| * Let Net-SNMP call subagentTrapCheck() once per second. |
| */ |
| if (s_trapevent.size) |
| snmp_alarm_register(1, SA_REPEAT, subagentTrapCheck, NULL); |
| |
| DEBUGMSG(("winExtDLL", "init_winExtDLL finished.\n")); |
| } |
| |
| void |
| shutdown_winExtDLL(void) |
| { |
| int i; |
| |
| DEBUGMSG(("winExtDLL", "shutdown_winExtDLL() started.\n")); |
| |
| for (i = s_winextdll_view.size - 1; i >= 0; i--) { |
| winextdll_view *const v = &WINEXTDLL_VIEW(i); |
| if (v && v->my_handler) { |
| DEBUGIF("winExtDLL") { |
| DEBUGMSG(("winExtDLL", |
| "unregistering handler for DLL %s and OID prefix ", |
| v->winextdll_info->dll_name)); |
| DEBUGMSGOID(("winExtDLL", v->name, v->name_length)); |
| DEBUGMSG(("winExtDLL", " (")); |
| DEBUGMSGSUBOID(("winExtDLL", v->name, v->name_length)); |
| DEBUGMSG(("winExtDLL", ").\n")); |
| } |
| netsnmp_unregister_handler(v->my_handler); |
| } |
| } |
| xarray_destroy(&s_winextdll_view); |
| |
| for (i = s_winextdll.size - 1; i >= 0; i--) { |
| winextdll *const ext_dll_info = &WINEXTDLL(i); |
| if (ext_dll_info->dll_handle) { |
| if (ext_dll_info->pfSnmpExtensionClose) { |
| DEBUGMSG(("winExtDLL", "closing %s.\n", |
| ext_dll_info->dll_name)); |
| ext_dll_info->pfSnmpExtensionClose(); |
| } |
| /* |
| * Freeing the Broadcom SNMP extension libraries triggers |
| * a deadlock, so skip bcmif.dll and baspmgnt.dll. |
| */ |
| if (!basename_equals(ext_dll_info->dll_name, "bcmif.dll") |
| && !basename_equals(ext_dll_info->dll_name, "baspmgnt.dll")) { |
| DEBUGMSG(("winExtDLL", "unloading %s.\n", |
| ext_dll_info->dll_name)); |
| FreeLibrary(ext_dll_info->dll_handle); |
| } |
| } |
| free(ext_dll_info->dll_name); |
| } |
| xarray_destroy(&s_winextdll); |
| |
| xarray_destroy(&s_trapevent_to_dllinfo); |
| |
| xarray_destroy(&s_trapevent); |
| |
| DEBUGMSG(("winExtDLL", "shutdown_winExtDLL() finished.\n")); |
| } |
| |
| /** |
| * Compare the basename of a path with a given string. |
| * |
| * @return 1 if the basename matches, 0 if not. |
| */ |
| static int |
| basename_equals(const char *path, const char *basename) |
| { |
| const size_t path_len = strlen(path); |
| const size_t basename_len = strlen(basename); |
| |
| netsnmp_assert(strchr(path, '/') == 0); |
| netsnmp_assert(strchr(basename, '/') == 0); |
| netsnmp_assert(strchr(basename, '\\') == 0); |
| |
| return path_len >= basename_len + 1 |
| && path[path_len - basename_len - 1] == '\\' |
| && 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) { |
| 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; |
| } |
| |
| 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; |
| |
| 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; |
| } |
| |
| switch (reqinfo->mode) { |
| case MODE_GET: |
| mode_name = "GET"; |
| nRequestType = SNMP_EXTENSION_GET; |
| netsnmp_assert(!context_info_head); |
| break; |
| case MODE_GETNEXT: |
| mode_name = "GETNEXT"; |
| nRequestType = SNMP_EXTENSION_GET_NEXT; |
| netsnmp_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)); |
| netsnmp_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; |
| netsnmp_assert(varbind); |
| |
| /* |
| * Convert the Net-SNMP varbind to a Windows SNMP varbind list. |
| */ |
| rc = convert_to_windows_varbind_list(&win_varbinds, varbind); |
| if (rc != SNMP_ERR_NOERROR) { |
| DEBUGMSG(("winExtDLL", |
| "converting varbind list to Windows format failed with" |
| " error code %d.\n", request->status)); |
| netsnmp_request_set_error(requests, rc); |
| goto free_win_varbinds; |
| } |
| |
| netsnmp_assert(win_varbinds.len == 1); |
| |
| /* |
| * For a GetNext PDU, if the varbind OID comes lexicographically |
| * before the root OID of this handler, replace it by the root OID. |
| */ |
| if (reqinfo->mode == MODE_GETNEXT |
| && snmp_oid_compare_w_n(win_varbinds.list[0].name.ids, |
| win_varbinds.list[0].name.idLength, |
| reginfo->rootoid, |
| reginfo->rootoid_len) < 0) { |
| DEBUGIF("winExtDLL") { |
| size_t oid1_namelen = 0, oid2_namelen = 0, outlen1 = 0, |
| outlen2 = 0; |
| char *oid1_name = NULL, *oid2_name = NULL; |
| int overflow1 = 0, overflow2 = 0; |
| |
| netsnmp_static_assert(sizeof(oid) == sizeof(UINT)); |
| netsnmp_sprint_realloc_objid((u_char **) & oid1_name, |
| &oid1_namelen, &outlen1, 1, |
| &overflow1, (const oid *) |
| win_varbinds.list[0].name.ids, |
| win_varbinds.list[0].name.idLength); |
| netsnmp_sprint_realloc_objid((u_char **) & oid2_name, |
| &oid2_namelen, &outlen2, 1, |
| &overflow2, reginfo->rootoid, |
| reginfo->rootoid_len); |
| DEBUGMSG(("winExtDLL", |
| "extension DLL %s: replacing OID %s%s by OID %s%s.\n", |
| ext_dll_info->dll_name, |
| oid1_name, overflow1 ? " [TRUNCATED]" : "", |
| oid2_name, overflow2 ? " [TRUNCATED]" : "")); |
| free(oid2_name); |
| free(oid1_name); |
| } |
| |
| SnmpUtilOidFree(&win_varbinds.list[0].name); |
| memset(&win_varbinds.list[0].name, 0, |
| sizeof(win_varbinds.list[0].name)); |
| copy_oid_to_new_windows_oid(&win_varbinds.list[0].name, |
| reginfo->rootoid, |
| reginfo->rootoid_len); |
| } |
| |
| if (ext_dll_info->pfSnmpExtensionQueryEx) { |
| result = ext_dll_info->pfSnmpExtensionQueryEx(nRequestType, |
| 1, |
| &win_varbinds, |
| get_context_info(request->index), |
| &ErrorStatus, |
| &ErrorIndex); |
| } else if (ext_dll_info->pfSnmpExtensionQuery) { |
| result = |
| ext_dll_info->pfSnmpExtensionQuery((BYTE) nRequestType, |
| &win_varbinds, |
| &ErrorStatus, |
| &ErrorIndex); |
| } else { |
| snmp_log(LOG_ERR, |
| "error in extension DLL %s: SNMP query function missing.\n", |
| ext_dll_info->dll_name); |
| result = FALSE; |
| } |
| |
| if (!result) { |
| snmp_log(LOG_ERR, |
| "extension DLL %s: SNMP query function failed.\n", |
| ext_dll_info->dll_name); |
| rc = SNMP_ERR_GENERR; |
| goto free_win_varbinds; |
| } |
| |
| rc = convert_win_snmp_err(ErrorStatus); |
| if (rc != SNMP_ERR_NOERROR) { |
| DEBUGIF("winExtDLL") { |
| size_t oid_namelen = 0, outlen = 0; |
| char *oid_name = NULL; |
| int overflow = 0; |
| |
| netsnmp_sprint_realloc_objid((u_char **) & oid_name, |
| &oid_namelen, |
| &outlen, 1, &overflow, |
| ext_dll_view_info->name, |
| ext_dll_view_info->name_length); |
| DEBUGMSG(("winExtDLL", "extension DLL %s: SNMP query function" |
| " returned error code %lu (Windows) / %d (Net-SNMP)" |
| " for request type %d, OID %s%s, ASN type %d and" |
| " value %ld.\n", |
| ext_dll_info->dll_name, ErrorStatus, rc, nRequestType, |
| oid_name, overflow ? " [TRUNCATED]" : "", |
| win_varbinds.list[0].value.asnType, |
| win_varbinds.list[0].value.asnValue.number)); |
| free(oid_name); |
| } |
| netsnmp_assert(ErrorIndex == 1); |
| netsnmp_request_set_error(requests, rc); |
| if (rc == SNMP_NOSUCHOBJECT || rc == SNMP_NOSUCHINSTANCE |
| || rc == SNMP_ERR_NOSUCHNAME) |
| rc = SNMP_ERR_NOERROR; |
| goto free_win_varbinds; |
| } |
| |
| copy_value = FALSE; |
| if (reqinfo->mode == MODE_GET) |
| copy_value = TRUE; |
| else if (reqinfo->mode == MODE_GETNEXT) { |
| const SnmpVarBind *win_varbind; |
| |
| win_varbind = &win_varbinds.list[0]; |
| |
| /* |
| * Verify whether the OID returned by the extension DLL fits |
| * inside the OID range this handler has been registered |
| * with. Also compare the OID passed to the extension DLL with |
| * the OID returned by the same DLL. If the DLL returned a |
| * lexicographically earlier OID, this means that there is no |
| * next OID in the MIB implemented by the DLL. |
| * |
| * Note: for some GetNext requests BoundsChecker will report |
| * that the code below accesses a dangling pointer. This is |
| * a limitation of BoundsChecker: apparently BoundsChecker is |
| * not able to cope with reallocation of memory for |
| * win_varbind by an SNMP extension DLL that has not been |
| * instrumented by BoundsChecker. |
| */ |
| if (netsnmp_oid_is_subtree_n_w(ext_dll_view_info->name, |
| ext_dll_view_info->name_length, |
| win_varbind->name.ids, |
| win_varbind->name.idLength) == 0 |
| && snmp_oid_compare_n_w(varbind->name, varbind->name_length, |
| win_varbind->name.ids, |
| win_varbind->name.idLength) < 0) { |
| /* |
| * Copy the OID returned by the extension DLL to the |
| * Net-SNMP varbind. |
| */ |
| snmp_set_var_objid_w(varbind, |
| win_varbind->name.ids, |
| win_varbind->name.idLength); |
| copy_value = TRUE; |
| } |
| } |
| if (copy_value) { |
| netsnmp_variable_list *result_vb; |
| |
| /* |
| * Copy the value returned by the extension DLL to the Net-SNMP |
| * varbind. |
| */ |
| result_vb = NULL; |
| rc = append_windows_varbind(&result_vb, &win_varbinds.list[0]); |
| netsnmp_assert(result_vb || rc != SNMP_ERR_NOERROR); |
| if (result_vb) { |
| snmp_set_var_typed_value(varbind, |
| result_vb->type, |
| result_vb->val.string, |
| result_vb->val_len); |
| snmp_free_varbind(result_vb); |
| } else { |
| netsnmp_request_set_error(requests, rc); |
| goto free_win_varbinds; |
| } |
| } |
| |
| free_win_varbinds: |
| if (reqinfo->mode == MODE_SET_COMMIT |
| || reqinfo->mode == MODE_SET_UNDO |
| || reqinfo->mode == MODE_SET_FREE) |
| free_context_info(request->index); |
| if (win_varbinds.list) |
| SnmpUtilVarBindListFree(&win_varbinds); |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * Iterate over the SNMP extension DLL information in the registry and store |
| * the retrieved information in s_winextdll[]. |
| * |
| * At the time an SNMP extension DLL is installed, some information about the |
| * DLL is written to the registry at one of the two following locations: |
| * HKLM\SYSTEM\CurrentControlSet\Control\SNMP\Parameters\ExtensionAgents for |
| * Windows Vista, Windows 7 and Windows 2008 or |
| * HKLM\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ExtensionAgents for |
| * earlier Windows versions. Under this key zero or more REG_SZ values are |
| * stored with the names of registry keys containing the DLL path. |
| */ |
| void |
| read_extension_dlls_from_registry() |
| { |
| DEBUGMSGTL(("winExtDLL", |
| "read_extension_dlls_from_registry called\n")); |
| |
| read_extension_dlls_from_registry_at |
| ("SYSTEM\\CurrentControlSet\\Services\\SNMP\\Parameters\\ExtensionAgents"); |
| read_extension_dlls_from_registry_at |
| ("SYSTEM\\CurrentControlSet\\Control\\SNMP\\Parameters\\ExtensionAgents"); |
| } |
| |
| void |
| read_extension_dlls_from_registry_at(const char *const subkey) |
| { |
| DWORD retCode; |
| HKEY hKey; |
| int i; |
| DWORD valueSize; |
| TCHAR valueName[MAX_VALUE_NAME]; |
| DWORD dataType; |
| TCHAR data[MAX_VALUE_NAME]; |
| DWORD dataSize; |
| |
| retCode = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, |
| 0, KEY_QUERY_VALUE, &hKey); |
| |
| if (retCode == ERROR_SUCCESS) { |
| for (i = 0; ; i++) { |
| valueSize = sizeof(valueName); |
| dataSize = sizeof(data); |
| retCode = RegEnumValue(hKey, i, valueName, &valueSize, NULL, |
| &dataType, (BYTE *) data, &dataSize); |
| |
| if (retCode != ERROR_SUCCESS) |
| break; |
| if (dataType == REG_SZ) { |
| winextdll ext_dll_info; |
| |
| memset(&ext_dll_info, 0, sizeof(ext_dll_info)); |
| ext_dll_info.dll_name = |
| read_extension_dll_path_from_registry(data); |
| if (ext_dll_info.dll_name) { |
| xarray_push_back(&s_winextdll, &ext_dll_info); |
| DEBUGMSG(("winExtDLL", "registry key %s: DLL %s.\n", |
| data, ext_dll_info.dll_name)); |
| } |
| } |
| } |
| RegCloseKey(hKey); |
| } |
| } |
| |
| /** Store the DLL path in dynamically allocated memory. */ |
| char * |
| read_extension_dll_path_from_registry(const TCHAR * keyName) |
| { |
| HKEY hKey; |
| DWORD key_value_type = 0; |
| TCHAR valueName[MAX_VALUE_NAME]; |
| DWORD key_value_size = MAX_VALUE_NAME; |
| TCHAR valueNameExpanded[MAX_VALUE_NAME]; |
| DWORD retCode; |
| char *result = 0; |
| |
| retCode = RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
| keyName, 0, KEY_QUERY_VALUE, &hKey); |
| |
| if (retCode != ERROR_SUCCESS) |
| return 0; |
| |
| retCode = RegQueryValueExA(hKey, |
| "Pathname", |
| NULL, |
| &key_value_type, |
| (BYTE *) valueName, &key_value_size); |
| |
| if (retCode != ERROR_SUCCESS) { |
| RegCloseKey(hKey); |
| return 0; |
| } |
| |
| if (key_value_type == REG_EXPAND_SZ) { |
| if (ExpandEnvironmentStrings |
| (valueName, valueNameExpanded, MAX_VALUE_NAME)) |
| result = strdup(valueNameExpanded); |
| } else if (key_value_type == REG_SZ) |
| result = strdup(valueName); |
| |
| RegCloseKey(hKey); |
| return result; |
| } |
| |
| /** |
| * Callback function called by the Net-SNMP agent to check for traps waiting |
| * to be processed. |
| */ |
| void |
| subagentTrapCheck(unsigned int clientreg, void *clientarg) |
| { |
| while (1) { |
| DWORD dwWaitResult; |
| BOOL bResult; |
| int i; |
| int j; |
| const winextdll *ext_dll_info; |
| |
| if (s_trapevent.size == 0) |
| return; |
| |
| dwWaitResult = WaitForMultipleObjects(s_trapevent.size, |
| &TRAPEVENT(0), FALSE, 0); |
| |
| i = dwWaitResult - WAIT_OBJECT_0; |
| if (i < 0 || i >= s_trapevent.size) { |
| netsnmp_assert(dwWaitResult == WAIT_TIMEOUT); |
| return; |
| } |
| |
| netsnmp_assert(s_trapevent.size == s_trapevent_to_dllinfo.size); |
| ext_dll_info = TRAPEVENT_TO_DLLINFO(i); |
| netsnmp_assert(ext_dll_info->subagentTrapEvent == TRAPEVENT(i)); |
| |
| /* |
| * Reset the signalled event just in case the extension DLL erroneously |
| * allocated a manual-reset event instead of an auto-reset event. It is |
| * important to reset the event BEFORE traps are processed, otherwise a |
| * race condition is triggered between the extension DLL setting the |
| * event and this code resetting the event. |
| */ |
| ResetEvent(TRAPEVENT(i)); |
| |
| if (!ext_dll_info->pfSnmpExtensionTrap) { |
| snmp_log(LOG_ERR, |
| "internal error in SNMP extension DLL %s: a trap is ready" |
| " but the function SnmpExtensionTrap() is missing.\n", |
| ext_dll_info->dll_name); |
| return; |
| } |
| |
| /* |
| * Process at most hundred traps per extension DLL. If the extension DLL |
| * has more traps waiting, that's probably a bug in the extension DLL. |
| */ |
| for (j = 0; j < 100; j++) { |
| AsnObjectIdentifier Enterprise = { 0, NULL }; |
| AsnInteger GenericTrap = 0; |
| AsnInteger SpecificTrap = 0; |
| AsnTimeticks TimeStamp = 0; |
| SnmpVarBindList TrapVarbinds = { NULL, 0 }; |
| |
| bResult = ext_dll_info->pfSnmpExtensionTrap(&Enterprise, |
| &GenericTrap, |
| &SpecificTrap, |
| &TimeStamp, |
| &TrapVarbinds); |
| |
| if (!bResult) |
| break; |
| |
| send_trap(&Enterprise, GenericTrap, SpecificTrap, TimeStamp, |
| &TrapVarbinds); |
| |
| SnmpUtilVarBindListFree(&TrapVarbinds); |
| } |
| } |
| } |
| |
| void |
| send_trap(const AsnObjectIdentifier * const pEnterprise, |
| const AsnInteger GenericTrap, |
| const AsnInteger SpecificTrap, |
| const AsnTimeticks TimeStamp, |
| const SnmpVarBindList * const pTrapVarbinds) |
| { |
| /* |
| * A quote from the paragraph in RFC 1908 about SNMPv1 to SNMPv2c |
| * trap translation (http://www.ietf.org/rfc/rfc1908.txt): |
| * <quote> |
| * If a Trap-PDU is received, then it is mapped into a SNMPv2-Trap- |
| * PDU. This is done by prepending onto the variable-bindings field |
| * two new bindings: sysUpTime.0 [6], which takes its value from the |
| * timestamp field of the Trap-PDU; and, snmpTrapOID.0 [6], which is |
| * calculated thusly: if the value of generic-trap field is |
| * `enterpriseSpecific', then the value used is the concatenation of |
| * the enterprise field from the Trap-PDU with two additional sub- |
| * identifiers, `0', and the value of the specific-trap field; |
| * otherwise, the value of the corresponding trap defined in [6] is |
| * used. |
| * </quote> |
| * |
| * Reference [6] refers to RFC 1907 (http://www.ietf.org/rfc/rfc1907.txt), |
| * where the generic trap OIDs have been defined as follows: |
| * coldStart ::= { snmpTraps 1 } |
| * warmStart ::= { snmpTraps 2 } |
| * linkDown ::= { snmpTraps 3 } |
| * linkUp ::= { snmpTraps 4 } |
| * authenticationFailure ::= { snmpTraps 5 } |
| * egpNeighborLoss ::= { snmpTraps 6 } |
| */ |
| static const oid sysuptime_oid[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 }; |
| static const size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid); |
| |
| static const oid snmptrap_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; |
| static const size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid); |
| |
| static const oid snmptraps_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 }; |
| static const size_t snmptraps_oid_len = OID_LENGTH(snmptraps_oid); |
| |
| oid vb2_oid[MAX_OID_LEN]; |
| size_t vb2_oid_len; |
| |
| netsnmp_variable_list *notification_vars = NULL; |
| |
| |
| /* |
| * Append the varbind (sysUpTime.0, TimeStamp). |
| */ |
| snmp_varlist_add_variable(¬ification_vars, |
| sysuptime_oid, sysuptime_oid_len, |
| ASN_TIMETICKS, |
| (const u_char *) &TimeStamp, |
| sizeof(TimeStamp)); |
| |
| if (GenericTrap == SNMP_GENERICTRAP_ENTERSPECIFIC) { |
| /* |
| * Enterprise specific trap: compute the OID |
| * *pEnterprise + ".0." + SpecificTrap. |
| */ |
| copy_oid_n_w(vb2_oid, &vb2_oid_len, |
| pEnterprise->ids, pEnterprise->idLength); |
| vb2_oid[vb2_oid_len++] = 0; |
| vb2_oid[vb2_oid_len++] = SpecificTrap; |
| } else { |
| /* |
| * Generic trap: compute the OID snmpTraps + "." + GenericTrap. |
| * Since the GenericTrap values are those defined in SNMPv1, since |
| * these values start at zero, and since the corresponding values in |
| * SNMPv2 start at one, translate the GenericTrap value accordingly. |
| * See also http://www.ietf.org/rfc/rfc1214.txt and |
| * http://www.ietf.org/rfc/rfc3418.txt. |
| */ |
| copy_oid(vb2_oid, &vb2_oid_len, snmptraps_oid, snmptraps_oid_len); |
| vb2_oid[vb2_oid_len++] = GenericTrap + 1; |
| } |
| |
| /* |
| * Append the varbind (snmpTrap, vb2_oid). |
| */ |
| snmp_varlist_add_variable(¬ification_vars, |
| snmptrap_oid, snmptrap_oid_len, |
| ASN_OBJECT_ID, |
| (u_char *) vb2_oid, |
| vb2_oid_len * sizeof(vb2_oid[0])); |
| |
| /* |
| * Append all the varbinds in pTrapVarbinds. |
| */ |
| append_windows_varbind_list(¬ification_vars, pTrapVarbinds); |
| |
| /* |
| * Send trap. |
| */ |
| send_v2trap(notification_vars); |
| |
| /* |
| * Free the memory allocated for notification_vars. |
| */ |
| snmp_free_varbind(notification_vars); |
| } |
| |
| /** |
| * Convert a Windows varbind to a Net-SNMP varbind and add it to the list of |
| * varbinds 'net_snmp_varbinds'. |
| * |
| * @note The memory allocated inside this function must be freed by the caller |
| * as follows: snmp_free_varbind(*net_snmp_varbinds). |
| */ |
| static int |
| append_windows_varbind_list(netsnmp_variable_list ** |
| const net_snmp_varbinds, |
| const SnmpVarBindList * const win_varbinds) |
| { |
| int i, status = SNMP_ERR_NOERROR; |
| |
| for (i = 0; i < win_varbinds->len; i++) { |
| status = |
| append_windows_varbind(net_snmp_varbinds, |
| &win_varbinds->list[i]); |
| if (status != SNMP_ERR_NOERROR) |
| break; |
| } |
| return status; |
| } |
| |
| static int |
| append_windows_varbind(netsnmp_variable_list ** const net_snmp_varbinds, |
| const SnmpVarBind * const win_varbind) |
| { |
| switch (win_varbind->value.asnType) { |
| case MS_ASN_INTEGER: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_INTEGER, |
| &win_varbind->value.asnValue.number, |
| sizeof(win_varbind->value.asnValue. |
| number)); |
| break; |
| case MS_ASN_BITS: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_BIT_STR, |
| win_varbind->value.asnValue.bits.stream, |
| win_varbind->value.asnValue.bits.length); |
| break; |
| case MS_ASN_OCTETSTRING: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_OCTET_STR, |
| win_varbind->value.asnValue.string. |
| stream, |
| win_varbind->value.asnValue.string. |
| length); |
| break; |
| case MS_ASN_NULL: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_NULL, 0, 0); |
| break; |
| case MS_ASN_OBJECTIDENTIFIER: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_OBJECT_ID, |
| win_varbind->value.asnValue. |
| object.ids, |
| win_varbind->value.asnValue.object. |
| idLength * sizeof(oid)); |
| break; |
| |
| /* |
| * MS_ASN_INTEGER32: synonym for MS_ASN_INTEGER. |
| */ |
| |
| case MS_ASN_SEQUENCE: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_SEQUENCE, |
| win_varbind->value.asnValue.sequence. |
| stream, |
| win_varbind->value.asnValue.sequence. |
| length); |
| break; |
| case MS_ASN_IPADDRESS: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_IPADDRESS, |
| win_varbind->value.asnValue.address. |
| stream, |
| win_varbind->value.asnValue.address. |
| length); |
| break; |
| case MS_ASN_COUNTER32: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_COUNTER, |
| &win_varbind->value.asnValue.counter, |
| sizeof(win_varbind->value.asnValue. |
| counter)); |
| break; |
| case MS_ASN_GAUGE32: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_GAUGE, |
| &win_varbind->value.asnValue.gauge, |
| sizeof(win_varbind->value.asnValue. |
| gauge)); |
| break; |
| case MS_ASN_TIMETICKS: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_TIMETICKS, |
| &win_varbind->value.asnValue.ticks, |
| sizeof(win_varbind->value.asnValue. |
| ticks)); |
| break; |
| case MS_ASN_OPAQUE: // AsnOctetString |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_OPAQUE, |
| win_varbind->value.asnValue.arbitrary. |
| stream, |
| win_varbind->value.asnValue.arbitrary. |
| length); |
| break; |
| case MS_ASN_COUNTER64: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_COUNTER64, |
| &win_varbind->value.asnValue.counter64, |
| sizeof(win_varbind->value.asnValue. |
| counter64)); |
| break; |
| case MS_ASN_UINTEGER32: |
| snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids, |
| win_varbind->name.idLength, |
| ASN_UNSIGNED, |
| &win_varbind->value.asnValue.unsigned32, |
| sizeof(win_varbind->value.asnValue. |
| unsigned32)); |
| break; |
| default: |
| return SNMP_ERR_GENERR; |
| } |
| |
| return SNMP_ERR_NOERROR; |
| } |
| |
| static int |
| snmp_set_var_objid_w(netsnmp_variable_list * var, const UINT * name, |
| UINT name_length) |
| { |
| netsnmp_static_assert(sizeof(oid) == sizeof(UINT)); |
| return snmp_set_var_objid(var, (const oid *) name, name_length); |
| } |
| |
| static netsnmp_variable_list * |
| snmp_varlist_add_variable_w(netsnmp_variable_list ** varlist, const UINT * name, |
| UINT name_length, u_char type, const void * value, |
| size_t len) |
| { |
| netsnmp_static_assert(sizeof(oid) == sizeof(UINT)); |
| return snmp_varlist_add_variable(varlist, (const oid *) name, name_length, type, |
| value, len); |
| } |
| |
| /** |
| * Convert a Net-SNMP varbind to a WinSNMP varbind list. |
| * |
| * @param[out] pVarBindList WinSNMP varbind list, initialized by this |
| * function. |
| * @param[in] varbind Net-SNMP varbind. |
| */ |
| int |
| convert_to_windows_varbind_list(SnmpVarBindList * pVarBindList, |
| netsnmp_variable_list * varbind) |
| { |
| SnmpVarBind *win_varbind; |
| |
| netsnmp_assert(pVarBindList); |
| netsnmp_assert(varbind); |
| |
| pVarBindList->len = 1; |
| pVarBindList->list |
| = (SnmpVarBind *) SnmpUtilMemAlloc(pVarBindList->len |
| * |
| sizeof(pVarBindList->list[0])); |
| if (pVarBindList->list == 0) |
| goto generr; |
| |
| memset(&pVarBindList->list[0], 0, sizeof(pVarBindList->list[0])); |
| |
| win_varbind = &pVarBindList->list[0]; |
| |
| if (varbind->name |
| && !copy_oid_to_new_windows_oid(&win_varbind->name, |
| varbind->name, |
| varbind->name_length)) |
| goto generr; |
| |
| switch (varbind->type) { |
| case ASN_BOOLEAN: |
| // There is no equivalent type in Microsoft's <snmp.h>. |
| netsnmp_assert(0); |
| win_varbind->value.asnType = MS_ASN_INTEGER; |
| win_varbind->value.asnValue.number = *(varbind->val.integer); |
| break; |
| case ASN_INTEGER: |
| win_varbind->value.asnType = MS_ASN_INTEGER; |
| win_varbind->value.asnValue.number = *(varbind->val.integer); |
| break; |
| case ASN_BIT_STR: |
| win_varbind->value.asnType = MS_ASN_BITS; |
| win_varbind->value.asnValue.string.stream |
| = winsnmp_memdup(varbind->val.string, varbind->val_len); |
| win_varbind->value.asnValue.string.length = |
| (UINT) (varbind->val_len); |
| win_varbind->value.asnValue.string.dynamic = TRUE; |
| break; |
| case ASN_OCTET_STR: |
| win_varbind->value.asnType = MS_ASN_OCTETSTRING; |
| win_varbind->value.asnValue.string.stream |
| = winsnmp_memdup(varbind->val.string, varbind->val_len); |
| win_varbind->value.asnValue.string.length = |
| (UINT) (varbind->val_len); |
| win_varbind->value.asnValue.string.dynamic = TRUE; |
| break; |
| case ASN_NULL: |
| win_varbind->value.asnType = MS_ASN_NULL; |
| memset(&win_varbind->value, 0, sizeof(win_varbind->value)); |
| break; |
| case ASN_OBJECT_ID: |
| win_varbind->value.asnType = MS_ASN_OBJECTIDENTIFIER; |
| if (!copy_oid_to_new_windows_oid |
| (&win_varbind->value.asnValue.object, varbind->val.objid, |
| varbind->val_len / sizeof(varbind->val.objid[0]))) |
| return SNMP_ERR_GENERR; |
| break; |
| case ASN_SEQUENCE: |
| win_varbind->value.asnType = MS_ASN_SEQUENCE; |
| win_varbind->value.asnValue.string.stream |
| = winsnmp_memdup(varbind->val.string, varbind->val_len); |
| win_varbind->value.asnValue.string.length = |
| (UINT) (varbind->val_len); |
| win_varbind->value.asnValue.string.dynamic = TRUE; |
| break; |
| case ASN_SET: |
| // There is no equivalent type in Microsoft's <snmp.h>. |
| netsnmp_assert(0); |
| win_varbind->value.asnType = MS_ASN_INTEGER; |
| win_varbind->value.asnValue.number = *(varbind->val.integer); |
| break; |
| case ASN_IPADDRESS: |
| win_varbind->value.asnType = MS_ASN_IPADDRESS; |
| win_varbind->value.asnValue.string.stream |
| = winsnmp_memdup(varbind->val.string, varbind->val_len); |
| win_varbind->value.asnValue.string.length = |
| (UINT) (varbind->val_len); |
| win_varbind->value.asnValue.string.dynamic = TRUE; |
| break; |
| case ASN_COUNTER: |
| win_varbind->value.asnType = MS_ASN_COUNTER32; |
| win_varbind->value.asnValue.counter = *(varbind->val.integer); |
| break; |
| /* |
| * ASN_GAUGE == ASN_UNSIGNED |
| */ |
| case ASN_UNSIGNED: |
| win_varbind->value.asnType = MS_ASN_UNSIGNED32; |
| win_varbind->value.asnValue.unsigned32 = *(varbind->val.integer); |
| break; |
| case ASN_TIMETICKS: |
| win_varbind->value.asnType = MS_ASN_TIMETICKS; |
| win_varbind->value.asnValue.ticks = *(varbind->val.integer); |
| break; |
| case ASN_OPAQUE: |
| win_varbind->value.asnType = MS_ASN_OPAQUE; |
| win_varbind->value.asnValue.string.stream |
| = winsnmp_memdup(varbind->val.string, varbind->val_len); |
| win_varbind->value.asnValue.string.length = |
| (UINT) (varbind->val_len); |
| win_varbind->value.asnValue.string.dynamic = TRUE; |
| break; |
| case ASN_COUNTER64: |
| win_varbind->value.asnType = MS_ASN_COUNTER64; |
| win_varbind->value.asnValue.counter64.HighPart |
| = varbind->val.counter64->high; |
| win_varbind->value.asnValue.counter64.LowPart |
| = varbind->val.counter64->low; |
| break; |
| default: |
| netsnmp_assert(0); |
| goto generr; |
| } |
| |
| return SNMP_ERR_NOERROR; |
| |
| generr: |
| SnmpUtilVarBindListFree(pVarBindList); |
| memset(pVarBindList, 0, sizeof(*pVarBindList)); |
| return SNMP_ERR_GENERR; |
| } |
| |
| /** Convert a Windows SNMP error code to the equivalent Net-SNMP error code. */ |
| int |
| convert_win_snmp_err(const int win_snmp_err) |
| { |
| switch (win_snmp_err) { |
| case SNMP_ERRORSTATUS_NOERROR: |
| return SNMP_ERR_NOERROR; |
| case SNMP_ERRORSTATUS_TOOBIG: |
| return SNMP_ERR_TOOBIG; |
| case SNMP_ERRORSTATUS_NOSUCHNAME: |
| /* |
| * Note: SNMP extension DLLs return SNMP_ERRORSTATUS_NOSUCHNAME |
| * when either noSuchObject or noSuchInstance should be returned to |
| * the SNMP manager (assuming SNMPv2c or SNMPv3). Unfortunately it |
| * is not possible without consulting the MIB to find out whether |
| * either SNMP_NOSUCHINSTANCE or SNMP_NOSUCHOBJECT should be returned. |
| * See also RFC 1448. |
| */ |
| return SNMP_NOSUCHINSTANCE; |
| case SNMP_ERRORSTATUS_BADVALUE: |
| return SNMP_ERR_BADVALUE; |
| case SNMP_ERRORSTATUS_READONLY: |
| return SNMP_ERR_READONLY; |
| case SNMP_ERRORSTATUS_GENERR: |
| return SNMP_ERR_GENERR; |
| case SNMP_ERRORSTATUS_NOACCESS: |
| return SNMP_ERR_NOACCESS; |
| case SNMP_ERRORSTATUS_WRONGTYPE: |
| return SNMP_ERR_WRONGTYPE; |
| case SNMP_ERRORSTATUS_WRONGLENGTH: |
| return SNMP_ERR_WRONGLENGTH; |
| case SNMP_ERRORSTATUS_WRONGENCODING: |
| return SNMP_ERR_WRONGENCODING; |
| case SNMP_ERRORSTATUS_WRONGVALUE: |
| return SNMP_ERR_WRONGVALUE; |
| case SNMP_ERRORSTATUS_NOCREATION: |
| return SNMP_ERR_NOCREATION; |
| case SNMP_ERRORSTATUS_INCONSISTENTVALUE: |
| return SNMP_ERR_INCONSISTENTVALUE; |
| case SNMP_ERRORSTATUS_RESOURCEUNAVAILABLE: |
| return SNMP_ERR_RESOURCEUNAVAILABLE; |
| case SNMP_ERRORSTATUS_COMMITFAILED: |
| return SNMP_ERR_COMMITFAILED; |
| case SNMP_ERRORSTATUS_UNDOFAILED: |
| return SNMP_ERR_UNDOFAILED; |
| case SNMP_ERRORSTATUS_AUTHORIZATIONERROR: |
| return SNMP_ERR_AUTHORIZATIONERROR; |
| case SNMP_ERRORSTATUS_NOTWRITABLE: |
| return SNMP_ERR_NOTWRITABLE; |
| case SNMP_ERRORSTATUS_INCONSISTENTNAME: |
| return SNMP_ERR_INCONSISTENTNAME; |
| } |
| netsnmp_assert(0); |
| return SNMP_ERR_GENERR; |
| } |
| |
| /** |
| * Look up the extension DLL view that was registered with the given OID. |
| */ |
| static winextdll_view * |
| lookup_view_by_oid(oid * const name, const size_t name_len) |
| { |
| int i; |
| |
| for (i = 0; i < s_winextdll_view.size; i++) { |
| if (netsnmp_oid_equals(WINEXTDLL_VIEW(i).name, |
| WINEXTDLL_VIEW(i).name_length, |
| name, name_len) == 0 |
| && WINEXTDLL_VIEW(i).my_handler) { |
| return &WINEXTDLL_VIEW(i); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static int |
| snmp_oid_compare_n_w(const oid * name1, size_t len1, const UINT * name2, |
| UINT len2) |
| { |
| netsnmp_static_assert(sizeof(oid) == sizeof(UINT)); |
| return snmp_oid_compare(name1, len1, (const oid *) name2, len2); |
| } |
| |
| static int |
| snmp_oid_compare_w_n(const UINT * name1, UINT len1, const oid * name2, |
| size_t len2) |
| { |
| netsnmp_static_assert(sizeof(oid) == sizeof(UINT)); |
| return snmp_oid_compare((const oid *) name1, len1, name2, len2); |
| } |
| |
| static int |
| netsnmp_oid_is_subtree_n_w(const oid * name1, size_t len1, const UINT * name2, |
| UINT len2) |
| { |
| netsnmp_static_assert(sizeof(oid) == sizeof(UINT)); |
| return netsnmp_oid_is_subtree(name1, len1, (const oid *) name2, len2); |
| } |
| |
| /** |
| * Copy an OID. |
| * |
| * @param[out] to_name Number of elements written to destination OID. |
| * @param[out] to_name_len Length of destination OID. Must have at least |
| * min(from_name_len, MAX_OID_LEN) elements. |
| * @param[in] from_name Original OID. |
| * @param[in] from_name_len Length of original OID. |
| */ |
| static void |
| copy_oid(oid * const to_name, size_t * const to_name_len, |
| const oid * const from_name, const size_t from_name_len) |
| { |
| int j; |
| |
| netsnmp_assert(to_name); |
| netsnmp_assert(to_name_len); |
| netsnmp_assert(from_name); |
| |
| for (j = 0; j < from_name_len && j < MAX_OID_LEN; j++) |
| to_name[j] = from_name[j]; |
| |
| *to_name_len = j; |
| } |
| |
| /** |
| * Copy an OID. |
| * |
| * @param[out] to_name Number of elements written to destination OID. |
| * @param[out] to_name_len Length of destination OID. Must have at least |
| * min(from_name_len, MAX_OID_LEN) elements. |
| * @param[in] from_name Original OID. |
| * @param[in] from_name_len Length of original OID. |
| */ |
| static void |
| copy_oid_n_w(oid * const to_name, size_t * const to_name_len, |
| const UINT * const from_name, const UINT from_name_len) |
| { |
| netsnmp_static_assert(sizeof(oid) == sizeof(UINT)); |
| copy_oid(to_name, to_name_len, (const oid *) from_name, from_name_len); |
| } |
| |
| /** |
| * Convert a Net-SNMP OID into a Windows OID and allocate memory for the |
| * Windows OID. |
| * |
| * @param[out] windows_oid Pointer to a AsnObjectIdentifier. |
| * @param[in] name Pointer to an array with elements of type oid |
| * and length name_len. |
| * @param[in] name_len Number of elements of input and output OID. |
| */ |
| static UINT * |
| copy_oid_to_new_windows_oid(AsnObjectIdentifier * const windows_oid, |
| const oid * const name, const size_t name_len) |
| { |
| netsnmp_assert(windows_oid); |
| netsnmp_assert(windows_oid->ids == 0); |
| netsnmp_assert(windows_oid->idLength == 0); |
| netsnmp_assert(name); |
| |
| windows_oid->ids |
| = |
| (UINT *) winsnmp_memdup(name, |
| sizeof(windows_oid->ids[0]) * name_len); |
| windows_oid->idLength = (UINT) name_len; |
| return windows_oid->ids; |
| } |
| |
| static u_char * |
| winsnmp_memdup(const void *src, const size_t len) |
| { |
| u_char *p; |
| |
| netsnmp_assert(len == (UINT) len); |
| |
| p = SnmpUtilMemAlloc((UINT) len); |
| if (p) |
| memcpy(p, src, len); |
| return p; |
| } |
| |
| #if 0 |
| /** Initialize array 'a'. */ |
| static void |
| xarray_init(xarray * a, size_t elem_size) |
| { |
| netsnmp_assert(a); |
| |
| memset(a, 0, sizeof(*a)); |
| a->elem_size = elem_size; |
| } |
| #endif |
| |
| /** Deallocate any memory that was dynamically allocated for 'a'. */ |
| static void |
| xarray_destroy(xarray * a) |
| { |
| netsnmp_assert(a); |
| |
| xarray_reserve(a, 0); |
| } |
| |
| /** |
| * Append the contents of the address range [ elem, elem + a->elem_size [ to a. |
| * |
| * Resize a if necessary. |
| * |
| * @return A pointer to the address where the data has been copied upon success, |
| * or NULL upon failure. |
| */ |
| static void * |
| xarray_push_back(xarray * a, const void *elem) |
| { |
| netsnmp_assert(a); |
| netsnmp_assert(elem); |
| netsnmp_assert(a->size <= a->reserved); |
| |
| if (a->size == a->reserved) |
| xarray_reserve(a, a->reserved == 0 ? 16 : 2 * a->reserved); |
| if (a->size < a->reserved) { |
| netsnmp_assert(a->size < a->reserved); |
| return memcpy((char *) (a->p) + a->elem_size * a->size++, elem, |
| a->elem_size); |
| } |
| return NULL; |
| } |
| |
| #if 0 |
| /** Erase [ elem, elem + a->elem_size [ from a. */ |
| static void |
| xarray_erase(xarray * a, void *const elem) |
| { |
| netsnmp_assert(a); |
| netsnmp_assert(a->size >= 1); |
| netsnmp_assert(a->p <= elem); |
| netsnmp_assert((const char *) elem + a->elem_size <= |
| (char *) a->p + a->size * a->elem_size); |
| netsnmp_assert(((const char *) elem - (char *) a->p) % a->elem_size == 0); |
| |
| a->size--; |
| memmove((char *) elem, (char *) elem + a->elem_size, |
| a->size - ((const char *) elem - |
| (char *) a->p) / a->elem_size); |
| } |
| #endif |
| |
| /** |
| * Change the number of allocated elements to 'reserved'. |
| * |
| * Can be used either for enlarging or for shrinking the memory allocated for |
| * 'a'. Does not modify 'a' if memory allocation fails. Newly allocted memory |
| * is not initialized. |
| * |
| * @return != NULL upon success, NULL upon failure. |
| */ |
| static void * |
| xarray_reserve(xarray * a, int reserved) |
| { |
| netsnmp_assert(a); |
| netsnmp_assert(a->size <= a->reserved); |
| |
| if ((a->p = realloc(a->p, a->elem_size * reserved))) |
| a->reserved = reserved; |
| else |
| a->reserved = 0; |
| return a->p; |
| } |
| |
| #endif /* USING_WINEXTDLL_MODULE */ |