1 /**
2  * @brief winExtDLL Net-SNMP agent extension module.
3  *
4  * Copyright (c) 2006-2009 Alex Burger.
5  * Copyright (c) 2009-2010 Bart Van Assche <bart.vanassche@gmail.com>.
6  *
7  * This Net-SNMP agent extension module loads Windows SNMP Extension Agent
8  * DLLs in the Net-SNMP agent. Not only extension DLLs provided with Windows
9  * (e.g. hostmib.dll) but also third-party extension DLLs are supported. This
10  * allows Net-SNMP to be a replacement for the Windows SNMP service, and makes
11  * it possible to use the SNMPv3 protocol.
12  *
13  * @see See also <a href="http://msdn.microsoft.com/en-us/library/aa378988(VS.85).aspx">SNMP Functions</a>
14  *   for more information about Microsoft's SNMP Extension Agent API.
15  *
16  * @note In order to use this agent extension module, the Windows SNMP service
17  *   must be installed first and must be disabled. Installing the Windows SNMP
18  *   service is the only way to install the Windows Extension DLLs and to make
19  *   sure that information about these DLLs is present in the registry.
20  *
21  * @note All Windows extension DLLs are loaded during startup of the Net-SNMP
22  *   service. The Net-SNMP service must be restarted to load new modules. This
23  *   extension is NOT for dynamically loading Net-SNMP extensions.
24  *
25  *
26  * History:
27  * - 2010/03/19:
28  *    * Multi-varbind set request PDUs are now handled correctly.
29  *    * If loading an extension DLL fails, the reason why this failed is now
30  *      logged.
31  *    * Fixed a memory leak that occurred when SnmpExtensionQuery() or
32  *      SnmpExtensionQueryEx() failed while processing an SNMP PDU. Note:
33  *      occurrence of an SNMP error does not make these functions fail, and
34  *      it is not yet known whether or not it was possible to trigger this
35  *      memory leak.
36  * - 2010/03/17: Fixed bug 2971257. Multi-varbind getNext requests with OIDs
37  *     in reverse lexicographical order are again processed correctly.
38  * - 2010/01/22: Compiles now with MinGW too.
39  * - 2009/12/11:
40  *   * The value of sysUpTime.0 reported by inetmib1.dll is now correct.
41  *   * A linkUp or linkDown trap is now sent after the status of a network
42  *     interface has changed.
43  * - 2009/03/26:
44  *   * Removed several artificial limits. Result: more than 100 SNMP extension
45  *     DLLs can now be loaded simultaneously and more than 100 OID ranges can
46  *     now be registered. Loading e.g. the Dell OpenManage SNMP extension DLL
47  *     does no longer crash Net-SNMP.
48  *   * Number of OID ranges registered during startup is now logged.
49  *   * It is no longer attempted to free the Broadcom SNMP extension DLLs
50  *     bcmif.dll and baspmgnt.dll since doing so triggers a deadlock.
51  *   * Added support for reregistration of an OID prefix. As an example, both
52  *     both Microsoft's inetmib1.dll and the Eicon Diva divasnmpx.dll register
53  *     the OID prefix iso.org.dod.internet.mgmt.mib-2.interfaces
54  *     (.1.3.6.1.2.1.2). WinExtDLL will process OIDs with this prefix by using
55  *     the handler that was registered last for the OID prefix. A message will
56  *     be logged indicating that a handler has been replaced.
57  * - 2009/03/10:
58  *   * Fixed several bugs in var_winExtDLL(): looking up extension DLL info
59  *     based on the OID in a varbind is wrong. It does happen during GetNext
60  *     processing that Net-SNMP passes intentionally varbinds to a handler
61  *     with OIDs that are outside the range registered by the handler. Fixed
62  *     this by filling in a pointer to the extension DLL info in
63  *     netsnmp_mib_handler::myvoid and by using that information in the
64  *     var_winExtDLL() handler function.
65  *   * SetRequest PDUs are now passed once to an extension DLL instead of
66  *     four times.
67  *   * The error status and error index of a multi-varbind set request is now
68  *     filled in correctly.
69  *   * Added support for the SNMP extension DLL three-phase SNMP set.
70  *   * Made traps SNMPv2 compliant by adding the sysUpTime.0 varbind.
71  *   * The varbind list generated by extension DLLs for e.g. linkUp and
72  *     linkDown traps is now passed to Net-SNMP. Previously this varbind list
73  *     was discarded for generic traps.
74  *   * Fixed memory leaks triggered by Get and GetNext PDU processing.
75  *   * Added missing RegCloseKey() calls.
76  *   * Added shutdown function shutdown_winExtDLL().
77  *   * Replaced #include <cstdio> by #include <stdio.h> such that this source
78  *     file compiles with Visual Studio 2005.
79  *   * Removed many unused local variables.
80  *   * Fixed several other compiler warnings.
81  * - 2006/09/09: creation of this file.
82  */
83 
84 #include <net-snmp/net-snmp-config.h>
85 #include <net-snmp/net-snmp-features.h>
86 #include <net-snmp/agent/mib_module_config.h>
87 
88 #ifdef USING_WINEXTDLL_MODULE
89 
90 #include <net-snmp/types.h>
91 
92 #include <stdio.h>
93 #include <stdlib.h>
94 #include <string.h>
95 #include <time.h>
96 #include <windows.h>
97 #include <winerror.h>
98 #include "../../win32/Snmp-winExtDLL.h"
99 
100 #include <net-snmp/net-snmp-includes.h>
101 #include <net-snmp/library/snmp_assert.h>
102 #include <net-snmp/agent/net-snmp-agent-includes.h>
103 #include "util_funcs.h"
104 #include "winExtDLL.h"
105 
106 netsnmp_feature_require(oid_is_subtree);
107 
108 
109 #define MAX_VALUE_NAME          16383
110 #define MS_ASN_UINTEGER32 MS_ASN_UNSIGNED32
111 
112 
113 typedef         BOOL(WINAPI *
114                      PFNSNMPEXTENSIONINIT) (DWORD dwUpTimeReference,
115                                             HANDLE * phSubagentTrapEvent,
116                                             AsnObjectIdentifier *
117                                             pFirstSupportedRegion);
118 
119 typedef         BOOL(WINAPI *
120                      PFNSNMPEXTENSIONINITEX) (AsnObjectIdentifier *
121                                               pNextSupportedRegion);
122 
123 typedef         BOOL(WINAPI *
124                      PFNSNMPEXTENSIONMONITOR) (LPVOID pAgentMgmtData);
125 
126 typedef         BOOL(WINAPI * PFNSNMPEXTENSIONQUERY) (BYTE bPduType,
127                                                       SnmpVarBindList *
128                                                       pVarBindList,
129                                                       AsnInteger32 *
130                                                       pErrorStatus,
131                                                       AsnInteger32 *
132                                                       pErrorIndex);
133 
134 typedef         BOOL(WINAPI * PFNSNMPEXTENSIONQUERYEX) (UINT nRequestType,
135                                                         UINT
136                                                         nTransactionId,
137                                                         SnmpVarBindList *
138                                                         pVarBindList,
139                                                         AsnOctetString *
140                                                         pContextInfo,
141                                                         AsnInteger32 *
142                                                         pErrorStatus,
143                                                         AsnInteger32 *
144                                                         pErrorIndex);
145 
146 typedef         BOOL(WINAPI * PFNSNMPEXTENSIONTRAP) (AsnObjectIdentifier *
147                                                      pEnterpriseOid,
148                                                      AsnInteger32 *
149                                                      pGenericTrapId,
150                                                      AsnInteger32 *
151                                                      pSpecificTrapId,
152                                                      AsnTimeticks *
153                                                      pTimeStamp,
154                                                      SnmpVarBindList *
155                                                      pVarBindList);
156 
157 typedef         VOID(WINAPI * PFNSNMPEXTENSIONCLOSE) (void);
158 
159 typedef BOOL (WINAPI *pfIsWow64Process)(HANDLE hProcess, BOOL *Wow64Process);
160 
161 
162 /**
163  * Extensible array, a data structure similar to the C++ STL class
164  * std::vector<>.
165  */
166 typedef struct {
167     /** Pointer to the memory allocated for the array. */
168     void           *p;
169     /** Number of bytes occupied by a single element.  */
170     size_t          elem_size;
171     /** Number of elements that have been allocated.   */
172     int             reserved;
173     /** Number of elements currently in use.           */
174     int             size;
175 } xarray;
176 
177 /**
178  * Information managed by winExtDLL about Windows SNMP extension DLL's.
179  */
180 typedef struct {
181     char           *dll_name;                        /**< Dynamically allocated DLL name. */
182     HANDLE          dll_handle;                      /**< DLL handle. */
183     PFNSNMPEXTENSIONINIT pfSnmpExtensionInit;
184     PFNSNMPEXTENSIONINITEX pfSnmpExtensionInitEx;
185     PFNSNMPEXTENSIONCLOSE pfSnmpExtensionClose;
186     PFNSNMPEXTENSIONQUERY pfSnmpExtensionQuery;
187     PFNSNMPEXTENSIONQUERYEX pfSnmpExtensionQueryEx;
188     PFNSNMPEXTENSIONTRAP pfSnmpExtensionTrap;
189     HANDLE          subagentTrapEvent;
190 } winextdll;
191 
192 /**
193  * Information managed by winExtDLL about a single view of a Windows SNMP
194  * extension DLL.
195  */
196 typedef struct {
197     winextdll      *winextdll_info;
198     netsnmp_handler_registration *my_handler;
199     oid             name[MAX_OID_LEN];                   /**< OID of this view. */
200     size_t          name_length;
201 } winextdll_view;
202 
203 /**
204  * Per varbind SNMP extension DLL context information for SNMP set operations.
205  */
206 typedef struct context_info_s {
207     struct context_info_s *next;
208     int             index;
209     AsnOctetString  context_info;
210 } context_info;
211 
212 
213 /*
214  * External function declarations.
215  */
216 void __declspec(dllimport) WINAPI SnmpSvcInitUptime(void);
217 
218 
219 /*
220  * Local functions declarations.
221  */
222 static int      basename_equals(const char *path, const char *basename);
223 static int      register_netsnmp_handler(winextdll_view *
224                                          const ext_dll_view_info);
225 static void     read_extension_dlls_from_registry(void);
226 static void     read_extension_dlls_from_registry_at(const char *const subkey);
227 static char    *read_extension_dll_path_from_registry(const TCHAR *);
228 static void     subagentTrapCheck(unsigned int clientreg, void *clientarg);
229 static int      var_winExtDLL(netsnmp_mib_handler *handler,
230                               netsnmp_handler_registration *reginfo,
231                               netsnmp_agent_request_info *reqinfo,
232                               netsnmp_request_info *requests);
233 static int      append_windows_varbind_list(netsnmp_variable_list **
234                                             const net_snmp_varbinds,
235                                             const SnmpVarBindList *
236                                             const win_varbinds);
237 static int      append_windows_varbind(netsnmp_variable_list **
238                                        const net_snmp_varbinds,
239                                        const SnmpVarBind *
240                                        const win_varbind);
241 static int      convert_to_windows_varbind_list(SnmpVarBindList *
242                                                 pVarBindList,
243                                                 netsnmp_variable_list *
244                                                 netsnmp_varbinds);
245 static int      convert_win_snmp_err(const int win_snmp_err);
246 static winextdll_view *lookup_view_by_oid(oid * const name,
247                                           const size_t name_len);
248 static int      snmp_oid_compare_n_w(const oid * name1, size_t len1,
249                                      const UINT * name2, UINT len2);
250 static int      snmp_oid_compare_w_n(const UINT * name1, UINT len1,
251                                      const oid * name2, size_t len2);
252 static int      netsnmp_oid_is_subtree_n_w(const oid * name1, size_t len1,
253                                            const UINT * name2, UINT len2);
254 static void     copy_oid(oid * const to_name, size_t * const to_name_len,
255                          const oid * const from_name,
256                          const size_t from_name_len);
257 static void     copy_oid_n_w(oid * const to_name, size_t * const to_name_len,
258                              const UINT * const from_name,
259                              const UINT from_name_len);
260 static UINT    *copy_oid_to_new_windows_oid(AsnObjectIdentifier *
261                                             const windows_oid,
262                                             const oid * const name,
263                                             const size_t name_len);
264 static int      snmp_set_var_objid_w(netsnmp_variable_list * var,
265                                      const UINT * name, UINT name_length);
266 static netsnmp_variable_list *
267 snmp_varlist_add_variable_w(netsnmp_variable_list ** varlist,
268                             const UINT * name, UINT name_length,
269                             u_char type, const void * value, size_t len);
270 static void     send_trap(const AsnObjectIdentifier * const,
271                           const AsnInteger, const AsnInteger,
272                           const AsnTimeticks,
273                           const SnmpVarBindList * const);
274 static u_char  *winsnmp_memdup(const void *src, const size_t len);
275 #if 0
276 static void     xarray_init(xarray * a, size_t elem_size);
277 #endif
278 static void     xarray_destroy(xarray * a);
279 static void    *xarray_push_back(xarray * a, const void *elem);
280 #if 0
281 static void     xarray_erase(xarray * a, void *const elem);
282 #endif
283 static void    *xarray_reserve(xarray * a, int reserved);
284 
285 
286 /*
287  * Local variable definitions.
288  */
289 #define WINEXTDLL(i)            ((winextdll*)s_winextdll.p)[i]
290 #define WINEXTDLL_VIEW(i)       ((winextdll_view*)s_winextdll_view.p)[i]
291 #define TRAPEVENT(i)            ((HANDLE*)s_trapevent.p)[i]
292 #define TRAPEVENT_TO_DLLINFO(i) ((winextdll**)s_trapevent_to_dllinfo.p)[i]
293 static const oid mibii_system_mib[] = { 1, 3, 6, 1, 2, 1, 1 };
294 static OSVERSIONINFO s_versioninfo = { sizeof(s_versioninfo) };
295 static xarray   s_winextdll = { 0, sizeof(winextdll) };
296 static xarray   s_winextdll_view = { 0, sizeof(winextdll_view) };
297 static xarray   s_trapevent = { 0, sizeof(HANDLE) };
298 static xarray   s_trapevent_to_dllinfo = { 0, sizeof(winextdll *) };
299 static context_info *context_info_head;
300 
301 
302 /*
303  * Function definitions.
304  */
305 
306 /** Initialize the winExtDLL extension agent. */
307 void
init_winExtDLL(void)308 init_winExtDLL(void)
309 {
310     BOOL            result, is_wow64_process = FALSE;
311     int             i;
312     uint32_t        uptime_reference;
313     pfIsWow64Process IsWow64Process;
314 
315     DEBUGMSG(("winExtDLL", "init_winExtDLL started.\n"));
316 
317     GetVersionEx(&s_versioninfo);
318 
319     IsWow64Process = (pfIsWow64Process)(uintptr_t)
320 	GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
321     if (IsWow64Process)
322         (*IsWow64Process)(GetCurrentProcess(), &is_wow64_process);
323 
324     SnmpSvcInitUptime();
325 
326     read_extension_dlls_from_registry();
327 
328     DEBUGMSG(("winExtDLL",
329               "init_winExtDLL: found %d extension DLLs in the registry.\n",
330               s_winextdll.size));
331 
332     xarray_reserve(&s_winextdll, 128);
333 
334     /*
335      * Load all the DLLs
336      */
337     for (i = 0; i < s_winextdll.size; i++) {
338         winextdll      *const ext_dll_info = &WINEXTDLL(i);
339         AsnObjectIdentifier view;
340         winextdll_view  ext_dll_view_info;
341 
342         netsnmp_assert(ext_dll_info);
343         if (!ext_dll_info->dll_name)
344             continue;
345 
346         DEBUGMSG(("winExtDLL", "loading DLL %s.\n",
347                   ext_dll_info->dll_name));
348         ext_dll_info->dll_handle = LoadLibrary(ext_dll_info->dll_name);
349 
350         if (ext_dll_info->dll_handle == NULL) {
351             const DWORD     dwErrorcode = GetLastError();
352             LPTSTR          lpMsgBuf;
353 
354             FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
355                           FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrorcode,
356                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
357                           (LPTSTR) & lpMsgBuf, 0, NULL);
358             if (lpMsgBuf) {
359                 LPTSTR          p;
360 
361                 /*
362                  * Remove trailing "\r\n".
363                  */
364                 p = strchr(lpMsgBuf, '\r');
365                 if (p)
366                     *p = '\0';
367             }
368             snmp_log(LOG_ERR,
369                      "init_winExtDLL: could not load SNMP extension"
370                      " DLL %s: %s\n",
371                      ext_dll_info->dll_name, lpMsgBuf ? lpMsgBuf : "(?)");
372             if (lpMsgBuf)
373                 LocalFree(lpMsgBuf);
374             continue;
375         }
376 
377         /*
378          * Store DLL name and functions in s_extension_dll_info array.
379          */
380         ext_dll_info->pfSnmpExtensionInit = (PFNSNMPEXTENSIONINIT)(uintptr_t)
381             GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionInit");
382         ext_dll_info->pfSnmpExtensionInitEx =
383 	    (PFNSNMPEXTENSIONINITEX)(uintptr_t)
384             GetProcAddress(ext_dll_info->dll_handle,
385                            "SnmpExtensionInitEx");
386         ext_dll_info->pfSnmpExtensionClose = (PFNSNMPEXTENSIONCLOSE)
387             GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionClose");
388         ext_dll_info->pfSnmpExtensionQuery = (PFNSNMPEXTENSIONQUERY)(uintptr_t)
389             GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionQuery");
390         ext_dll_info->pfSnmpExtensionQueryEx =
391 	    (PFNSNMPEXTENSIONQUERYEX)(uintptr_t)
392             GetProcAddress(ext_dll_info->dll_handle,
393                            "SnmpExtensionQueryEx");
394         ext_dll_info->pfSnmpExtensionTrap = (PFNSNMPEXTENSIONTRAP)(uintptr_t)
395             GetProcAddress(ext_dll_info->dll_handle, "SnmpExtensionTrap");
396 
397 
398         if (ext_dll_info->pfSnmpExtensionQuery == NULL
399             && ext_dll_info->pfSnmpExtensionQueryEx == NULL) {
400             snmp_log(LOG_ERR,
401                      "error in extension DLL %s: SNMP query function missing.\n",
402                      ext_dll_info->dll_name);
403         }
404 
405         /*
406          * At least on a 64-bit Windows 7 system invoking SnmpExtensionInit()
407          * in the 32-bit version of evntagnt.dll hangs. Also, all queries in
408          * lmmib2.dll fail with "generic error" on a 64-bit Windows 7 system.
409          * So skip these two DLLs.
410          */
411         if (s_versioninfo.dwMajorVersion >= 6
412             && ((is_wow64_process
413                  && basename_equals(ext_dll_info->dll_name, "evntagnt.dll"))
414                 || basename_equals(ext_dll_info->dll_name, "lmmib2.dll"))) {
415             DEBUGMSG(("winExtDLL", "init_winExtDLL: skipped DLL %s.\n",
416                       ext_dll_info->dll_name));
417             continue;
418         }
419 
420         /*
421          * Init and get first supported view from Windows SNMP extension DLL.
422          * Note: although according to the documentation of SnmpExtensionInit()
423          * the first argument of this function should be ignored by extension
424          * DLLs, passing a correct value for this first argument is necessary
425          * to make inetmib1.dll work correctly. Passing zero as the first
426          * argument causes inetmib1.dll to report an incorrect value for
427          * sysUpTime.0 and also causes the same DLL not to send linkUp or
428          * linkDown traps.
429          */
430         ext_dll_info->subagentTrapEvent = NULL;
431         view.idLength = 0;
432         view.ids = NULL;
433         if (!is_wow64_process && s_versioninfo.dwMajorVersion >= 6)
434             uptime_reference = GetTickCount() - 10 * SnmpSvcGetUptime();
435         else
436             uptime_reference = GetTickCount() / 10;
437         result =
438             ext_dll_info->pfSnmpExtensionInit(uptime_reference,
439                                               &ext_dll_info->
440                                               subagentTrapEvent, &view);
441         DEBUGMSG(("winExtDLL",
442                   "init_winExtDLL: DLL %s initialization result %d\n",
443                   ext_dll_info->dll_name, result));
444 
445         if (ext_dll_info->subagentTrapEvent != NULL) {
446             xarray_push_back(&s_trapevent,
447                              &ext_dll_info->subagentTrapEvent);
448             xarray_push_back(&s_trapevent_to_dllinfo, &ext_dll_info);
449         }
450 
451         memset(&ext_dll_view_info, 0, sizeof(ext_dll_view_info));
452         ext_dll_view_info.winextdll_info = ext_dll_info;
453         DEBUGMSG(("winExtDLL",
454                   "init_winExtDLL: DLL %s view length %d\n",
455                   ext_dll_info->dll_name, view.idLength));
456         if (view.idLength) {
457             /*
458              * Skip the mib-2 system section on Windows Vista and later because
459              * at least on a 64-bit Windows 7 system all queries in that section
460              * fail with status "generic error".
461              */
462             if (s_versioninfo.dwMajorVersion >= 6
463                 && snmp_oid_compare_w_n(view.ids, view.idLength,
464                                         mibii_system_mib,
465                                         sizeof(mibii_system_mib) /
466                                         sizeof(mibii_system_mib[0])) == 0) {
467                 DEBUGMSG(("winExtDLL",
468                           "init_winExtDLL: skipping system section of DLL %s.\n",
469                           ext_dll_info->dll_name));
470             } else {
471                 copy_oid_n_w(ext_dll_view_info.name,
472                              &ext_dll_view_info.name_length,
473                              view.ids, view.idLength);
474                 xarray_push_back(&s_winextdll_view, &ext_dll_view_info);
475             }
476         }
477 
478         /*
479          * Loop looking for more supported views.
480          */
481         while (ext_dll_info->pfSnmpExtensionInitEx
482                && ext_dll_info->pfSnmpExtensionInitEx(&view)) {
483             DEBUGMSG(("winExtDLL",
484                       "init_winExtDLL: DLL %s view length %d\n",
485                       ext_dll_info->dll_name, view.idLength));
486             memset(&ext_dll_view_info, 0, sizeof(ext_dll_view_info));
487             ext_dll_view_info.winextdll_info = ext_dll_info;
488             copy_oid_n_w(ext_dll_view_info.name,
489                          &ext_dll_view_info.name_length, view.ids,
490                          view.idLength);
491             xarray_push_back(&s_winextdll_view, &ext_dll_view_info);
492         }
493     }
494 
495     /*
496      * Note: since register_netsnmp_handler() writes a pointer to the
497      * winextdll_view in one of the Net-SNMP data structures, it is not
498      * allowed to move winextdll_view data structures in memory after
499      * registration with Net-SNMP. Or: register_snmp_handler() must be called
500      * only once it is sure that the size of array s_winextdll_view won't change
501      * anymore.
502      */
503     for (i = 0; i < s_winextdll_view.size; i++)
504         register_netsnmp_handler(&WINEXTDLL_VIEW(i));
505 
506     DEBUGMSG(("winExtDLL",
507               "init_winExtDLL: registered %d OID ranges.\n",
508               s_winextdll_view.size));
509 
510     /*
511      * Let Net-SNMP call subagentTrapCheck() once per second.
512      */
513     if (s_trapevent.size)
514         snmp_alarm_register(1, SA_REPEAT, subagentTrapCheck, NULL);
515 
516     DEBUGMSG(("winExtDLL", "init_winExtDLL finished.\n"));
517 }
518 
519 void
shutdown_winExtDLL(void)520 shutdown_winExtDLL(void)
521 {
522     int             i;
523 
524     DEBUGMSG(("winExtDLL", "shutdown_winExtDLL() started.\n"));
525 
526     for (i = s_winextdll_view.size - 1; i >= 0; i--) {
527         winextdll_view *const v = &WINEXTDLL_VIEW(i);
528         if (v && v->my_handler) {
529             DEBUGIF("winExtDLL") {
530                 DEBUGMSG(("winExtDLL",
531                           "unregistering handler for DLL %s and OID prefix ",
532                           v->winextdll_info->dll_name));
533                 DEBUGMSGOID(("winExtDLL", v->name, v->name_length));
534                 DEBUGMSG(("winExtDLL", " ("));
535                 DEBUGMSGSUBOID(("winExtDLL", v->name, v->name_length));
536                 DEBUGMSG(("winExtDLL", ").\n"));
537             }
538             netsnmp_unregister_handler(v->my_handler);
539         }
540     }
541     xarray_destroy(&s_winextdll_view);
542 
543     for (i = s_winextdll.size - 1; i >= 0; i--) {
544         winextdll      *const ext_dll_info = &WINEXTDLL(i);
545         if (ext_dll_info->dll_handle) {
546             if (ext_dll_info->pfSnmpExtensionClose) {
547                 DEBUGMSG(("winExtDLL", "closing %s.\n",
548                           ext_dll_info->dll_name));
549                 ext_dll_info->pfSnmpExtensionClose();
550             }
551             /*
552              * Freeing the Broadcom SNMP extension libraries triggers
553              * a deadlock, so skip bcmif.dll and baspmgnt.dll.
554              */
555             if (!basename_equals(ext_dll_info->dll_name, "bcmif.dll")
556                 && !basename_equals(ext_dll_info->dll_name, "baspmgnt.dll")) {
557                 DEBUGMSG(("winExtDLL", "unloading %s.\n",
558                           ext_dll_info->dll_name));
559                 FreeLibrary(ext_dll_info->dll_handle);
560             }
561         }
562         free(ext_dll_info->dll_name);
563     }
564     xarray_destroy(&s_winextdll);
565 
566     xarray_destroy(&s_trapevent_to_dllinfo);
567 
568     xarray_destroy(&s_trapevent);
569 
570     DEBUGMSG(("winExtDLL", "shutdown_winExtDLL() finished.\n"));
571 }
572 
573 /**
574  * Compare the basename of a path with a given string.
575  *
576  * @return 1 if the basename matches, 0 if not.
577  */
578 static int
basename_equals(const char * path,const char * basename)579 basename_equals(const char *path, const char *basename)
580 {
581     const size_t    path_len = strlen(path);
582     const size_t    basename_len = strlen(basename);
583 
584     netsnmp_assert(strchr(path, '/') == 0);
585     netsnmp_assert(strchr(basename, '/') == 0);
586     netsnmp_assert(strchr(basename, '\\') == 0);
587 
588     return path_len >= basename_len + 1
589         && path[path_len - basename_len - 1] == '\\'
590         && strcasecmp(path + path_len - basename_len, basename) == 0;
591 }
592 
593 /**
594  * Register a single OID subtree with Net-SNMP.
595  *
596  * @return 1 if successful, 0 if not.
597  */
598 int
register_netsnmp_handler(winextdll_view * const ext_dll_view_info)599 register_netsnmp_handler(winextdll_view * const ext_dll_view_info)
600 {
601     winextdll      *ext_dll_info;
602     winextdll_view *previously_registered_view;
603 
604     ext_dll_info = ext_dll_view_info->winextdll_info;
605 
606     previously_registered_view
607         = lookup_view_by_oid(ext_dll_view_info->name,
608                              ext_dll_view_info->name_length);
609 
610     if (previously_registered_view) {
611         size_t          oid_namelen, outlen;
612         char           *oid_name;
613         int             buffer_large_enough;
614 
615         oid_namelen = 0;
616         outlen = 0;
617         oid_name = NULL;
618         buffer_large_enough =
619             sprint_realloc_objid((u_char **) & oid_name, &oid_namelen,
620                                  &outlen, 1, ext_dll_view_info->name,
621                                  ext_dll_view_info->name_length);
622         snmp_log(LOG_INFO, "OID range %s%s: replacing handler %s by %s.\n",
623                  oid_name ? oid_name : "",
624                  buffer_large_enough ? "" : " [TRUNCATED]",
625                  previously_registered_view->winextdll_info->dll_name,
626                  ext_dll_view_info->winextdll_info->dll_name);
627         if (oid_name)
628             free(oid_name);
629 
630         previously_registered_view->winextdll_info = ext_dll_info;
631         memset(ext_dll_view_info, 0, sizeof(*ext_dll_view_info));
632         return 1;
633     } else {
634         // Create handler registration
635         ext_dll_view_info->my_handler
636             = netsnmp_create_handler_registration(ext_dll_info->dll_name,
637                                                   var_winExtDLL,
638                                                   ext_dll_view_info->name,
639                                                   ext_dll_view_info->
640                                                   name_length,
641                                                   HANDLER_CAN_RWRITE);
642 
643         if (ext_dll_view_info->my_handler) {
644             ext_dll_view_info->my_handler->handler->myvoid =
645                 ext_dll_view_info;
646             if (netsnmp_register_handler(ext_dll_view_info->my_handler)
647                 == MIB_REGISTERED_OK) {
648                 DEBUGIF("winExtDLL") {
649                     DEBUGMSG(("winExtDLL",
650                               "registering handler for DLL %s and OID prefix ",
651                               ext_dll_info->dll_name));
652                     DEBUGMSGOID(("winExtDLL", ext_dll_view_info->name,
653                                  ext_dll_view_info->name_length));
654                     DEBUGMSG(("winExtDLL", " ("));
655                     DEBUGMSGSUBOID(("winExtDLL", ext_dll_view_info->name,
656                                     ext_dll_view_info->name_length));
657                     DEBUGMSG(("winExtDLL", ").\n"));
658                 }
659                 return 1;
660             } else {
661                 snmp_log(LOG_ERR, "handler registration failed.\n");
662                 ext_dll_view_info->my_handler = 0;
663             }
664         } else {
665             snmp_log(LOG_ERR, "handler creation failed.\n");
666         }
667     }
668 
669     return 0;
670 }
671 
672 /**
673  * Allocate SNMP extension DLL context information. Such context information
674  * is necessary to allow an extension DLL to process a set request.
675  *
676  * @param[in] index Varbind index in original PDU.
677  *
678  * @return NULL if context information for the specified index was already
679  *   allocated, and otherwise a pointer to the newly allocated context
680  *   information.
681  */
682 static context_info *
alloc_context_info(const int index)683 alloc_context_info(const int index)
684 {
685     context_info   *p;
686 
687     DEBUGMSG(("winExtDLL:context_info", "alloc_context_info(%d)\n",
688               index));
689 
690     for (p = context_info_head; p; p = p->next) {
691         if (p->index == index) {
692             netsnmp_assert(FALSE);
693             return NULL;
694         }
695     }
696 
697     p = calloc(1, sizeof(context_info));
698     p->next = context_info_head;
699     context_info_head = p;
700     p->index = index;
701 
702     return p;
703 }
704 
705 /**
706  * Deallocate SNMP extension DLL context information.
707  *
708  * @param[in] index Varbind index in original PDU.
709  */
710 static void
free_context_info(const int index)711 free_context_info(const int index)
712 {
713     context_info  **pprev = &context_info_head;
714     context_info   *p;
715 
716     DEBUGMSG(("winExtDLL:context_info", "free_context_info(%d)\n", index));
717 
718     for (p = context_info_head; p; p = p->next) {
719         if (p->index == index) {
720             *pprev = p->next;
721             free(p);
722             break;
723         }
724         pprev = &p->next;
725     }
726 }
727 
728 /**
729  * Look up SNMP extension DLL context information.
730  *
731  * @param[in] index Varbind index in original PDU.
732  */
733 static AsnOctetString *
get_context_info(const int index)734 get_context_info(const int index)
735 {
736     context_info   *p;
737 
738     DEBUGMSG(("winExtDLL:context_info", "get_context_info(%d)\n", index));
739 
740     for (p = context_info_head; p; p = p->next)
741         if (p->index == index)
742             return &p->context_info;
743 
744     netsnmp_assert(FALSE);
745     return NULL;
746 }
747 
748 /*
749  * Translate Net-SNMP request mode into an SnmpExtensionQuery() PDU type
750  * or into an SnmpExtensionQueryEx() request type.
751  */
752 static int
get_request_type(int mode,int request_type,UINT * nRequestType)753 get_request_type(int mode, int request_type, UINT *nRequestType)
754 {
755     switch (request_type) {
756     case 0:
757         /* SnmpExtensionQuery() PDU type */
758         switch (mode) {
759         case MODE_GET:
760             *nRequestType = SNMP_PDU_GET;
761             return 1;
762         case MODE_GETNEXT:
763             *nRequestType = SNMP_PDU_GETNEXT;
764             return 1;
765         case MODE_SET_RESERVE1:
766             return 0;
767         case MODE_SET_RESERVE2:
768             return 0;
769         case MODE_SET_ACTION:
770             return 0;
771         case MODE_SET_UNDO:
772             return 0;
773         case MODE_SET_COMMIT:
774             *nRequestType = SNMP_PDU_SET;
775             return 1;
776         case MODE_SET_FREE:
777             return 0;
778         default:
779             DEBUGMSG(("winExtDLL", "internal error: invalid mode %d.\n", mode));
780             netsnmp_assert(0);
781             return 0;
782         }
783     case 1:
784         /* SnmpExtensionQueryEx() request type */
785         switch (mode) {
786         case MODE_GET:
787             *nRequestType = SNMP_EXTENSION_GET;
788             return 1;
789         case MODE_GETNEXT:
790             *nRequestType = SNMP_EXTENSION_GET_NEXT;
791             return 1;
792         case MODE_SET_RESERVE1:
793             *nRequestType = SNMP_EXTENSION_SET_TEST;
794             return 1;
795         case MODE_SET_RESERVE2:
796             return 0;
797         case MODE_SET_ACTION:
798             return 0;
799         case MODE_SET_UNDO:
800             *nRequestType = SNMP_EXTENSION_SET_UNDO;
801             return 1;
802         case MODE_SET_COMMIT:
803             *nRequestType = SNMP_EXTENSION_SET_COMMIT;
804             return 1;
805         case MODE_SET_FREE:
806             *nRequestType = SNMP_EXTENSION_SET_CLEANUP;
807             return 1;
808         default:
809             DEBUGMSG(("winExtDLL", "internal error: invalid mode %d.\n", mode));
810             netsnmp_assert(0);
811             return 0;
812         }
813     default:
814         DEBUGMSG(("winExtDLL", "internal error: invalid argument %d.\n",
815                   request_type));
816         netsnmp_assert(0);
817         return 0;
818     }
819 }
820 
821 static int
var_winExtDLL(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)822 var_winExtDLL(netsnmp_mib_handler *handler,
823               netsnmp_handler_registration *reginfo,
824               netsnmp_agent_request_info *reqinfo,
825               netsnmp_request_info *requests)
826 {
827     winextdll_view *const ext_dll_view_info = handler->myvoid;
828     winextdll      *ext_dll_info;
829     netsnmp_request_info *request;
830     UINT            nRequestType;
831     int             rc;
832 
833     netsnmp_assert(ext_dll_view_info);
834     ext_dll_info = ext_dll_view_info->winextdll_info;
835 #if ! defined(NDEBUG)
836     netsnmp_assert(ext_dll_view_info ==
837            lookup_view_by_oid(reginfo->rootoid, reginfo->rootoid_len));
838 #endif
839 
840     if (ext_dll_info == 0) {
841         DEBUGMSG(("winExtDLL",
842                   "internal error: no matching extension DLL found.\n"));
843         netsnmp_assert(0);
844         return SNMP_ERR_GENERR;
845     }
846 
847     if (!get_request_type(reqinfo->mode, !!ext_dll_info->pfSnmpExtensionQueryEx,
848                           &nRequestType)) {
849         return SNMP_ERR_NOERROR;
850     }
851 
852     rc = SNMP_ERR_NOERROR;
853 
854     for (request = requests; request; request = request->next) {
855         netsnmp_variable_list *varbind;
856         SnmpVarBindList win_varbinds;
857         AsnInteger32    ErrorStatus;
858         AsnInteger32    ErrorIndex;
859         BOOL            result;
860         BOOL            copy_value;
861 
862         memset(&win_varbinds, 0, sizeof(win_varbinds));
863 
864         if (request->processed || rc != SNMP_ERR_NOERROR)
865             goto free_win_varbinds;
866 
867         if (reqinfo->mode == MODE_SET_RESERVE1)
868             alloc_context_info(request->index);
869 
870         varbind = request->requestvb;
871         netsnmp_assert(varbind);
872 
873         /*
874          * Convert the Net-SNMP varbind to a Windows SNMP varbind list.
875          */
876         rc = convert_to_windows_varbind_list(&win_varbinds, varbind);
877         if (rc != SNMP_ERR_NOERROR) {
878             DEBUGMSG(("winExtDLL",
879                       "converting varbind list to Windows format failed with"
880                       " error code %d.\n", request->status));
881             netsnmp_request_set_error(requests, rc);
882             goto free_win_varbinds;
883         }
884 
885         netsnmp_assert(win_varbinds.len == 1);
886 
887         /*
888          * For a GetNext PDU, if the varbind OID comes lexicographically
889          * before the root OID of this handler, replace it by the root OID.
890          */
891         if (reqinfo->mode == MODE_GETNEXT
892             && snmp_oid_compare_w_n(win_varbinds.list[0].name.ids,
893                                     win_varbinds.list[0].name.idLength,
894                                     reginfo->rootoid,
895                                     reginfo->rootoid_len) < 0) {
896             DEBUGIF("winExtDLL") {
897                 size_t          oid1_namelen = 0, oid2_namelen = 0, outlen1 = 0,
898                                 outlen2 = 0;
899                 char           *oid1_name = NULL, *oid2_name = NULL;
900                 int             overflow1 = 0, overflow2 = 0;
901 
902                 netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
903                 netsnmp_sprint_realloc_objid((u_char **) & oid1_name,
904                                              &oid1_namelen, &outlen1, 1,
905                                              &overflow1, (const oid *)
906                                              win_varbinds.list[0].name.ids,
907                                              win_varbinds.list[0].name.idLength);
908                 netsnmp_sprint_realloc_objid((u_char **) & oid2_name,
909                                              &oid2_namelen, &outlen2, 1,
910                                              &overflow2, reginfo->rootoid,
911                                              reginfo->rootoid_len);
912                 DEBUGMSG(("winExtDLL",
913                           "extension DLL %s: replacing OID %s%s by OID %s%s.\n",
914                           ext_dll_info->dll_name,
915                           oid1_name, overflow1 ? " [TRUNCATED]" : "",
916                           oid2_name, overflow2 ? " [TRUNCATED]" : ""));
917                 free(oid2_name);
918                 free(oid1_name);
919             }
920 
921             SnmpUtilOidFree(&win_varbinds.list[0].name);
922             memset(&win_varbinds.list[0].name, 0,
923                    sizeof(win_varbinds.list[0].name));
924             copy_oid_to_new_windows_oid(&win_varbinds.list[0].name,
925                                         reginfo->rootoid,
926                                         reginfo->rootoid_len);
927         }
928 
929         if (ext_dll_info->pfSnmpExtensionQueryEx) {
930             result = ext_dll_info->pfSnmpExtensionQueryEx(nRequestType,
931                                                           1,
932                                                           &win_varbinds,
933                                                           get_context_info(request->index),
934                                                           &ErrorStatus,
935                                                           &ErrorIndex);
936         } else if (ext_dll_info->pfSnmpExtensionQuery) {
937             result =
938                 ext_dll_info->pfSnmpExtensionQuery((BYTE) nRequestType,
939                                                    &win_varbinds,
940                                                    &ErrorStatus,
941                                                    &ErrorIndex);
942         } else {
943             snmp_log(LOG_ERR,
944                      "error in extension DLL %s: SNMP query function missing.\n",
945                      ext_dll_info->dll_name);
946             result = FALSE;
947         }
948 
949         if (!result) {
950             snmp_log(LOG_ERR,
951                      "extension DLL %s: SNMP query function failed.\n",
952                      ext_dll_info->dll_name);
953             rc = SNMP_ERR_GENERR;
954             goto free_win_varbinds;
955         }
956 
957         rc = convert_win_snmp_err(ErrorStatus);
958         if (rc != SNMP_ERR_NOERROR) {
959             DEBUGIF("winExtDLL") {
960                 size_t          oid_namelen = 0, outlen = 0;
961                 char           *oid_name = NULL;
962                 int             overflow = 0;
963 
964                 netsnmp_sprint_realloc_objid((u_char **) & oid_name,
965                                              &oid_namelen,
966                                              &outlen, 1, &overflow,
967                                              ext_dll_view_info->name,
968                                              ext_dll_view_info->name_length);
969                 DEBUGMSG(("winExtDLL", "extension DLL %s: SNMP query function"
970                           " returned error code %lu (Windows) / %d (Net-SNMP)"
971                           " for request type %d, OID %s%s, ASN type %d and"
972                           " value %ld.\n",
973                           ext_dll_info->dll_name, ErrorStatus, rc, nRequestType,
974                           oid_name, overflow ? " [TRUNCATED]" : "",
975                           win_varbinds.list[0].value.asnType,
976                           win_varbinds.list[0].value.asnValue.number));
977                 free(oid_name);
978             }
979             netsnmp_assert(ErrorIndex == 1);
980             netsnmp_request_set_error(requests, rc);
981             if (rc == SNMP_NOSUCHOBJECT || rc == SNMP_NOSUCHINSTANCE
982                 || rc == SNMP_ERR_NOSUCHNAME)
983                 rc = SNMP_ERR_NOERROR;
984             goto free_win_varbinds;
985         }
986 
987         copy_value = FALSE;
988         if (reqinfo->mode == MODE_GET)
989             copy_value = TRUE;
990         else if (reqinfo->mode == MODE_GETNEXT) {
991             const SnmpVarBind *win_varbind;
992 
993             win_varbind = &win_varbinds.list[0];
994 
995             /*
996              * Verify whether the OID returned by the extension DLL fits
997              * inside the OID range this handler has been registered
998              * with. Also compare the OID passed to the extension DLL with
999              * the OID returned by the same DLL. If the DLL returned a
1000              * lexicographically earlier OID, this means that there is no
1001              * next OID in the MIB implemented by the DLL.
1002              *
1003              * Note: for some GetNext requests BoundsChecker will report
1004              * that the code below accesses a dangling pointer. This is
1005              * a limitation of BoundsChecker: apparently BoundsChecker is
1006              * not able to cope with reallocation of memory for
1007              * win_varbind by an SNMP extension DLL that has not been
1008              * instrumented by BoundsChecker.
1009              */
1010             if (netsnmp_oid_is_subtree_n_w(ext_dll_view_info->name,
1011                                            ext_dll_view_info->name_length,
1012                                            win_varbind->name.ids,
1013                                            win_varbind->name.idLength) == 0
1014                 && snmp_oid_compare_n_w(varbind->name, varbind->name_length,
1015                                         win_varbind->name.ids,
1016                                         win_varbind->name.idLength) < 0) {
1017                 /*
1018                  * Copy the OID returned by the extension DLL to the
1019                  * Net-SNMP varbind.
1020                  */
1021                 snmp_set_var_objid_w(varbind,
1022                                      win_varbind->name.ids,
1023                                      win_varbind->name.idLength);
1024                 copy_value = TRUE;
1025             }
1026         }
1027         if (copy_value) {
1028             netsnmp_variable_list *result_vb;
1029 
1030             /*
1031              * Copy the value returned by the extension DLL to the Net-SNMP
1032              * varbind.
1033              */
1034             result_vb = NULL;
1035             rc = append_windows_varbind(&result_vb, &win_varbinds.list[0]);
1036             netsnmp_assert(result_vb || rc != SNMP_ERR_NOERROR);
1037             if (result_vb) {
1038                 snmp_set_var_typed_value(varbind,
1039                                          result_vb->type,
1040                                          result_vb->val.string,
1041                                          result_vb->val_len);
1042                 snmp_free_varbind(result_vb);
1043             } else {
1044                 netsnmp_request_set_error(requests, rc);
1045                 goto free_win_varbinds;
1046             }
1047         }
1048 
1049 free_win_varbinds:
1050         if (reqinfo->mode == MODE_SET_COMMIT
1051             || reqinfo->mode == MODE_SET_UNDO
1052             || reqinfo->mode == MODE_SET_FREE)
1053             free_context_info(request->index);
1054         if (win_varbinds.list)
1055             SnmpUtilVarBindListFree(&win_varbinds);
1056     }
1057 
1058     return rc;
1059 }
1060 
1061 /**
1062  * Iterate over the SNMP extension DLL information in the registry and store
1063  * the retrieved information in s_winextdll[].
1064  *
1065  * At the time an SNMP extension DLL is installed, some information about the
1066  * DLL is written to the registry at one of the two following locations:
1067  * HKLM\SYSTEM\CurrentControlSet\Control\SNMP\Parameters\ExtensionAgents for
1068  * Windows Vista, Windows 7 and Windows 2008 or
1069  * HKLM\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ExtensionAgents for
1070  * earlier Windows versions. Under this key zero or more REG_SZ values are
1071  * stored with the names of registry keys containing the DLL path.
1072  */
1073 void
read_extension_dlls_from_registry()1074 read_extension_dlls_from_registry()
1075 {
1076     DEBUGMSGTL(("winExtDLL",
1077                 "read_extension_dlls_from_registry called\n"));
1078 
1079     read_extension_dlls_from_registry_at
1080         ("SYSTEM\\CurrentControlSet\\Services\\SNMP\\Parameters\\ExtensionAgents");
1081     read_extension_dlls_from_registry_at
1082         ("SYSTEM\\CurrentControlSet\\Control\\SNMP\\Parameters\\ExtensionAgents");
1083 }
1084 
1085 void
read_extension_dlls_from_registry_at(const char * const subkey)1086 read_extension_dlls_from_registry_at(const char *const subkey)
1087 {
1088     DWORD           retCode;
1089     HKEY            hKey;
1090     int             i;
1091     DWORD           valueSize;
1092     TCHAR           valueName[MAX_VALUE_NAME];
1093     DWORD           dataType;
1094     TCHAR           data[MAX_VALUE_NAME];
1095     DWORD           dataSize;
1096 
1097     retCode = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey,
1098                             0, KEY_QUERY_VALUE, &hKey);
1099 
1100     if (retCode == ERROR_SUCCESS) {
1101         for (i = 0; ; i++) {
1102             valueSize = sizeof(valueName);
1103             dataSize = sizeof(data);
1104             retCode = RegEnumValue(hKey, i, valueName, &valueSize, NULL,
1105                                    &dataType, (BYTE *) data, &dataSize);
1106 
1107             if (retCode != ERROR_SUCCESS)
1108                 break;
1109             if (dataType == REG_SZ) {
1110                 winextdll       ext_dll_info;
1111 
1112                 memset(&ext_dll_info, 0, sizeof(ext_dll_info));
1113                 ext_dll_info.dll_name =
1114                     read_extension_dll_path_from_registry(data);
1115                 if (ext_dll_info.dll_name) {
1116                     xarray_push_back(&s_winextdll, &ext_dll_info);
1117                     DEBUGMSG(("winExtDLL", "registry key %s: DLL %s.\n",
1118                               data, ext_dll_info.dll_name));
1119                 }
1120             }
1121         }
1122         RegCloseKey(hKey);
1123     }
1124 }
1125 
1126 /** Store the DLL path in dynamically allocated memory. */
1127 char           *
read_extension_dll_path_from_registry(const TCHAR * keyName)1128 read_extension_dll_path_from_registry(const TCHAR * keyName)
1129 {
1130     HKEY            hKey;
1131     DWORD           key_value_type = 0;
1132     TCHAR           valueName[MAX_VALUE_NAME];
1133     DWORD           key_value_size = MAX_VALUE_NAME;
1134     TCHAR           valueNameExpanded[MAX_VALUE_NAME];
1135     DWORD           retCode;
1136     char           *result = 0;
1137 
1138     retCode = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1139                             keyName, 0, KEY_QUERY_VALUE, &hKey);
1140 
1141     if (retCode != ERROR_SUCCESS)
1142         return 0;
1143 
1144     retCode = RegQueryValueExA(hKey,
1145                                "Pathname",
1146                                NULL,
1147                                &key_value_type,
1148                                (BYTE *) valueName, &key_value_size);
1149 
1150     if (retCode != ERROR_SUCCESS) {
1151         RegCloseKey(hKey);
1152         return 0;
1153     }
1154 
1155     if (key_value_type == REG_EXPAND_SZ) {
1156         if (ExpandEnvironmentStrings
1157             (valueName, valueNameExpanded, MAX_VALUE_NAME))
1158             result = strdup(valueNameExpanded);
1159     } else if (key_value_type == REG_SZ)
1160         result = strdup(valueName);
1161 
1162     RegCloseKey(hKey);
1163     return result;
1164 }
1165 
1166 /**
1167  * Callback function called by the Net-SNMP agent to check for traps waiting
1168  * to be processed.
1169  */
1170 void
subagentTrapCheck(unsigned int clientreg,void * clientarg)1171 subagentTrapCheck(unsigned int clientreg, void *clientarg)
1172 {
1173     while (1) {
1174         DWORD           dwWaitResult;
1175         BOOL            bResult;
1176         int             i;
1177         int             j;
1178         const winextdll *ext_dll_info;
1179 
1180         if (s_trapevent.size == 0)
1181             return;
1182 
1183         dwWaitResult = WaitForMultipleObjects(s_trapevent.size,
1184                                               &TRAPEVENT(0), FALSE, 0);
1185 
1186         i = dwWaitResult - WAIT_OBJECT_0;
1187         if (i < 0 || i >= s_trapevent.size) {
1188             netsnmp_assert(dwWaitResult == WAIT_TIMEOUT);
1189             return;
1190         }
1191 
1192         netsnmp_assert(s_trapevent.size == s_trapevent_to_dllinfo.size);
1193         ext_dll_info = TRAPEVENT_TO_DLLINFO(i);
1194         netsnmp_assert(ext_dll_info->subagentTrapEvent == TRAPEVENT(i));
1195 
1196         /*
1197          * Reset the signalled event just in case the extension DLL erroneously
1198          * allocated a manual-reset event instead of an auto-reset event. It is
1199          * important to reset the event BEFORE traps are processed, otherwise a
1200          * race condition is triggered between the extension DLL setting the
1201          * event and this code resetting the event.
1202          */
1203         ResetEvent(TRAPEVENT(i));
1204 
1205         if (!ext_dll_info->pfSnmpExtensionTrap) {
1206             snmp_log(LOG_ERR,
1207                      "internal error in SNMP extension DLL %s: a trap is ready"
1208                      " but the function SnmpExtensionTrap() is missing.\n",
1209                      ext_dll_info->dll_name);
1210             return;
1211         }
1212 
1213         /*
1214          * Process at most hundred traps per extension DLL. If the extension DLL
1215          * has more traps waiting, that's probably a bug in the extension DLL.
1216          */
1217         for (j = 0; j < 100; j++) {
1218             AsnObjectIdentifier Enterprise = { 0, NULL };
1219             AsnInteger      GenericTrap = 0;
1220             AsnInteger      SpecificTrap = 0;
1221             AsnTimeticks    TimeStamp = 0;
1222             SnmpVarBindList TrapVarbinds = { NULL, 0 };
1223 
1224             bResult = ext_dll_info->pfSnmpExtensionTrap(&Enterprise,
1225                                                         &GenericTrap,
1226                                                         &SpecificTrap,
1227                                                         &TimeStamp,
1228                                                         &TrapVarbinds);
1229 
1230             if (!bResult)
1231                 break;
1232 
1233             send_trap(&Enterprise, GenericTrap, SpecificTrap, TimeStamp,
1234                       &TrapVarbinds);
1235 
1236             SnmpUtilVarBindListFree(&TrapVarbinds);
1237         }
1238     }
1239 }
1240 
1241 void
send_trap(const AsnObjectIdentifier * const pEnterprise,const AsnInteger GenericTrap,const AsnInteger SpecificTrap,const AsnTimeticks TimeStamp,const SnmpVarBindList * const pTrapVarbinds)1242 send_trap(const AsnObjectIdentifier * const pEnterprise,
1243           const AsnInteger GenericTrap,
1244           const AsnInteger SpecificTrap,
1245           const AsnTimeticks TimeStamp,
1246           const SnmpVarBindList * const pTrapVarbinds)
1247 {
1248     /*
1249      * A quote from the paragraph in RFC 1908 about SNMPv1 to SNMPv2c
1250      * trap translation (http://www.ietf.org/rfc/rfc1908.txt):
1251      * <quote>
1252      * If a Trap-PDU is received, then it is mapped into a SNMPv2-Trap-
1253      * PDU.  This is done by prepending onto the variable-bindings field
1254      * two new bindings:  sysUpTime.0 [6], which takes its value from the
1255      * timestamp field of the Trap-PDU; and, snmpTrapOID.0 [6], which is
1256      * calculated thusly:  if the value of generic-trap field is
1257      * `enterpriseSpecific', then the value used is the concatenation of
1258      * the enterprise field from the Trap-PDU with two additional sub-
1259      * identifiers, `0', and the value of the specific-trap field;
1260      * otherwise, the value of the corresponding trap defined in [6] is
1261      * used.
1262      * </quote>
1263      *
1264      * Reference [6] refers to RFC 1907 (http://www.ietf.org/rfc/rfc1907.txt),
1265      * where the generic trap OIDs have been defined as follows:
1266      * coldStart             ::= { snmpTraps 1 }
1267      * warmStart             ::= { snmpTraps 2 }
1268      * linkDown              ::= { snmpTraps 3 }
1269      * linkUp                ::= { snmpTraps 4 }
1270      * authenticationFailure ::= { snmpTraps 5 }
1271      * egpNeighborLoss       ::= { snmpTraps 6 }
1272      */
1273     static const oid sysuptime_oid[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
1274     static const size_t sysuptime_oid_len = OID_LENGTH(sysuptime_oid);
1275 
1276     static const oid snmptrap_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
1277     static const size_t snmptrap_oid_len = OID_LENGTH(snmptrap_oid);
1278 
1279     static const oid snmptraps_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 };
1280     static const size_t snmptraps_oid_len = OID_LENGTH(snmptraps_oid);
1281 
1282     oid             vb2_oid[MAX_OID_LEN];
1283     size_t          vb2_oid_len;
1284 
1285     netsnmp_variable_list *notification_vars = NULL;
1286 
1287 
1288     /*
1289      * Append the varbind (sysUpTime.0, TimeStamp).
1290      */
1291     snmp_varlist_add_variable(&notification_vars,
1292                               sysuptime_oid, sysuptime_oid_len,
1293                               ASN_TIMETICKS,
1294                               (const u_char *) &TimeStamp,
1295                               sizeof(TimeStamp));
1296 
1297     if (GenericTrap == SNMP_GENERICTRAP_ENTERSPECIFIC) {
1298         /*
1299          * Enterprise specific trap: compute the OID
1300          * *pEnterprise + ".0." + SpecificTrap.
1301          */
1302         copy_oid_n_w(vb2_oid, &vb2_oid_len,
1303                      pEnterprise->ids, pEnterprise->idLength);
1304         vb2_oid[vb2_oid_len++] = 0;
1305         vb2_oid[vb2_oid_len++] = SpecificTrap;
1306     } else {
1307         /*
1308          * Generic trap: compute the OID snmpTraps + "." + GenericTrap.
1309          * Since the GenericTrap values are those defined in SNMPv1, since
1310          * these values start at zero, and since the corresponding values in
1311          * SNMPv2 start at one, translate the GenericTrap value accordingly.
1312          * See also http://www.ietf.org/rfc/rfc1214.txt and
1313          * http://www.ietf.org/rfc/rfc3418.txt.
1314          */
1315         copy_oid(vb2_oid, &vb2_oid_len, snmptraps_oid, snmptraps_oid_len);
1316         vb2_oid[vb2_oid_len++] = GenericTrap + 1;
1317     }
1318 
1319     /*
1320      * Append the varbind (snmpTrap, vb2_oid).
1321      */
1322     snmp_varlist_add_variable(&notification_vars,
1323                               snmptrap_oid, snmptrap_oid_len,
1324                               ASN_OBJECT_ID,
1325                               (u_char *) vb2_oid,
1326                               vb2_oid_len * sizeof(vb2_oid[0]));
1327 
1328     /*
1329      * Append all the varbinds in pTrapVarbinds.
1330      */
1331     append_windows_varbind_list(&notification_vars, pTrapVarbinds);
1332 
1333     /*
1334      * Send trap.
1335      */
1336     send_v2trap(notification_vars);
1337 
1338     /*
1339      * Free the memory allocated for notification_vars.
1340      */
1341     snmp_free_varbind(notification_vars);
1342 }
1343 
1344 /**
1345  * Convert a Windows varbind to a Net-SNMP varbind and add it to the list of
1346  * varbinds 'net_snmp_varbinds'.
1347  *
1348  * @note The memory allocated inside this function must be freed by the caller
1349  *   as follows: snmp_free_varbind(*net_snmp_varbinds).
1350  */
1351 static int
append_windows_varbind_list(netsnmp_variable_list ** const net_snmp_varbinds,const SnmpVarBindList * const win_varbinds)1352 append_windows_varbind_list(netsnmp_variable_list **
1353                             const net_snmp_varbinds,
1354                             const SnmpVarBindList * const win_varbinds)
1355 {
1356     int             i, status = SNMP_ERR_NOERROR;
1357 
1358     for (i = 0; i < win_varbinds->len; i++) {
1359         status =
1360             append_windows_varbind(net_snmp_varbinds,
1361                                    &win_varbinds->list[i]);
1362         if (status != SNMP_ERR_NOERROR)
1363             break;
1364     }
1365     return status;
1366 }
1367 
1368 static int
append_windows_varbind(netsnmp_variable_list ** const net_snmp_varbinds,const SnmpVarBind * const win_varbind)1369 append_windows_varbind(netsnmp_variable_list ** const net_snmp_varbinds,
1370                        const SnmpVarBind * const win_varbind)
1371 {
1372     switch (win_varbind->value.asnType) {
1373     case MS_ASN_INTEGER:
1374         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1375                                     win_varbind->name.idLength,
1376                                     ASN_INTEGER,
1377                                     &win_varbind->value.asnValue.number,
1378                                     sizeof(win_varbind->value.asnValue.
1379                                            number));
1380         break;
1381     case MS_ASN_BITS:
1382         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1383                                     win_varbind->name.idLength,
1384                                     ASN_BIT_STR,
1385                                     win_varbind->value.asnValue.bits.stream,
1386                                     win_varbind->value.asnValue.bits.length);
1387         break;
1388     case MS_ASN_OCTETSTRING:
1389         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1390                                     win_varbind->name.idLength,
1391                                     ASN_OCTET_STR,
1392                                     win_varbind->value.asnValue.string.
1393                                     stream,
1394                                     win_varbind->value.asnValue.string.
1395                                     length);
1396         break;
1397     case MS_ASN_NULL:
1398         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1399                                     win_varbind->name.idLength,
1400                                     ASN_NULL, 0, 0);
1401         break;
1402     case MS_ASN_OBJECTIDENTIFIER:
1403         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1404                                     win_varbind->name.idLength,
1405                                     ASN_OBJECT_ID,
1406                                     win_varbind->value.asnValue.
1407                                     object.ids,
1408                                     win_varbind->value.asnValue.object.
1409                                     idLength * sizeof(oid));
1410         break;
1411 
1412         /*
1413          * MS_ASN_INTEGER32: synonym for MS_ASN_INTEGER.
1414          */
1415 
1416     case MS_ASN_SEQUENCE:
1417         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1418                                     win_varbind->name.idLength,
1419                                     ASN_SEQUENCE,
1420                                     win_varbind->value.asnValue.sequence.
1421                                     stream,
1422                                     win_varbind->value.asnValue.sequence.
1423                                     length);
1424         break;
1425     case MS_ASN_IPADDRESS:
1426         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1427                                     win_varbind->name.idLength,
1428                                     ASN_IPADDRESS,
1429                                     win_varbind->value.asnValue.address.
1430                                     stream,
1431                                     win_varbind->value.asnValue.address.
1432                                     length);
1433         break;
1434     case MS_ASN_COUNTER32:
1435         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1436                                     win_varbind->name.idLength,
1437                                     ASN_COUNTER,
1438                                     &win_varbind->value.asnValue.counter,
1439                                     sizeof(win_varbind->value.asnValue.
1440                                            counter));
1441         break;
1442     case MS_ASN_GAUGE32:
1443         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1444                                     win_varbind->name.idLength,
1445                                     ASN_GAUGE,
1446                                     &win_varbind->value.asnValue.gauge,
1447                                     sizeof(win_varbind->value.asnValue.
1448                                            gauge));
1449         break;
1450     case MS_ASN_TIMETICKS:
1451         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1452                                     win_varbind->name.idLength,
1453                                     ASN_TIMETICKS,
1454                                     &win_varbind->value.asnValue.ticks,
1455                                     sizeof(win_varbind->value.asnValue.
1456                                            ticks));
1457         break;
1458     case MS_ASN_OPAQUE:        // AsnOctetString
1459         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1460                                     win_varbind->name.idLength,
1461                                     ASN_OPAQUE,
1462                                     win_varbind->value.asnValue.arbitrary.
1463                                     stream,
1464                                     win_varbind->value.asnValue.arbitrary.
1465                                     length);
1466         break;
1467     case MS_ASN_COUNTER64:
1468         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1469                                     win_varbind->name.idLength,
1470                                     ASN_COUNTER64,
1471                                     &win_varbind->value.asnValue.counter64,
1472                                     sizeof(win_varbind->value.asnValue.
1473                                            counter64));
1474         break;
1475     case MS_ASN_UINTEGER32:
1476         snmp_varlist_add_variable_w(net_snmp_varbinds, win_varbind->name.ids,
1477                                     win_varbind->name.idLength,
1478                                     ASN_UNSIGNED,
1479                                     &win_varbind->value.asnValue.unsigned32,
1480                                     sizeof(win_varbind->value.asnValue.
1481                                            unsigned32));
1482         break;
1483     default:
1484         return SNMP_ERR_GENERR;
1485     }
1486 
1487     return SNMP_ERR_NOERROR;
1488 }
1489 
1490 static int
snmp_set_var_objid_w(netsnmp_variable_list * var,const UINT * name,UINT name_length)1491 snmp_set_var_objid_w(netsnmp_variable_list * var, const UINT * name,
1492                      UINT name_length)
1493 {
1494     netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
1495     return snmp_set_var_objid(var, (const oid *) name, name_length);
1496 }
1497 
1498 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)1499 snmp_varlist_add_variable_w(netsnmp_variable_list ** varlist, const UINT * name,
1500                             UINT name_length, u_char type, const void * value,
1501                             size_t len)
1502 {
1503     netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
1504     return snmp_varlist_add_variable(varlist, (const oid *) name, name_length, type,
1505                                      value, len);
1506 }
1507 
1508 /**
1509  * Convert a Net-SNMP varbind to a WinSNMP varbind list.
1510  *
1511  * @param[out] pVarBindList WinSNMP varbind list, initialized by this
1512  *               function.
1513  * @param[in]  varbind Net-SNMP varbind.
1514  */
1515 int
convert_to_windows_varbind_list(SnmpVarBindList * pVarBindList,netsnmp_variable_list * varbind)1516 convert_to_windows_varbind_list(SnmpVarBindList * pVarBindList,
1517                                 netsnmp_variable_list * varbind)
1518 {
1519     SnmpVarBind    *win_varbind;
1520 
1521     netsnmp_assert(pVarBindList);
1522     netsnmp_assert(varbind);
1523 
1524     pVarBindList->len = 1;
1525     pVarBindList->list
1526         = (SnmpVarBind *) SnmpUtilMemAlloc(pVarBindList->len
1527                                            *
1528                                            sizeof(pVarBindList->list[0]));
1529     if (pVarBindList->list == 0)
1530         goto generr;
1531 
1532     memset(&pVarBindList->list[0], 0, sizeof(pVarBindList->list[0]));
1533 
1534     win_varbind = &pVarBindList->list[0];
1535 
1536     if (varbind->name
1537         && !copy_oid_to_new_windows_oid(&win_varbind->name,
1538                                         varbind->name,
1539                                         varbind->name_length))
1540         goto generr;
1541 
1542     switch (varbind->type) {
1543     case ASN_BOOLEAN:
1544         // There is no equivalent type in Microsoft's <snmp.h>.
1545         netsnmp_assert(0);
1546         win_varbind->value.asnType = MS_ASN_INTEGER;
1547         win_varbind->value.asnValue.number = *(varbind->val.integer);
1548         break;
1549     case ASN_INTEGER:
1550         win_varbind->value.asnType = MS_ASN_INTEGER;
1551         win_varbind->value.asnValue.number = *(varbind->val.integer);
1552         break;
1553     case ASN_BIT_STR:
1554         win_varbind->value.asnType = MS_ASN_BITS;
1555         win_varbind->value.asnValue.string.stream
1556             = winsnmp_memdup(varbind->val.string, varbind->val_len);
1557         win_varbind->value.asnValue.string.length =
1558             (UINT) (varbind->val_len);
1559         win_varbind->value.asnValue.string.dynamic = TRUE;
1560         break;
1561     case ASN_OCTET_STR:
1562         win_varbind->value.asnType = MS_ASN_OCTETSTRING;
1563         win_varbind->value.asnValue.string.stream
1564             = winsnmp_memdup(varbind->val.string, varbind->val_len);
1565         win_varbind->value.asnValue.string.length =
1566             (UINT) (varbind->val_len);
1567         win_varbind->value.asnValue.string.dynamic = TRUE;
1568         break;
1569     case ASN_NULL:
1570         win_varbind->value.asnType = MS_ASN_NULL;
1571         memset(&win_varbind->value, 0, sizeof(win_varbind->value));
1572         break;
1573     case ASN_OBJECT_ID:
1574         win_varbind->value.asnType = MS_ASN_OBJECTIDENTIFIER;
1575         if (!copy_oid_to_new_windows_oid
1576             (&win_varbind->value.asnValue.object, varbind->val.objid,
1577              varbind->val_len / sizeof(varbind->val.objid[0])))
1578             return SNMP_ERR_GENERR;
1579         break;
1580     case ASN_SEQUENCE:
1581         win_varbind->value.asnType = MS_ASN_SEQUENCE;
1582         win_varbind->value.asnValue.string.stream
1583             = winsnmp_memdup(varbind->val.string, varbind->val_len);
1584         win_varbind->value.asnValue.string.length =
1585             (UINT) (varbind->val_len);
1586         win_varbind->value.asnValue.string.dynamic = TRUE;
1587         break;
1588     case ASN_SET:
1589         // There is no equivalent type in Microsoft's <snmp.h>.
1590         netsnmp_assert(0);
1591         win_varbind->value.asnType = MS_ASN_INTEGER;
1592         win_varbind->value.asnValue.number = *(varbind->val.integer);
1593         break;
1594     case ASN_IPADDRESS:
1595         win_varbind->value.asnType = MS_ASN_IPADDRESS;
1596         win_varbind->value.asnValue.string.stream
1597             = winsnmp_memdup(varbind->val.string, varbind->val_len);
1598         win_varbind->value.asnValue.string.length =
1599             (UINT) (varbind->val_len);
1600         win_varbind->value.asnValue.string.dynamic = TRUE;
1601         break;
1602     case ASN_COUNTER:
1603         win_varbind->value.asnType = MS_ASN_COUNTER32;
1604         win_varbind->value.asnValue.counter = *(varbind->val.integer);
1605         break;
1606         /*
1607          * ASN_GAUGE == ASN_UNSIGNED
1608          */
1609     case ASN_UNSIGNED:
1610         win_varbind->value.asnType = MS_ASN_UNSIGNED32;
1611         win_varbind->value.asnValue.unsigned32 = *(varbind->val.integer);
1612         break;
1613     case ASN_TIMETICKS:
1614         win_varbind->value.asnType = MS_ASN_TIMETICKS;
1615         win_varbind->value.asnValue.ticks = *(varbind->val.integer);
1616         break;
1617     case ASN_OPAQUE:
1618         win_varbind->value.asnType = MS_ASN_OPAQUE;
1619         win_varbind->value.asnValue.string.stream
1620             = winsnmp_memdup(varbind->val.string, varbind->val_len);
1621         win_varbind->value.asnValue.string.length =
1622             (UINT) (varbind->val_len);
1623         win_varbind->value.asnValue.string.dynamic = TRUE;
1624         break;
1625     case ASN_COUNTER64:
1626         win_varbind->value.asnType = MS_ASN_COUNTER64;
1627         win_varbind->value.asnValue.counter64.HighPart
1628             = varbind->val.counter64->high;
1629         win_varbind->value.asnValue.counter64.LowPart
1630             = varbind->val.counter64->low;
1631         break;
1632     default:
1633         netsnmp_assert(0);
1634         goto generr;
1635     }
1636 
1637     return SNMP_ERR_NOERROR;
1638 
1639   generr:
1640     SnmpUtilVarBindListFree(pVarBindList);
1641     memset(pVarBindList, 0, sizeof(*pVarBindList));
1642     return SNMP_ERR_GENERR;
1643 }
1644 
1645 /** Convert a Windows SNMP error code to the equivalent Net-SNMP error code. */
1646 int
convert_win_snmp_err(const int win_snmp_err)1647 convert_win_snmp_err(const int win_snmp_err)
1648 {
1649     switch (win_snmp_err) {
1650     case SNMP_ERRORSTATUS_NOERROR:
1651         return SNMP_ERR_NOERROR;
1652     case SNMP_ERRORSTATUS_TOOBIG:
1653         return SNMP_ERR_TOOBIG;
1654     case SNMP_ERRORSTATUS_NOSUCHNAME:
1655         /*
1656          * Note: SNMP extension DLLs return SNMP_ERRORSTATUS_NOSUCHNAME
1657          * when either noSuchObject or noSuchInstance should be returned to
1658          * the SNMP manager (assuming SNMPv2c or SNMPv3). Unfortunately it
1659          * is not possible without consulting the MIB to find out whether
1660          * either SNMP_NOSUCHINSTANCE or SNMP_NOSUCHOBJECT should be returned.
1661          * See also RFC 1448.
1662          */
1663         return SNMP_NOSUCHINSTANCE;
1664     case SNMP_ERRORSTATUS_BADVALUE:
1665         return SNMP_ERR_BADVALUE;
1666     case SNMP_ERRORSTATUS_READONLY:
1667         return SNMP_ERR_READONLY;
1668     case SNMP_ERRORSTATUS_GENERR:
1669         return SNMP_ERR_GENERR;
1670     case SNMP_ERRORSTATUS_NOACCESS:
1671         return SNMP_ERR_NOACCESS;
1672     case SNMP_ERRORSTATUS_WRONGTYPE:
1673         return SNMP_ERR_WRONGTYPE;
1674     case SNMP_ERRORSTATUS_WRONGLENGTH:
1675         return SNMP_ERR_WRONGLENGTH;
1676     case SNMP_ERRORSTATUS_WRONGENCODING:
1677         return SNMP_ERR_WRONGENCODING;
1678     case SNMP_ERRORSTATUS_WRONGVALUE:
1679         return SNMP_ERR_WRONGVALUE;
1680     case SNMP_ERRORSTATUS_NOCREATION:
1681         return SNMP_ERR_NOCREATION;
1682     case SNMP_ERRORSTATUS_INCONSISTENTVALUE:
1683         return SNMP_ERR_INCONSISTENTVALUE;
1684     case SNMP_ERRORSTATUS_RESOURCEUNAVAILABLE:
1685         return SNMP_ERR_RESOURCEUNAVAILABLE;
1686     case SNMP_ERRORSTATUS_COMMITFAILED:
1687         return SNMP_ERR_COMMITFAILED;
1688     case SNMP_ERRORSTATUS_UNDOFAILED:
1689         return SNMP_ERR_UNDOFAILED;
1690     case SNMP_ERRORSTATUS_AUTHORIZATIONERROR:
1691         return SNMP_ERR_AUTHORIZATIONERROR;
1692     case SNMP_ERRORSTATUS_NOTWRITABLE:
1693         return SNMP_ERR_NOTWRITABLE;
1694     case SNMP_ERRORSTATUS_INCONSISTENTNAME:
1695         return SNMP_ERR_INCONSISTENTNAME;
1696     }
1697     netsnmp_assert(0);
1698     return SNMP_ERR_GENERR;
1699 }
1700 
1701 /**
1702  * Look up the extension DLL view that was registered with the given OID.
1703  */
1704 static winextdll_view *
lookup_view_by_oid(oid * const name,const size_t name_len)1705 lookup_view_by_oid(oid * const name, const size_t name_len)
1706 {
1707     int             i;
1708 
1709     for (i = 0; i < s_winextdll_view.size; i++) {
1710         if (netsnmp_oid_equals(WINEXTDLL_VIEW(i).name,
1711                                WINEXTDLL_VIEW(i).name_length,
1712                                name, name_len) == 0
1713             && WINEXTDLL_VIEW(i).my_handler) {
1714             return &WINEXTDLL_VIEW(i);
1715         }
1716     }
1717 
1718     return NULL;
1719 }
1720 
1721 static int
snmp_oid_compare_n_w(const oid * name1,size_t len1,const UINT * name2,UINT len2)1722 snmp_oid_compare_n_w(const oid * name1, size_t len1, const UINT * name2,
1723                      UINT len2)
1724 {
1725     netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
1726     return snmp_oid_compare(name1, len1, (const oid *) name2, len2);
1727 }
1728 
1729 static int
snmp_oid_compare_w_n(const UINT * name1,UINT len1,const oid * name2,size_t len2)1730 snmp_oid_compare_w_n(const UINT * name1, UINT len1, const oid * name2,
1731                      size_t len2)
1732 {
1733     netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
1734     return snmp_oid_compare((const oid *) name1, len1, name2, len2);
1735 }
1736 
1737 static int
netsnmp_oid_is_subtree_n_w(const oid * name1,size_t len1,const UINT * name2,UINT len2)1738 netsnmp_oid_is_subtree_n_w(const oid * name1, size_t len1, const UINT * name2,
1739                            UINT len2)
1740 {
1741     netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
1742     return netsnmp_oid_is_subtree(name1, len1, (const oid *) name2, len2);
1743 }
1744 
1745 /**
1746  * Copy an OID.
1747  *
1748  * @param[out] to_name       Number of elements written to destination OID.
1749  * @param[out] to_name_len   Length of destination OID. Must have at least
1750  *                           min(from_name_len, MAX_OID_LEN) elements.
1751  * @param[in]  from_name     Original OID.
1752  * @param[in]  from_name_len Length of original OID.
1753  */
1754 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)1755 copy_oid(oid * const to_name, size_t * const to_name_len,
1756          const oid * const from_name, const size_t from_name_len)
1757 {
1758     int             j;
1759 
1760     netsnmp_assert(to_name);
1761     netsnmp_assert(to_name_len);
1762     netsnmp_assert(from_name);
1763 
1764     for (j = 0; j < from_name_len && j < MAX_OID_LEN; j++)
1765         to_name[j] = from_name[j];
1766 
1767     *to_name_len = j;
1768 }
1769 
1770 /**
1771  * Copy an OID.
1772  *
1773  * @param[out] to_name       Number of elements written to destination OID.
1774  * @param[out] to_name_len   Length of destination OID. Must have at least
1775  *                           min(from_name_len, MAX_OID_LEN) elements.
1776  * @param[in]  from_name     Original OID.
1777  * @param[in]  from_name_len Length of original OID.
1778  */
1779 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)1780 copy_oid_n_w(oid * const to_name, size_t * const to_name_len,
1781              const UINT * const from_name, const UINT from_name_len)
1782 {
1783     netsnmp_static_assert(sizeof(oid) == sizeof(UINT));
1784     copy_oid(to_name, to_name_len, (const oid *) from_name, from_name_len);
1785 }
1786 
1787 /**
1788  * Convert a Net-SNMP OID into a Windows OID and allocate memory for the
1789  * Windows OID.
1790  *
1791  * @param[out] windows_oid   Pointer to a AsnObjectIdentifier.
1792  * @param[in]  name   Pointer to an array with elements of type oid
1793  *           and length name_len.
1794  * @param[in]  name_len Number of elements of input and output OID.
1795  */
1796 static UINT    *
copy_oid_to_new_windows_oid(AsnObjectIdentifier * const windows_oid,const oid * const name,const size_t name_len)1797 copy_oid_to_new_windows_oid(AsnObjectIdentifier * const windows_oid,
1798                             const oid * const name, const size_t name_len)
1799 {
1800     netsnmp_assert(windows_oid);
1801     netsnmp_assert(windows_oid->ids == 0);
1802     netsnmp_assert(windows_oid->idLength == 0);
1803     netsnmp_assert(name);
1804 
1805     windows_oid->ids
1806         =
1807         (UINT *) winsnmp_memdup(name,
1808                                 sizeof(windows_oid->ids[0]) * name_len);
1809     windows_oid->idLength = (UINT) name_len;
1810     return windows_oid->ids;
1811 }
1812 
1813 static u_char  *
winsnmp_memdup(const void * src,const size_t len)1814 winsnmp_memdup(const void *src, const size_t len)
1815 {
1816     u_char         *p;
1817 
1818     netsnmp_assert(len == (UINT) len);
1819 
1820     p = SnmpUtilMemAlloc((UINT) len);
1821     if (p)
1822         memcpy(p, src, len);
1823     return p;
1824 }
1825 
1826 #if 0
1827 /** Initialize array 'a'. */
1828 static void
1829 xarray_init(xarray * a, size_t elem_size)
1830 {
1831     netsnmp_assert(a);
1832 
1833     memset(a, 0, sizeof(*a));
1834     a->elem_size = elem_size;
1835 }
1836 #endif
1837 
1838 /** Deallocate any memory that was dynamically allocated for 'a'. */
1839 static void
xarray_destroy(xarray * a)1840 xarray_destroy(xarray * a)
1841 {
1842     netsnmp_assert(a);
1843 
1844     xarray_reserve(a, 0);
1845 }
1846 
1847 /**
1848  * Append the contents of the address range [ elem, elem + a->elem_size [ to a.
1849  *
1850  * Resize a if necessary.
1851  *
1852  * @return A pointer to the address where the data has been copied upon success,
1853  *         or NULL upon failure.
1854  */
1855 static void    *
xarray_push_back(xarray * a,const void * elem)1856 xarray_push_back(xarray * a, const void *elem)
1857 {
1858     netsnmp_assert(a);
1859     netsnmp_assert(elem);
1860     netsnmp_assert(a->size <= a->reserved);
1861 
1862     if (a->size == a->reserved)
1863         xarray_reserve(a, a->reserved == 0 ? 16 : 2 * a->reserved);
1864     if (a->size < a->reserved) {
1865         netsnmp_assert(a->size < a->reserved);
1866         return memcpy((char *) (a->p) + a->elem_size * a->size++, elem,
1867                       a->elem_size);
1868     }
1869     return NULL;
1870 }
1871 
1872 #if 0
1873 /** Erase [ elem, elem + a->elem_size [ from a. */
1874 static void
1875 xarray_erase(xarray * a, void *const elem)
1876 {
1877     netsnmp_assert(a);
1878     netsnmp_assert(a->size >= 1);
1879     netsnmp_assert(a->p <= elem);
1880     netsnmp_assert((const char *) elem + a->elem_size <=
1881            (char *) a->p + a->size * a->elem_size);
1882     netsnmp_assert(((const char *) elem - (char *) a->p) % a->elem_size == 0);
1883 
1884     a->size--;
1885     memmove((char *) elem, (char *) elem + a->elem_size,
1886             a->size - ((const char *) elem -
1887                        (char *) a->p) / a->elem_size);
1888 }
1889 #endif
1890 
1891 /**
1892  * Change the number of allocated elements to 'reserved'.
1893  *
1894  * Can be used either for enlarging or for shrinking the memory allocated for
1895  * 'a'. Does not modify 'a' if memory allocation fails. Newly allocted memory
1896  * is not initialized.
1897  *
1898  * @return != NULL upon success, NULL upon failure.
1899  */
1900 static void    *
xarray_reserve(xarray * a,int reserved)1901 xarray_reserve(xarray * a, int reserved)
1902 {
1903     netsnmp_assert(a);
1904     netsnmp_assert(a->size <= a->reserved);
1905 
1906     if ((a->p = realloc(a->p, a->elem_size * reserved)))
1907         a->reserved = reserved;
1908     else
1909         a->reserved = 0;
1910     return a->p;
1911 }
1912 
1913 #endif                          /* USING_WINEXTDLL_MODULE */
1914