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(¬ification_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(¬ification_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(¬ification_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