Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

990 lines
35 KiB

  1. // snmptrap.c
  2. //
  3. // Original Microsoft code modified by ACE*COMM
  4. // for use with WSNMP32.DLL and other trap receiving
  5. // clients, per contract.
  6. //
  7. // Bob Natale, ACE*COMM ([email protected])
  8. // For NT v5 Beta, v970228
  9. // Additional enhancements planned.
  10. //
  11. // This version of SNMPTRAP has no dependencies
  12. // on either MGMTAPI.DLL or WSNMP32.DLL.
  13. //
  14. // WinSNMP clients use the SnmpRegister() function.
  15. //
  16. // Other clients will need to match the following
  17. // values and structures:
  18. //
  19. // SNMP_TRAP structure
  20. // SNMPTRAPPIPE name
  21. // TRAPBUFSIZE value
  22. //
  23. // Change log:
  24. // ------------------------------------------------
  25. // 4.0.1381.3 Apr 8, 1998 Bob Natale
  26. //
  27. // 1. Re-worked the trap port monitoring thread into
  28. // two threads...one for IP and one for IPX, to
  29. // comply with WinSock v2's restrictions against
  30. // multi-protocol select().
  31. //
  32. // 2. General clean-up/streamlining wrt "legacy" code
  33. // from original MS version...more to do here, esp.
  34. // wrt error handling code that does not do anything.
  35. // ------------------------------------------------
  36. // 4.0.1381.4 Apr. 10, 1998 Bob Natale
  37. //
  38. // 1. Replaced mutex calls with critical_sectin calls.
  39. //
  40. // 2. Cleaned out some dead code (removed commented out code)
  41. // ------------------------------------------------
  42. // Jan. 2, 2001 Frank Li
  43. // 1. remove TerminateThread
  44. // 2. add debug build loggings
  45. // ------------------------------------------------
  46. #include <windows.h>
  47. #include <winsock.h>
  48. #include <wsipx.h>
  49. #include <process.h>
  50. #ifdef DBG // include files for debug trace only
  51. #include <stdio.h>
  52. #include <time.h>
  53. #endif
  54. //--------------------------- PRIVATE VARIABLES -----------------------------
  55. #define SNMPMGRTRAPPIPE "\\\\.\\PIPE\\MGMTAPI"
  56. #define MAX_OUT_BUFS 16
  57. #define TRAPBUFSIZE 4096
  58. #define IP_TRAP_PORT 162
  59. #define IPX_TRAP_PORT 36880
  60. //
  61. // constants added to allocate trap buffer for fixing trap data of length
  62. // > 8192 bytes. Here is the buffer allocation scheme based on the common
  63. // cases that trap data sizes are less than 4-KBytes:
  64. // 1. LargeTrap
  65. // if (trap data size >= 8192 bytes), allocate MAX_UDP_SIZE sized buffer
  66. // 2. MediumTrap
  67. // if (trap data size <= 4096 bytes), allocate FOUR_K_BUF_SIZE sized buffer
  68. // 3. SmallTrap
  69. // if (4096 < trap data size < 8192), allocate just enough buffer size.
  70. // Note:
  71. // - when LargeTrap is received, the allocated buffer will stay for a time of
  72. // MAXUDPLEN_BUFFER_TIME from the last LargeTrap received.
  73. // - Once MediumTrap is received, subsequent SmallTrap will reuse the
  74. // last MediumTrap allocated buffer.
  75. //
  76. #define MAX_UDP_SIZE (65535-8) // max udp len - 8bytes udp header
  77. #define MAX_FIONREAD_UDP_SIZE 8192 // max winsock FIONREAD reported size (8kB)
  78. #define FOUR_K_BUF_SIZE 4096 // buffer of 4-KBytes in size
  79. #define MAXUDPLEN_BUFFER_TIME (2*60*1000) // max. 2 mins to keep the
  80. // last allocated large buffer.
  81. // ******** INITIALIZE A LIST HEAD ********
  82. #define ll_init(head) (head)->next = (head)->prev = (head);
  83. // ******** TEST A LIST FOR EMPTY ********
  84. #define ll_empt(head) ( ((head)->next) == (head) )
  85. // ******** Get ptr to next entry ********
  86. #define ll_next(item,head)\
  87. ( (ll_node *)(item)->next == (head) ? 0 : \
  88. (ll_node *)(item)->next )
  89. // ******** Get ptr to prev entry ********
  90. #define ll_prev(item)\
  91. ( (ll_node *)(item)->prev )
  92. // ******** ADD AN ITEM TO THE END OF A LIST ********
  93. #define ll_adde(item,head)\
  94. {\
  95. ll_node *pred = (head)->prev;\
  96. ((ll_node *)(item))->next = (head);\
  97. ((ll_node *)(item))->prev = pred;\
  98. (pred)->next = ((ll_node *)(item));\
  99. (head)->prev = ((ll_node *)(item));\
  100. }
  101. // ******** REMOVE AN ITEM FROM A LIST ********
  102. #define ll_rmv(item)\
  103. {\
  104. ll_node *pred = ((ll_node *)(item))->prev;\
  105. ll_node *succ = ((ll_node *)(item))->next;\
  106. pred->next = succ;\
  107. succ->prev = pred;\
  108. }
  109. // ******** List head/node ********
  110. typedef struct ll_s
  111. { // linked list structure
  112. struct ll_s *next; // next node
  113. struct ll_s *prev; // prev. node
  114. } ll_node; // linked list node
  115. typedef struct
  116. {// shared by server trap thread and pipe thread
  117. ll_node links;
  118. HANDLE hPipe;
  119. } svrPipeListEntry;
  120. typedef struct
  121. {
  122. SOCKADDR Addr;
  123. int AddrLen;
  124. UINT TrapBufSz;
  125. char TrapBuf[TRAPBUFSIZE]; // the size of this array should match the size of the structure
  126. // defined in wsnmp_no.c!!!
  127. } SNMP_TRAP, *PSNMP_TRAP;
  128. typedef struct
  129. {
  130. SOCKET s;
  131. OVERLAPPED ol;
  132. } TRAP_THRD_CONTEXT, *PTRAP_THRD_CONTEXT;
  133. HANDLE hExitEvent = NULL;
  134. LPCTSTR svcName = "SNMPTRAP";
  135. SERVICE_STATUS_HANDLE hService = 0;
  136. SERVICE_STATUS status =
  137. {SERVICE_WIN32, SERVICE_STOPPED, SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0};
  138. SOCKET ipSock = INVALID_SOCKET;
  139. SOCKET ipxSock = INVALID_SOCKET;
  140. HANDLE ipThread = NULL;
  141. HANDLE ipxThread = NULL;
  142. CRITICAL_SECTION cs_PIPELIST;
  143. ll_node *pSvrPipeListHead = NULL;
  144. // global variables added to remove the TerminateThread call
  145. OVERLAPPED g_ol; // overlapped struct for svrPipeThread
  146. TRAP_THRD_CONTEXT g_ipThreadContext; // context for ip svrTrapThread
  147. TRAP_THRD_CONTEXT g_ipxThreadContext; // context for ipx svrTrapThread
  148. ///////////////////////////////////////////////////////////////////////////////
  149. // //
  150. // SNMPTRAP Debugging Prototypes //
  151. // //
  152. ///////////////////////////////////////////////////////////////////////////////
  153. #if DBG
  154. VOID
  155. WINAPI
  156. SnmpTrapDbgPrint(
  157. IN LPSTR szFormat,
  158. IN ...
  159. );
  160. #define SNMPTRAPDBG(_x_) SnmpTrapDbgPrint _x_
  161. #else
  162. #define SNMPTRAPDBG(_x_)
  163. #endif
  164. //--------------------------- PRIVATE PROTOTYPES ----------------------------
  165. DWORD WINAPI svrTrapThread (IN OUT LPVOID threadParam);
  166. DWORD WINAPI svrPipeThread (IN LPVOID threadParam);
  167. VOID WINAPI svcHandlerFunction (IN DWORD dwControl);
  168. VOID WINAPI svcMainFunction (IN DWORD dwNumServicesArgs,
  169. IN LPSTR *lpServiceArgVectors);
  170. void FreeSvrPipeEntryList(IN ll_node* head);
  171. //--------------------------- PRIVATE PROCEDURES ----------------------------
  172. VOID WINAPI svcHandlerFunction (IN DWORD dwControl)
  173. {
  174. if (dwControl == SERVICE_CONTROL_STOP)
  175. {
  176. status.dwCurrentState = SERVICE_STOP_PENDING;
  177. status.dwCheckPoint++;
  178. status.dwWaitHint = 45000;
  179. if (!SetServiceStatus(hService, &status))
  180. exit(1);
  181. // set event causing trap thread to terminate
  182. if (!SetEvent(hExitEvent))
  183. {
  184. status.dwCurrentState = SERVICE_STOPPED;
  185. status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  186. status.dwServiceSpecificExitCode = 1; // OPENISSUE - svc err code
  187. status.dwCheckPoint = 0;
  188. status.dwWaitHint = 0;
  189. // We are exiting in any case, so ignore any error...
  190. SetServiceStatus (hService, &status);
  191. exit(1);
  192. }
  193. }
  194. else
  195. // dwControl == SERVICE_CONTROL_INTERROGATE
  196. // dwControl == SERVICE_CONTROL_PAUSE
  197. // dwControl == SERVICE_CONTROL_CONTINUE
  198. // dwControl == <anything else>
  199. {
  200. if (status.dwCurrentState == SERVICE_STOP_PENDING ||
  201. status.dwCurrentState == SERVICE_START_PENDING)
  202. status.dwCheckPoint++;
  203. if (!SetServiceStatus (hService, &status))
  204. exit(1);
  205. }
  206. } // end_svcHandlerFunction()
  207. VOID WINAPI svcMainFunction (IN DWORD dwNumServicesArgs,
  208. IN LPSTR *lpServiceArgVectors)
  209. {
  210. WSADATA WinSockData;
  211. HANDLE hPipeThread = NULL;
  212. DWORD dwThreadId;
  213. //---------------------------------------------------------------------
  214. hService = RegisterServiceCtrlHandler (svcName, svcHandlerFunction);
  215. if (hService == 0)
  216. {
  217. status.dwCurrentState = SERVICE_STOPPED;
  218. status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  219. status.dwServiceSpecificExitCode = 2; // OPENISSUE - svc err code
  220. status.dwCheckPoint = 0;
  221. status.dwWaitHint = 0;
  222. // We are exiting in any case, so ignore any error...
  223. SetServiceStatus (hService, &status);
  224. exit(1);
  225. }
  226. status.dwCurrentState = SERVICE_START_PENDING;
  227. status.dwWaitHint = 20000;
  228. if (!SetServiceStatus(hService, &status))
  229. exit(1);
  230. __try
  231. {
  232. InitializeCriticalSection (&cs_PIPELIST);
  233. }
  234. __except(EXCEPTION_EXECUTE_HANDLER)
  235. {
  236. exit(1);
  237. }
  238. memset(&g_ipThreadContext.ol, 0, sizeof(g_ipThreadContext.ol));
  239. memset(&g_ipxThreadContext.ol, 0, sizeof(g_ipxThreadContext.ol));
  240. if (WSAStartup ((WORD)0x0101, &WinSockData))
  241. goto CLOSE_OUT; // WinSock startup failure
  242. // allocate linked-list header for client received traps
  243. if ((pSvrPipeListHead = (ll_node *)GlobalAlloc (GPTR, sizeof(ll_node))) == NULL)
  244. goto CLOSE_OUT;
  245. ll_init(pSvrPipeListHead);
  246. if ((hPipeThread = (HANDLE)_beginthreadex
  247. (NULL, 0, svrPipeThread, NULL, 0, &dwThreadId)) == 0)
  248. goto CLOSE_OUT;
  249. //-----------------------------------------------------------------------------------
  250. //CHECK_IP:
  251. ipSock = socket (AF_INET, SOCK_DGRAM, 0);
  252. if (ipSock != INVALID_SOCKET)
  253. {
  254. struct sockaddr_in localAddress_in;
  255. struct servent *serv;
  256. ZeroMemory (&localAddress_in, sizeof(localAddress_in));
  257. localAddress_in.sin_family = AF_INET;
  258. if ((serv = getservbyname ("snmp-trap", "udp")) == NULL)
  259. localAddress_in.sin_port = htons (IP_TRAP_PORT);
  260. else
  261. localAddress_in.sin_port = (SHORT)serv->s_port;
  262. localAddress_in.sin_addr.s_addr = htonl (INADDR_ANY);
  263. if (bind (ipSock, (LPSOCKADDR)&localAddress_in, sizeof(localAddress_in)) != SOCKET_ERROR)
  264. {
  265. g_ipThreadContext.s = ipSock;
  266. // init the overlapped struct with manual reset non-signaled event
  267. g_ipThreadContext.ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  268. if (NULL == g_ipThreadContext.ol.hEvent)
  269. goto CLOSE_OUT;
  270. ipThread = (HANDLE)_beginthreadex
  271. (NULL, 0, svrTrapThread, (LPVOID)&g_ipThreadContext, 0, &dwThreadId);
  272. }
  273. }
  274. //-----------------------------------------------------------------------------------
  275. //CHECK_IPX:
  276. ipxSock = socket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
  277. if (ipxSock != INVALID_SOCKET)
  278. {
  279. struct sockaddr_ipx localAddress_ipx;
  280. ZeroMemory (&localAddress_ipx, sizeof(localAddress_ipx));
  281. localAddress_ipx.sa_family = AF_IPX;
  282. localAddress_ipx.sa_socket = htons (IPX_TRAP_PORT);
  283. if (bind (ipxSock, (LPSOCKADDR)&localAddress_ipx, sizeof(localAddress_ipx)) != SOCKET_ERROR)
  284. {
  285. g_ipxThreadContext.s = ipxSock;
  286. // init the overlapped struct with manual reset non-signaled event
  287. g_ipxThreadContext.ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  288. if (NULL == g_ipxThreadContext.ol.hEvent)
  289. goto CLOSE_OUT;
  290. ipxThread = (HANDLE)_beginthreadex
  291. (NULL, 0, svrTrapThread, (LPVOID)&g_ipxThreadContext, 0, &dwThreadId);
  292. }
  293. }
  294. //-----------------------------------------------------------------------------------
  295. // We are ready to listen for traps...
  296. status.dwCurrentState = SERVICE_RUNNING;
  297. status.dwCheckPoint = 0;
  298. status.dwWaitHint = 0;
  299. if (!SetServiceStatus(hService, &status))
  300. goto CLOSE_OUT;
  301. WaitForSingleObject (hExitEvent, INFINITE);
  302. //-----------------------------------------------------------------------------------
  303. CLOSE_OUT:
  304. // make sure we can bail out if we are here because of goto statements above
  305. SetEvent(hExitEvent);
  306. if (hPipeThread != NULL)
  307. {
  308. SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ol.hEvent.\n"));
  309. SetEvent(g_ol.hEvent); // signal to terminate the svrPipeThread thread
  310. WaitForSingleObject (hPipeThread, INFINITE);
  311. SNMPTRAPDBG(("svcMainFunction: WaitForSingleObject hPipeThread INFINITE done.\n"));
  312. CloseHandle (hPipeThread);
  313. }
  314. if (ipSock != INVALID_SOCKET)
  315. closesocket (ipSock); // unblock any socket call
  316. if (ipThread != NULL)
  317. {
  318. SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ipThreadContext.ol.hEvent.\n"));
  319. SetEvent(g_ipThreadContext.ol.hEvent); // signal to terminate thread
  320. WaitForSingleObject (ipThread, INFINITE);
  321. CloseHandle (ipThread);
  322. }
  323. if (g_ipThreadContext.ol.hEvent)
  324. CloseHandle(g_ipThreadContext.ol.hEvent);
  325. if (ipxSock != INVALID_SOCKET)
  326. closesocket (ipxSock); // unblock any socket call
  327. if (ipxThread != NULL)
  328. {
  329. SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ipxThreadContext.ol.hEvent.\n"));
  330. SetEvent(g_ipxThreadContext.ol.hEvent); // signal to terminate thread
  331. WaitForSingleObject (ipxThread, INFINITE);
  332. CloseHandle (ipxThread);
  333. }
  334. if (g_ipxThreadContext.ol.hEvent)
  335. CloseHandle(g_ipxThreadContext.ol.hEvent);
  336. EnterCriticalSection (&cs_PIPELIST);
  337. if (pSvrPipeListHead != NULL)
  338. {
  339. FreeSvrPipeEntryList(pSvrPipeListHead);
  340. pSvrPipeListHead = NULL;
  341. }
  342. LeaveCriticalSection (&cs_PIPELIST);
  343. DeleteCriticalSection (&cs_PIPELIST);
  344. WSACleanup();
  345. status.dwCurrentState = SERVICE_STOPPED;
  346. status.dwCheckPoint = 0;
  347. status.dwWaitHint = 0;
  348. if (!SetServiceStatus(hService, &status))
  349. exit(1);
  350. } // end_svcMainFunction()
  351. //--------------------------- PUBLIC PROCEDURES -----------------------------
  352. int __cdecl main ()
  353. {
  354. BOOL fOk;
  355. OSVERSIONINFO osInfo;
  356. SERVICE_TABLE_ENTRY svcStartTable[2] =
  357. {
  358. {(LPTSTR)svcName, svcMainFunction},
  359. {NULL, NULL}
  360. };
  361. osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  362. fOk = GetVersionEx (&osInfo);
  363. if (fOk && (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT))
  364. { // create event to synchronize trap server shutdown
  365. hExitEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
  366. if (NULL == hExitEvent)
  367. {
  368. exit(1);
  369. }
  370. // init the overlapped struct used by svrTrapThread
  371. // with manual reset non-signaled event
  372. memset(&g_ol, 0, sizeof(g_ol));
  373. g_ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  374. if (NULL == g_ol.hEvent)
  375. {
  376. CloseHandle(hExitEvent);
  377. exit(1);
  378. }
  379. // this call will not return until service stopped
  380. fOk = StartServiceCtrlDispatcher (svcStartTable);
  381. CloseHandle (hExitEvent);
  382. CloseHandle(g_ol.hEvent);
  383. }
  384. return fOk;
  385. } // end_main()
  386. //
  387. DWORD WINAPI svrTrapThread (LPVOID threadParam)
  388. // This thread takes a SOCKET from the TRAP_THRD_CONTEXT parameter,
  389. // loops on select()
  390. // for data in-coming over that socket, writing it back
  391. // out to clients over all pipes currently on the list of
  392. // trap notification pipes shared by this thread and the
  393. // pipe thread
  394. {
  395. PSNMP_TRAP pRecvTrap = NULL;
  396. struct fd_set readfds;
  397. PTRAP_THRD_CONTEXT pThreadContext = (PTRAP_THRD_CONTEXT) threadParam;
  398. SOCKET fd = INVALID_SOCKET;
  399. int len;
  400. DWORD dwLastAllocatedUdpDataLen = 0; // the last allocated UDP data buffer size
  401. DWORD dwLastBigBufferRequestTime = 0; // the tick count that the last
  402. // LargeTrap received
  403. BOOL fTimeoutForMaxUdpLenBuffer = FALSE; // need to deallocate the big buffer
  404. //
  405. if (NULL == pThreadContext)
  406. return 0;
  407. fd = pThreadContext->s;
  408. dwLastBigBufferRequestTime = GetTickCount();
  409. while (TRUE)
  410. {
  411. ULONG ulTrapSize = 0;
  412. DWORD dwError = 0;
  413. if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0))
  414. {
  415. SNMPTRAPDBG(("svrTrapThread: exit 0.\n"));
  416. break;
  417. }
  418. // construct readfds which gets destroyed by select()
  419. FD_ZERO(&readfds);
  420. FD_SET(fd, &readfds);
  421. if (select (0, &readfds, NULL, NULL, NULL) == SOCKET_ERROR)
  422. {
  423. SNMPTRAPDBG(("svrTrapThread: select failed %d.\n", WSAGetLastError()));
  424. break; // terminate thread
  425. }
  426. if (!(FD_ISSET(fd, &readfds)))
  427. continue;
  428. if (ioctlsocket(
  429. fd, // socket to query
  430. FIONREAD, // query for the size of the incoming datagram
  431. &ulTrapSize // unsigned long to store the size of the datagram
  432. ) != 0)
  433. {
  434. dwError = WSAGetLastError();
  435. SNMPTRAPDBG((
  436. "ioctlsocket FIONREAD failed: lasterror: 0x%08lx\n",
  437. dwError));
  438. continue; // continue if we could not determine the size of the
  439. // incoming datagram
  440. }
  441. if (ulTrapSize >= MAX_FIONREAD_UDP_SIZE)
  442. {
  443. dwLastBigBufferRequestTime = GetTickCount(); // update tickcount
  444. // the ulTrapSize is not accurat on reporting the size of the
  445. // next UDP datagram message. KB Q192599 and KB Q140263
  446. if ( NULL == pRecvTrap ||
  447. dwLastAllocatedUdpDataLen < MAX_UDP_SIZE )
  448. {
  449. if (pRecvTrap)
  450. {
  451. GlobalFree(pRecvTrap);
  452. pRecvTrap = NULL;
  453. dwLastAllocatedUdpDataLen = 0;
  454. }
  455. SNMPTRAPDBG((
  456. "allocate LargeTrap of size : %d\n",
  457. sizeof(SNMP_TRAP) - TRAPBUFSIZE + MAX_UDP_SIZE));
  458. // allocate for the trap header + max udp size
  459. pRecvTrap = (PSNMP_TRAP)GlobalAlloc(GPTR, (sizeof(SNMP_TRAP) -
  460. TRAPBUFSIZE + MAX_UDP_SIZE));
  461. if (NULL == pRecvTrap)
  462. {
  463. SNMPTRAPDBG(("svrTrapThread: GlobalAlloc failed.\n"));
  464. dwLastAllocatedUdpDataLen = 0;
  465. break;
  466. }
  467. dwLastAllocatedUdpDataLen = MAX_UDP_SIZE;
  468. }
  469. }
  470. else
  471. {
  472. // winsock has reported the exact amount of UDP datagram
  473. // size to be recieved as long as the next datagram is less than
  474. // 8-kbyte
  475. //
  476. // if we've allocated a big buffer before, check to see if we need
  477. // to deallocate it to save the usage of resource.
  478. //
  479. fTimeoutForMaxUdpLenBuffer = FALSE; // reset timeout flag
  480. if (MAX_UDP_SIZE == dwLastAllocatedUdpDataLen)
  481. {
  482. // we've allocated a big buffer before
  483. DWORD dwCurrTime = GetTickCount();
  484. if (dwCurrTime < dwLastBigBufferRequestTime)
  485. {
  486. // wrap around occured. we just simply assume it is time to
  487. // release the big buffer.
  488. fTimeoutForMaxUdpLenBuffer = TRUE;
  489. SNMPTRAPDBG((
  490. "Timeout to free LargeTrap buffer of size %d bytes.\n",
  491. dwLastAllocatedUdpDataLen));
  492. }
  493. else
  494. {
  495. if ( (dwCurrTime-dwLastBigBufferRequestTime) >
  496. MAXUDPLEN_BUFFER_TIME )
  497. {
  498. // after quite a long time, we don't have a large UDP
  499. // datagram received.
  500. fTimeoutForMaxUdpLenBuffer = TRUE;
  501. SNMPTRAPDBG((
  502. "Timeout to free LargeTrap buffer size of %d bytes.\n",
  503. dwLastAllocatedUdpDataLen));
  504. }
  505. }
  506. }
  507. if (pRecvTrap == NULL ||
  508. fTimeoutForMaxUdpLenBuffer ||
  509. dwLastAllocatedUdpDataLen < ulTrapSize)
  510. {
  511. // allocate/reallocate buffer
  512. if (pRecvTrap != NULL)
  513. {
  514. GlobalFree(pRecvTrap);
  515. pRecvTrap = NULL;
  516. dwLastAllocatedUdpDataLen = 0;
  517. }
  518. if (FOUR_K_BUF_SIZE >= ulTrapSize)
  519. {
  520. // allocate at least 4 KBytes buffer to avoid
  521. // re-allocations on different sizes of small trap received
  522. pRecvTrap = (PSNMP_TRAP)GlobalAlloc(GPTR, (sizeof(SNMP_TRAP) -
  523. TRAPBUFSIZE + FOUR_K_BUF_SIZE));
  524. dwLastAllocatedUdpDataLen = FOUR_K_BUF_SIZE;
  525. SNMPTRAPDBG((
  526. "allocate SmallTrap of size : %d\n",
  527. sizeof(SNMP_TRAP) - TRAPBUFSIZE + FOUR_K_BUF_SIZE));
  528. }
  529. else
  530. {
  531. // allocate what is necessary
  532. pRecvTrap = (PSNMP_TRAP)GlobalAlloc(GPTR, (sizeof(SNMP_TRAP) -
  533. TRAPBUFSIZE + ulTrapSize));
  534. dwLastAllocatedUdpDataLen = ulTrapSize;
  535. SNMPTRAPDBG((
  536. "allocate MediumTrap of size : %d\n",
  537. sizeof(SNMP_TRAP) - TRAPBUFSIZE + ulTrapSize));
  538. }
  539. if (NULL == pRecvTrap) // if there is so few memory that we can't allocate a bit ..
  540. { // bail out and stop the SNMPTRAP service (bug? - other option => 100% CPU which is worst)
  541. SNMPTRAPDBG(("svrTrapThread: GlobalAlloc failed.\n"));
  542. dwLastAllocatedUdpDataLen = 0;
  543. break;
  544. }
  545. }
  546. }
  547. pRecvTrap->TrapBufSz = dwLastAllocatedUdpDataLen; // actual buffer size
  548. pRecvTrap->AddrLen = sizeof(pRecvTrap->Addr);
  549. len = recvfrom (
  550. fd,
  551. pRecvTrap->TrapBuf,
  552. pRecvTrap->TrapBufSz,
  553. 0,
  554. &(pRecvTrap->Addr),
  555. &(pRecvTrap->AddrLen));
  556. if (len == SOCKET_ERROR)
  557. {
  558. dwError = WSAGetLastError();
  559. SNMPTRAPDBG((
  560. "recvfrom failed: ulTrapSize: %d bytes, TrapBufSz: %d bytes, lasterror: 0x%08lx\n",
  561. ulTrapSize, pRecvTrap->TrapBufSz, dwError));
  562. continue;
  563. }
  564. EnterCriticalSection (&cs_PIPELIST);
  565. pRecvTrap->TrapBufSz = len; // the acutal trap data len received
  566. // add header to length
  567. len += sizeof(SNMP_TRAP) - sizeof(pRecvTrap->TrapBuf); // - TRAPBUFSIZE
  568. if (!ll_empt(pSvrPipeListHead))
  569. {
  570. DWORD written;
  571. ll_node *item = pSvrPipeListHead;
  572. while (item = ll_next(item, pSvrPipeListHead))
  573. {
  574. if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0))
  575. {
  576. SNMPTRAPDBG(("svrTrapThread: exit 1.\n"));
  577. LeaveCriticalSection (&cs_PIPELIST);
  578. break;
  579. }
  580. if (!WriteFile(
  581. ((svrPipeListEntry *)item)->hPipe,
  582. (LPBYTE)pRecvTrap,
  583. len,
  584. &written,
  585. &pThreadContext->ol))
  586. {
  587. if (ERROR_IO_PENDING == GetLastError())
  588. {
  589. SNMPTRAPDBG(("svrTrapThread: before GetOverlappedResult.\n"));
  590. GetOverlappedResult(
  591. ((svrPipeListEntry *)item)->hPipe,
  592. &pThreadContext->ol,
  593. &written,
  594. TRUE // Block
  595. );
  596. SNMPTRAPDBG(("svrTrapThread: after GetOverlappedResult.\n"));
  597. if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0))
  598. {
  599. SNMPTRAPDBG(("svrTrapThread: exit 2.\n"));
  600. LeaveCriticalSection (&cs_PIPELIST);
  601. break;
  602. }
  603. // reset event to non-signaled state for next I/O
  604. ResetEvent(pThreadContext->ol.hEvent);
  605. }
  606. else
  607. {
  608. ll_node *hold;
  609. if (!DisconnectNamedPipe(((svrPipeListEntry *)item)->hPipe))
  610. {
  611. ; // Placeholder for error handling
  612. }
  613. if (!CloseHandle(((svrPipeListEntry *)item)->hPipe))
  614. {
  615. ; // Placeholder for error handling
  616. }
  617. hold = ll_prev(item);
  618. ll_rmv(item);
  619. GlobalFree(item); // check for errors?
  620. item = hold;
  621. }
  622. } // end_if !WriteFile
  623. else if (written != (DWORD)len)
  624. {
  625. SNMPTRAPDBG(("svrTrapThread: written != len\n"));
  626. ; // Placeholder for error handling
  627. }
  628. } // end_while item = ll_next
  629. } // end_if !ll_empt
  630. LeaveCriticalSection (&cs_PIPELIST);
  631. } // end while TRUE
  632. if (pRecvTrap != NULL)
  633. GlobalFree(pRecvTrap);
  634. return 0;
  635. } // end svrTrapThread()
  636. PACL AllocGenericACL()
  637. {
  638. PACL pAcl;
  639. PSID pSidAdmins, pSidUsers, pSidLocalService;
  640. SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY;
  641. DWORD dwAclLength;
  642. pSidAdmins = pSidUsers = pSidLocalService = NULL;
  643. // Bug# 179644 The SNMP trap service should not run in the LocalSystem account
  644. if ( !AllocateAndInitializeSid( &Authority,
  645. 2,
  646. SECURITY_BUILTIN_DOMAIN_RID,
  647. DOMAIN_ALIAS_RID_ADMINS,
  648. 0, 0, 0, 0, 0, 0,
  649. &pSidAdmins ) ||
  650. !AllocateAndInitializeSid( &Authority,
  651. 2,
  652. SECURITY_BUILTIN_DOMAIN_RID,
  653. DOMAIN_ALIAS_RID_USERS,
  654. 0, 0, 0, 0, 0, 0,
  655. &pSidUsers ) ||
  656. !AllocateAndInitializeSid( &Authority,
  657. 1,
  658. SECURITY_LOCAL_SERVICE_RID,
  659. 0,
  660. 0, 0, 0, 0, 0, 0,
  661. &pSidLocalService ))
  662. {
  663. if (pSidAdmins)
  664. {
  665. FreeSid(pSidAdmins);
  666. }
  667. if (pSidUsers)
  668. {
  669. FreeSid(pSidUsers);
  670. }
  671. return NULL;
  672. }
  673. dwAclLength = sizeof(ACL) +
  674. sizeof(ACCESS_ALLOWED_ACE) -
  675. sizeof(ULONG) +
  676. GetLengthSid(pSidAdmins) +
  677. sizeof(ACCESS_ALLOWED_ACE) -
  678. sizeof(ULONG) +
  679. GetLengthSid(pSidUsers) +
  680. sizeof(ACCESS_ALLOWED_ACE) -
  681. sizeof(ULONG) +
  682. GetLengthSid(pSidLocalService);
  683. pAcl = GlobalAlloc (GPTR, dwAclLength);
  684. if (pAcl != NULL)
  685. {
  686. if (!InitializeAcl( pAcl, dwAclLength, ACL_REVISION) ||
  687. !AddAccessAllowedAce ( pAcl,
  688. ACL_REVISION,
  689. GENERIC_READ | GENERIC_WRITE,
  690. pSidLocalService ) ||
  691. !AddAccessAllowedAce ( pAcl,
  692. ACL_REVISION,
  693. GENERIC_READ | GENERIC_WRITE,
  694. pSidAdmins ) ||
  695. !AddAccessAllowedAce ( pAcl,
  696. ACL_REVISION,
  697. (GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_CREATE_PIPE_INSTANCE)),
  698. pSidUsers ))
  699. {
  700. GlobalFree(pAcl);
  701. pAcl = NULL;
  702. }
  703. }
  704. FreeSid(pSidAdmins);
  705. FreeSid(pSidUsers);
  706. FreeSid(pSidLocalService);
  707. return pAcl;
  708. }
  709. void FreeGenericACL( PACL pAcl)
  710. {
  711. if (pAcl != NULL)
  712. GlobalFree(pAcl);
  713. }
  714. DWORD WINAPI svrPipeThread (LPVOID threadParam)
  715. {
  716. // This thread creates a named pipe instance and
  717. // blocks waiting for a client connection. When
  718. // client connects, the pipe handle is added to the
  719. // list of trap notification pipes.
  720. // It then waits for another connection.
  721. DWORD nInBufLen = sizeof(SNMP_TRAP);
  722. DWORD nOutBufLen = sizeof(SNMP_TRAP) * MAX_OUT_BUFS;
  723. SECURITY_ATTRIBUTES S_Attrib;
  724. SECURITY_DESCRIPTOR S_Desc;
  725. PACL pAcl;
  726. DWORD dwRead;
  727. // construct security decsriptor
  728. InitializeSecurityDescriptor (&S_Desc, SECURITY_DESCRIPTOR_REVISION);
  729. if ((pAcl = AllocGenericACL()) == NULL ||
  730. !SetSecurityDescriptorDacl (&S_Desc, TRUE, pAcl, FALSE))
  731. {
  732. FreeGenericACL(pAcl);
  733. return (0);
  734. }
  735. S_Attrib.nLength = sizeof(SECURITY_ATTRIBUTES);
  736. S_Attrib.lpSecurityDescriptor = &S_Desc;
  737. S_Attrib.bInheritHandle = TRUE;
  738. while (TRUE)
  739. {
  740. HANDLE hPipe;
  741. svrPipeListEntry *item;
  742. BOOL bSuccess;
  743. // eliminate the TerminateThread call in CLOSE_OUT of svcMainFunction
  744. if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0))
  745. {
  746. SNMPTRAPDBG(("svrPipeThread: exit 0.\n"));
  747. break;
  748. }
  749. hPipe = CreateNamedPipe (SNMPMGRTRAPPIPE,
  750. PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
  751. (PIPE_WAIT | PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE),
  752. PIPE_UNLIMITED_INSTANCES,
  753. nOutBufLen, nInBufLen, 0, &S_Attrib);
  754. if (hPipe == INVALID_HANDLE_VALUE)
  755. {
  756. SNMPTRAPDBG(("svrPipeThread: CreateNamedPipe failed 0x%08lx.\n", GetLastError()));
  757. break;
  758. }
  759. else
  760. {
  761. bSuccess = ConnectNamedPipe(hPipe, &g_ol);
  762. if (!bSuccess && GetLastError() == ERROR_IO_PENDING)
  763. {
  764. // blocking wait until g_ol.hEvent signaled by system for a new client
  765. // connection request or by our own termination.
  766. SNMPTRAPDBG(("svrPipeThread: before GetOverlappedResult.\n"));
  767. bSuccess = GetOverlappedResult(hPipe, &g_ol, &dwRead, TRUE);
  768. SNMPTRAPDBG(("svrPipeThread: after GetOverlappedResult.\n"));
  769. if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0))
  770. {
  771. SNMPTRAPDBG(("svrPipeThread: exit 1.\n"));
  772. CloseHandle(hPipe);
  773. break;
  774. }
  775. // reset event to non-signaled state for next I/O
  776. ResetEvent(g_ol.hEvent);
  777. }
  778. // check return from either ConnectNamedPipe or GetOverlappedResult.
  779. // If a client managed to connect between the CreateNamedPipe and
  780. // ConnectNamedPipe calls, ERROR_PIPE_CONNECTED will result
  781. if (!bSuccess && GetLastError() != ERROR_PIPE_CONNECTED)
  782. {
  783. // something went wrong, close instance and try again
  784. SNMPTRAPDBG(("svrPipeThread: ConnectNamedPipe 0x%08lx.\n", GetLastError()));
  785. CloseHandle(hPipe);
  786. continue;
  787. }
  788. }
  789. if (!(item = (svrPipeListEntry *)
  790. GlobalAlloc (GPTR, sizeof(svrPipeListEntry))))
  791. {
  792. SNMPTRAPDBG(("svrPipeThread: E_OUTOFMEMORY\n"));
  793. DisconnectNamedPipe(hPipe);
  794. CloseHandle(hPipe);
  795. break;
  796. }
  797. else
  798. {
  799. ll_node *crt;
  800. item->hPipe = hPipe;
  801. SNMPTRAPDBG(("svrPipeThread: add connected client to pipe list\n"));
  802. EnterCriticalSection (&cs_PIPELIST);
  803. ll_adde(item, pSvrPipeListHead);
  804. crt = pSvrPipeListHead;
  805. // scan all the pipe instances to detect the ones that are disconnected
  806. while (crt = ll_next(crt, pSvrPipeListHead))
  807. {
  808. DWORD dwError;
  809. // subsequent ConnectNamePipe() on a handle already connected return:
  810. // - ERROR_PIPE_CONNECTED if the client is still there
  811. // - ERROR_NO_DATA if the client has disconnected
  812. ConnectNamedPipe(
  813. ((svrPipeListEntry *)crt)->hPipe,
  814. NULL);
  815. dwError = GetLastError();
  816. // For anything else but ERROR_PIPE_CONNECTED, conclude there has been
  817. // something wrong with the client/pipe so disconect and close the handle
  818. // and release the memory
  819. if (dwError != ERROR_PIPE_CONNECTED)
  820. {
  821. ll_node *hold;
  822. SNMPTRAPDBG(("svrPipeThread: disconnect client pipe handle 0x%08lx.\n", ((svrPipeListEntry *)crt)->hPipe));
  823. if (!DisconnectNamedPipe(((svrPipeListEntry *)crt)->hPipe))
  824. {
  825. ; // Placeholder for error handling
  826. }
  827. if (!CloseHandle(((svrPipeListEntry *)crt)->hPipe))
  828. {
  829. ; // Placeholder for error handling
  830. }
  831. hold = ll_prev(crt);
  832. ll_rmv(crt);
  833. GlobalFree(crt); // check for errors?
  834. crt = hold;
  835. } // end_if
  836. }
  837. LeaveCriticalSection (&cs_PIPELIST);
  838. } // end_else
  839. } // end_while TRUE
  840. FreeGenericACL(pAcl);
  841. return(0);
  842. } // end_svrPipeThread()
  843. void FreeSvrPipeEntryList(ll_node* head)
  844. {
  845. if (head)
  846. {
  847. ll_node* current;
  848. current = head;
  849. while (current = ll_next(current, head))
  850. {
  851. ll_node *hold;
  852. if (!DisconnectNamedPipe(((svrPipeListEntry *)current)->hPipe))
  853. {
  854. ; // Placeholder for error handling
  855. }
  856. if (!CloseHandle(((svrPipeListEntry *)current)->hPipe))
  857. {
  858. ; // Placeholder for error handling
  859. }
  860. hold = ll_prev(current);
  861. ll_rmv(current);
  862. GlobalFree(current); // check for errors?
  863. current = hold;
  864. }
  865. GlobalFree(head);
  866. }
  867. }
  868. #if DBG
  869. // modified from snmp\common\dll\dbg.c
  870. #define MAX_LOG_ENTRY_LEN 512
  871. VOID
  872. WINAPI
  873. SnmpTrapDbgPrint(
  874. LPSTR szFormat,
  875. ...
  876. )
  877. /*++
  878. Routine Description:
  879. Prints debug message.
  880. Arguments:
  881. szFormat - formatting string (see printf).
  882. Return Values:
  883. None.
  884. --*/
  885. {
  886. va_list arglist;
  887. // 640 octets should be enough to encode oid's of 128 sub-ids.
  888. // (one subid can be encoded on at most 5 octets; there can be at
  889. // 128 sub-ids per oid. MAX_LOG_ENTRY_LEN = 512
  890. char szLogEntry[4*MAX_LOG_ENTRY_LEN];
  891. time_t now;
  892. // initialize variable args
  893. va_start(arglist, szFormat);
  894. time(&now);
  895. strftime(szLogEntry, MAX_LOG_ENTRY_LEN, "%H:%M:%S :", localtime(&now));
  896. // transfer variable args to buffer
  897. vsprintf(szLogEntry + strlen(szLogEntry), szFormat, arglist);
  898. // output entry to debugger
  899. OutputDebugStringA(szLogEntry);
  900. }
  901. #endif