|
|
// snmptrap.c
//
// Original Microsoft code modified by ACE*COMM
// for use with WSNMP32.DLL and other trap receiving
// clients, per contract.
//
// Bob Natale, ACE*COMM ([email protected])
// For NT v5 Beta, v970228
// Additional enhancements planned.
//
// This version of SNMPTRAP has no dependencies
// on either MGMTAPI.DLL or WSNMP32.DLL.
//
// WinSNMP clients use the SnmpRegister() function.
//
// Other clients will need to match the following
// values and structures:
//
// SNMP_TRAP structure
// SNMPTRAPPIPE name
// TRAPBUFSIZE value
//
// Change log:
// ------------------------------------------------
// 4.0.1381.3 Apr 8, 1998 Bob Natale
//
// 1. Re-worked the trap port monitoring thread into
// two threads...one for IP and one for IPX, to
// comply with WinSock v2's restrictions against
// multi-protocol select().
//
// 2. General clean-up/streamlining wrt "legacy" code
// from original MS version...more to do here, esp.
// wrt error handling code that does not do anything.
// ------------------------------------------------
// 4.0.1381.4 Apr. 10, 1998 Bob Natale
//
// 1. Replaced mutex calls with critical_sectin calls.
//
// 2. Cleaned out some dead code (removed commented out code)
// ------------------------------------------------
// Jan. 2, 2001 Frank Li
// 1. remove TerminateThread
// 2. add debug build loggings
// ------------------------------------------------
#include <windows.h>
#include <winsock.h>
#include <wsipx.h>
#include <process.h>
#ifdef DBG // include files for debug trace only
#include <stdio.h>
#include <time.h>
#endif
//--------------------------- PRIVATE VARIABLES -----------------------------
#define SNMPMGRTRAPPIPE "\\\\.\\PIPE\\MGMTAPI"
#define MAX_OUT_BUFS 16
#define TRAPBUFSIZE 4096
#define IP_TRAP_PORT 162
#define IPX_TRAP_PORT 36880
#define SNMPTRAP_WAIT_HINT 20000
//
// constants added to allocate trap buffer for fixing trap data of length
// > 8192 bytes. Here is the buffer allocation scheme based on the common
// cases that trap data sizes are less than 4-KBytes:
// 1. LargeTrap
// if (trap data size >= 8192 bytes), allocate MAX_UDP_SIZE sized buffer
// 2. MediumTrap
// if (trap data size <= 4096 bytes), allocate FOUR_K_BUF_SIZE sized buffer
// 3. SmallTrap
// if (4096 < trap data size < 8192), allocate just enough buffer size.
// Note:
// - when LargeTrap is received, the allocated buffer will stay for a time of
// MAXUDPLEN_BUFFER_TIME from the last LargeTrap received.
// - Once MediumTrap is received, subsequent SmallTrap will reuse the
// last MediumTrap allocated buffer.
//
#define MAX_UDP_SIZE (65535-8) // max udp len - 8bytes udp header
#define MAX_FIONREAD_UDP_SIZE 8192 // max winsock FIONREAD reported size (8kB)
#define FOUR_K_BUF_SIZE 4096 // buffer of 4-KBytes in size
#define MAXUDPLEN_BUFFER_TIME (2*60*1000) // max. 2 mins to keep the
// last allocated large buffer.
// ******** INITIALIZE A LIST HEAD ********
#define ll_init(head) (head)->next = (head)->prev = (head);
// ******** TEST A LIST FOR EMPTY ********
#define ll_empt(head) ( ((head)->next) == (head) )
// ******** Get ptr to next entry ********
#define ll_next(item,head)\
( (ll_node *)(item)->next == (head) ? 0 : \ (ll_node *)(item)->next ) // ******** Get ptr to prev entry ********
#define ll_prev(item)\
( (ll_node *)(item)->prev ) // ******** ADD AN ITEM TO THE END OF A LIST ********
#define ll_adde(item,head)\
{\ ll_node *pred = (head)->prev;\ ((ll_node *)(item))->next = (head);\ ((ll_node *)(item))->prev = pred;\ (pred)->next = ((ll_node *)(item));\ (head)->prev = ((ll_node *)(item));\ } // ******** REMOVE AN ITEM FROM A LIST ********
#define ll_rmv(item)\
{\ ll_node *pred = ((ll_node *)(item))->prev;\ ll_node *succ = ((ll_node *)(item))->next;\ pred->next = succ;\ succ->prev = pred;\ } // ******** List head/node ********
typedef struct ll_s { // linked list structure
struct ll_s *next; // next node
struct ll_s *prev; // prev. node
} ll_node; // linked list node
typedef struct {// shared by server trap thread and pipe thread
ll_node links; HANDLE hPipe; } svrPipeListEntry; typedef struct { SOCKADDR Addr; int AddrLen; UINT TrapBufSz; char TrapBuf[TRAPBUFSIZE]; // the size of this array should match the size of the structure
// defined in wsnmp_no.c!!!
} SNMP_TRAP, *PSNMP_TRAP; typedef struct { SOCKET s; OVERLAPPED ol; } TRAP_THRD_CONTEXT, *PTRAP_THRD_CONTEXT;
HANDLE hExitEvent = NULL; LPCTSTR svcName = "SNMPTRAP"; SERVICE_STATUS_HANDLE hService = 0; SERVICE_STATUS status = {SERVICE_WIN32, SERVICE_STOPPED, SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0}; SOCKET ipSock = INVALID_SOCKET; SOCKET ipxSock = INVALID_SOCKET; HANDLE ipThread = NULL; HANDLE ipxThread = NULL; CRITICAL_SECTION cs_PIPELIST; ll_node *pSvrPipeListHead = NULL;
// global variables added to remove the TerminateThread call
OVERLAPPED g_ol; // overlapped struct for svrPipeThread
TRAP_THRD_CONTEXT g_ipThreadContext; // context for ip svrTrapThread
TRAP_THRD_CONTEXT g_ipxThreadContext; // context for ipx svrTrapThread
///////////////////////////////////////////////////////////////////////////////
// //
// SNMPTRAP Debugging Prototypes //
// //
///////////////////////////////////////////////////////////////////////////////
#if DBG
VOID WINAPI SnmpTrapDbgPrint( IN LPSTR szFormat, IN ... ); #define SNMPTRAPDBG(_x_) SnmpTrapDbgPrint _x_
#else
#define SNMPTRAPDBG(_x_)
#endif
//--------------------------- PRIVATE PROTOTYPES ----------------------------
DWORD WINAPI svrTrapThread (IN OUT LPVOID threadParam); DWORD WINAPI svrPipeThread (IN LPVOID threadParam); VOID WINAPI svcHandlerFunction (IN DWORD dwControl); VOID WINAPI svcMainFunction (IN DWORD dwNumServicesArgs, IN LPSTR *lpServiceArgVectors); void FreeSvrPipeEntryList(IN ll_node* head);
//--------------------------- PRIVATE PROCEDURES ----------------------------
VOID WINAPI svcHandlerFunction (IN DWORD dwControl) { if (dwControl == SERVICE_CONTROL_STOP) { status.dwCurrentState = SERVICE_STOP_PENDING; status.dwCheckPoint++; status.dwWaitHint = SNMPTRAP_WAIT_HINT; if (!SetServiceStatus(hService, &status)) exit(1); // set event causing trap thread to terminate
if (!SetEvent(hExitEvent)) { status.dwCurrentState = SERVICE_STOPPED; status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; status.dwServiceSpecificExitCode = 1; // OPENISSUE - svc err code
status.dwCheckPoint = 0; status.dwWaitHint = 0; // We are exiting in any case, so ignore any error...
SetServiceStatus (hService, &status); exit(1); } } else // dwControl == SERVICE_CONTROL_INTERROGATE
// dwControl == SERVICE_CONTROL_PAUSE
// dwControl == SERVICE_CONTROL_CONTINUE
// dwControl == <anything else>
{ if (status.dwCurrentState == SERVICE_STOP_PENDING || status.dwCurrentState == SERVICE_START_PENDING) status.dwCheckPoint++; if (!SetServiceStatus (hService, &status)) exit(1); } } // end_svcHandlerFunction()
VOID WINAPI svcMainFunction (IN DWORD dwNumServicesArgs, IN LPSTR *lpServiceArgVectors) { WSADATA WinSockData; HANDLE hPipeThread = NULL; DWORD dwThreadId; //---------------------------------------------------------------------
hService = RegisterServiceCtrlHandler (svcName, svcHandlerFunction); if (hService == 0) { // We are exiting in any case, so ignore any error...
SNMPTRAPDBG(( "svcMainFunction: RegisterServiceCtrlHandler error 0x%08lx .\n", GetLastError() )); exit(1); } status.dwCurrentState = SERVICE_START_PENDING; status.dwWaitHint = SNMPTRAP_WAIT_HINT;
if (!SetServiceStatus(hService, &status)) exit(1);
__try { InitializeCriticalSection (&cs_PIPELIST); } __except(EXCEPTION_EXECUTE_HANDLER) { exit(1); }
memset(&g_ipThreadContext.ol, 0, sizeof(g_ipThreadContext.ol)); memset(&g_ipxThreadContext.ol, 0, sizeof(g_ipxThreadContext.ol));
if (WSAStartup ((WORD)0x0101, &WinSockData)) goto CLOSE_OUT; // WinSock startup failure
// allocate linked-list header for client received traps
if ((pSvrPipeListHead = (ll_node *)GlobalAlloc (GPTR, sizeof(ll_node))) == NULL) goto CLOSE_OUT; ll_init(pSvrPipeListHead); if ((hPipeThread = (HANDLE)_beginthreadex (NULL, 0, svrPipeThread, NULL, 0, &dwThreadId)) == 0) goto CLOSE_OUT; //-----------------------------------------------------------------------------------
//CHECK_IP:
ipSock = socket (AF_INET, SOCK_DGRAM, 0); if (ipSock != INVALID_SOCKET) { struct sockaddr_in localAddress_in; struct servent *serv; ZeroMemory (&localAddress_in, sizeof(localAddress_in)); localAddress_in.sin_family = AF_INET; if ((serv = getservbyname ("snmp-trap", "udp")) == NULL) localAddress_in.sin_port = htons (IP_TRAP_PORT); else localAddress_in.sin_port = (SHORT)serv->s_port; localAddress_in.sin_addr.s_addr = htonl (INADDR_ANY); if (bind (ipSock, (LPSOCKADDR)&localAddress_in, sizeof(localAddress_in)) != SOCKET_ERROR) { g_ipThreadContext.s = ipSock; // init the overlapped struct with manual reset non-signaled event
g_ipThreadContext.ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == g_ipThreadContext.ol.hEvent) goto CLOSE_OUT; ipThread = (HANDLE)_beginthreadex (NULL, 0, svrTrapThread, (LPVOID)&g_ipThreadContext, 0, &dwThreadId); } } //-----------------------------------------------------------------------------------
//CHECK_IPX:
ipxSock = socket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX); if (ipxSock != INVALID_SOCKET) { struct sockaddr_ipx localAddress_ipx; ZeroMemory (&localAddress_ipx, sizeof(localAddress_ipx)); localAddress_ipx.sa_family = AF_IPX; localAddress_ipx.sa_socket = htons (IPX_TRAP_PORT); if (bind (ipxSock, (LPSOCKADDR)&localAddress_ipx, sizeof(localAddress_ipx)) != SOCKET_ERROR) { g_ipxThreadContext.s = ipxSock; // init the overlapped struct with manual reset non-signaled event
g_ipxThreadContext.ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == g_ipxThreadContext.ol.hEvent) goto CLOSE_OUT; ipxThread = (HANDLE)_beginthreadex (NULL, 0, svrTrapThread, (LPVOID)&g_ipxThreadContext, 0, &dwThreadId); } } //-----------------------------------------------------------------------------------
// We are ready to listen for traps...
status.dwCurrentState = SERVICE_RUNNING; status.dwCheckPoint = 0; status.dwWaitHint = 0; if (!SetServiceStatus(hService, &status)) goto CLOSE_OUT; WaitForSingleObject (hExitEvent, INFINITE); //-----------------------------------------------------------------------------------
CLOSE_OUT: // make sure we can bail out if we are here because of goto statements above
SetEvent(hExitEvent);
status.dwCurrentState = SERVICE_STOP_PENDING; // in case we need to report
// reporting progress in stopping service
status.dwCheckPoint++; if (!SetServiceStatus(hService, &status)) { SNMPTRAPDBG(( "svcMainFunction: error 0x%08lx setting service status.\n", GetLastError() )); } if (hPipeThread != NULL) { SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ol.hEvent.\n")); SetEvent(g_ol.hEvent); // signal to terminate the svrPipeThread thread
WaitForSingleObject (hPipeThread, INFINITE); SNMPTRAPDBG(("svcMainFunction: WaitForSingleObject hPipeThread INFINITE done.\n")); // reporting progress in stopping service
status.dwCheckPoint++; if (!SetServiceStatus(hService, &status)) { SNMPTRAPDBG(( "svcMainFunction: error 0x%08lx setting service status.\n", GetLastError() )); } CloseHandle (hPipeThread); }
if (ipThread != NULL) { SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ipThreadContext.ol.hEvent.\n")); SetEvent(g_ipThreadContext.ol.hEvent); // signal to terminate thread
WaitForSingleObject (ipThread, INFINITE); // reporting progress in stopping service
status.dwCheckPoint++; if (!SetServiceStatus(hService, &status)) { SNMPTRAPDBG(( "svcMainFunction: error 0x%08lx setting service status.\n", GetLastError() )); } CloseHandle (ipThread); } if (ipSock != INVALID_SOCKET) closesocket (ipSock); if (g_ipThreadContext.ol.hEvent) CloseHandle(g_ipThreadContext.ol.hEvent);
if (ipxThread != NULL) { SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ipxThreadContext.ol.hEvent.\n")); SetEvent(g_ipxThreadContext.ol.hEvent); // signal to terminate thread
WaitForSingleObject (ipxThread, INFINITE); // reporting progress in stopping service
status.dwCheckPoint++; if (!SetServiceStatus(hService, &status)) { SNMPTRAPDBG(( "svcMainFunction: error 0x%08lx setting service status.\n", GetLastError() )); } CloseHandle (ipxThread); } if (ipxSock != INVALID_SOCKET) closesocket (ipxSock); if (g_ipxThreadContext.ol.hEvent) CloseHandle(g_ipxThreadContext.ol.hEvent);
EnterCriticalSection (&cs_PIPELIST); if (pSvrPipeListHead != NULL) { FreeSvrPipeEntryList(pSvrPipeListHead); pSvrPipeListHead = NULL; } LeaveCriticalSection (&cs_PIPELIST); DeleteCriticalSection (&cs_PIPELIST); WSACleanup(); status.dwCurrentState = SERVICE_STOPPED; status.dwCheckPoint = 0; status.dwWaitHint = 0; if (!SetServiceStatus(hService, &status)) exit(1); } // end_svcMainFunction()
//--------------------------- PUBLIC PROCEDURES -----------------------------
int __cdecl main () { BOOL fOk; SERVICE_TABLE_ENTRY svcStartTable[2] = { {(LPTSTR)svcName, svcMainFunction}, {NULL, NULL} }; // create event to synchronize trap server shutdown
hExitEvent = CreateEvent (NULL, TRUE, FALSE, NULL); if (NULL == hExitEvent) { exit(1); } // init the overlapped struct used by svrTrapThread
// with manual reset non-signaled event
memset(&g_ol, 0, sizeof(g_ol)); g_ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == g_ol.hEvent) { CloseHandle(hExitEvent); exit(1); } // this call will not return until service stopped
fOk = StartServiceCtrlDispatcher (svcStartTable); CloseHandle (hExitEvent); CloseHandle(g_ol.hEvent); return fOk; } // end_main()
//
DWORD WINAPI svrTrapThread (LPVOID threadParam) // This thread takes a SOCKET from the TRAP_THRD_CONTEXT parameter,
// loops on select()
// for data in-coming over that socket, writing it back
// out to clients over all pipes currently on the list of
// trap notification pipes shared by this thread and the
// pipe thread
{ PSNMP_TRAP pRecvTrap = NULL; struct fd_set readfds; PTRAP_THRD_CONTEXT pThreadContext = (PTRAP_THRD_CONTEXT) threadParam; SOCKET fd = INVALID_SOCKET; int len; DWORD dwLastAllocatedUdpDataLen = 0; // the last allocated UDP data buffer size
DWORD dwLastBigBufferRequestTime = 0; // the tick count that the last
// LargeTrap received
BOOL fTimeoutForMaxUdpLenBuffer = FALSE; // need to deallocate the big buffer
struct timeval tvTimeout; // timeout for select
//
if (NULL == pThreadContext) return 0; fd = pThreadContext->s; dwLastBigBufferRequestTime = GetTickCount(); // select with timeout so that we can response to SERVICE_CONTROL_STOP
tvTimeout.tv_sec = 5; // 5 sec. timeout value
tvTimeout.tv_usec = 0; // When select returns, the contents of the
// tvTimeout structure are not altered.
while (TRUE) {
ULONG ulTrapSize = 0; DWORD dwError = 0; int nRet = 0;
if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0)) { SNMPTRAPDBG(("svrTrapThread: exit 0.\n")); break; }
// construct readfds which gets destroyed by select()
FD_ZERO(&readfds); FD_SET(fd, &readfds);
nRet = select (0, &readfds, NULL, NULL, &tvTimeout); if (0 == nRet) { // timeout
continue; } else if (SOCKET_ERROR == nRet) { SNMPTRAPDBG(("svrTrapThread: select failed %d.\n", WSAGetLastError())); break; // terminate thread
} if (!(FD_ISSET(fd, &readfds))) continue;
if (ioctlsocket( fd, // socket to query
FIONREAD, // query for the size of the incoming datagram
&ulTrapSize // unsigned long to store the size of the datagram
) != 0) { dwError = WSAGetLastError(); SNMPTRAPDBG(( "ioctlsocket FIONREAD failed: lasterror: 0x%08lx\n", dwError));
continue; // continue if we could not determine the size of the
// incoming datagram
} if (ulTrapSize >= MAX_FIONREAD_UDP_SIZE) { dwLastBigBufferRequestTime = GetTickCount(); // update tickcount
// the ulTrapSize is not accurate on reporting the size of the
// next UDP datagram message. KB Q192599 and KB Q140263
if ( NULL == pRecvTrap || dwLastAllocatedUdpDataLen < MAX_UDP_SIZE ) { if (pRecvTrap) { GlobalFree(pRecvTrap); pRecvTrap = NULL; dwLastAllocatedUdpDataLen = 0; } SNMPTRAPDBG(( "allocate LargeTrap of size : %d\n", sizeof(SNMP_TRAP) - TRAPBUFSIZE + MAX_UDP_SIZE)); // allocate for the trap header + max udp size
pRecvTrap = (PSNMP_TRAP)GlobalAlloc(GPTR, (sizeof(SNMP_TRAP) - TRAPBUFSIZE + MAX_UDP_SIZE)); if (NULL == pRecvTrap) { SNMPTRAPDBG(("svrTrapThread: GlobalAlloc failed.\n")); dwLastAllocatedUdpDataLen = 0; break; } dwLastAllocatedUdpDataLen = MAX_UDP_SIZE; } } else { // winsock has reported the exact amount of UDP datagram
// size to be recieved as long as the next datagram is less than
// 8-kbyte
//
// if we've allocated a big buffer before, check to see if we need
// to deallocate it to save the usage of resource.
//
fTimeoutForMaxUdpLenBuffer = FALSE; // reset timeout flag
if (MAX_UDP_SIZE == dwLastAllocatedUdpDataLen) { // we've allocated a big buffer before
DWORD dwCurrTime = GetTickCount(); if (dwCurrTime < dwLastBigBufferRequestTime) { // wrap around occured. we just simply assume it is time to
// release the big buffer.
fTimeoutForMaxUdpLenBuffer = TRUE; SNMPTRAPDBG(( "Timeout to free LargeTrap buffer of size %d bytes.\n", dwLastAllocatedUdpDataLen)); } else { if ( (dwCurrTime-dwLastBigBufferRequestTime) > MAXUDPLEN_BUFFER_TIME ) { // after quite a long time, we don't have a large UDP
// datagram received.
fTimeoutForMaxUdpLenBuffer = TRUE; SNMPTRAPDBG(( "Timeout to free LargeTrap buffer size of %d bytes.\n", dwLastAllocatedUdpDataLen)); } } }
if (pRecvTrap == NULL || fTimeoutForMaxUdpLenBuffer || dwLastAllocatedUdpDataLen < ulTrapSize) { // allocate/reallocate buffer
if (pRecvTrap != NULL) { GlobalFree(pRecvTrap); pRecvTrap = NULL; dwLastAllocatedUdpDataLen = 0; } if (FOUR_K_BUF_SIZE >= ulTrapSize) { // allocate at least 4 KBytes buffer to avoid
// re-allocations on different sizes of small trap received
pRecvTrap = (PSNMP_TRAP)GlobalAlloc(GPTR, (sizeof(SNMP_TRAP) - TRAPBUFSIZE + FOUR_K_BUF_SIZE)); dwLastAllocatedUdpDataLen = FOUR_K_BUF_SIZE; SNMPTRAPDBG(( "allocate SmallTrap of size : %d\n", sizeof(SNMP_TRAP) - TRAPBUFSIZE + FOUR_K_BUF_SIZE)); } else { // allocate what is necessary
pRecvTrap = (PSNMP_TRAP)GlobalAlloc(GPTR, (sizeof(SNMP_TRAP) - TRAPBUFSIZE + ulTrapSize)); dwLastAllocatedUdpDataLen = ulTrapSize; SNMPTRAPDBG(( "allocate MediumTrap of size : %d\n", sizeof(SNMP_TRAP) - TRAPBUFSIZE + ulTrapSize)); } if (NULL == pRecvTrap) // if there is so few memory that we can't allocate a bit ..
{ // bail out and stop the SNMPTRAP service (bug? - other option => 100% CPU which is worst)
SNMPTRAPDBG(("svrTrapThread: GlobalAlloc failed.\n")); dwLastAllocatedUdpDataLen = 0; break; } } }
pRecvTrap->TrapBufSz = dwLastAllocatedUdpDataLen; // actual buffer size
pRecvTrap->AddrLen = sizeof(pRecvTrap->Addr);
len = recvfrom ( fd, pRecvTrap->TrapBuf, pRecvTrap->TrapBufSz, 0, &(pRecvTrap->Addr), &(pRecvTrap->AddrLen)); if (len == SOCKET_ERROR) { dwError = WSAGetLastError(); SNMPTRAPDBG(( "recvfrom failed: ulTrapSize: %d bytes, TrapBufSz: %d bytes, lasterror: 0x%08lx\n", ulTrapSize, pRecvTrap->TrapBufSz, dwError)); continue; }
EnterCriticalSection (&cs_PIPELIST); pRecvTrap->TrapBufSz = len; // the acutal trap data len received
// add header to length
len += sizeof(SNMP_TRAP) - sizeof(pRecvTrap->TrapBuf); // - TRAPBUFSIZE
if (!ll_empt(pSvrPipeListHead)) { DWORD written; ll_node *item = pSvrPipeListHead; while (item = ll_next(item, pSvrPipeListHead)) { if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0)) { SNMPTRAPDBG(("svrTrapThread: exit 1.\n")); LeaveCriticalSection (&cs_PIPELIST); goto EXIT_TRAP_THREAD; } if (!WriteFile( ((svrPipeListEntry *)item)->hPipe, (LPBYTE)pRecvTrap, len, &written, &pThreadContext->ol)) { if (ERROR_IO_PENDING == GetLastError()) { SNMPTRAPDBG(("svrTrapThread: before GetOverlappedResult.\n")); GetOverlappedResult( ((svrPipeListEntry *)item)->hPipe, &pThreadContext->ol, &written, TRUE // Block
); SNMPTRAPDBG(("svrTrapThread: after GetOverlappedResult.\n")); if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0)) { SNMPTRAPDBG(("svrTrapThread: exit 2.\n")); LeaveCriticalSection (&cs_PIPELIST); goto EXIT_TRAP_THREAD; } // reset event to non-signaled state for next I/O
ResetEvent(pThreadContext->ol.hEvent); } else { ll_node *hold;
if (!DisconnectNamedPipe(((svrPipeListEntry *)item)->hPipe)) { ; // Placeholder for error handling
} if (!CloseHandle(((svrPipeListEntry *)item)->hPipe)) { ; // Placeholder for error handling
} hold = ll_prev(item); ll_rmv(item); GlobalFree(item); // check for errors?
item = hold; } } // end_if !WriteFile
else if (written != (DWORD)len) { SNMPTRAPDBG(("svrTrapThread: written != len\n")); ; // Placeholder for error handling
} } // end_while item = ll_next
} // end_if !ll_empt
LeaveCriticalSection (&cs_PIPELIST); } // end while TRUE
EXIT_TRAP_THREAD: if (pRecvTrap != NULL) GlobalFree(pRecvTrap);
return 0; } // end svrTrapThread()
PACL AllocGenericACL() { PACL pAcl; PSID pSidAdmins, pSidUsers, pSidLocalService; SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY; DWORD dwAclLength;
pSidAdmins = pSidUsers = pSidLocalService = NULL;
// Bug# 179644 The SNMP trap service should not run in the LocalSystem account
if ( !AllocateAndInitializeSid( &Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSidAdmins ) || !AllocateAndInitializeSid( &Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &pSidUsers ) || !AllocateAndInitializeSid( &Authority, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &pSidLocalService )) { if (pSidAdmins) { FreeSid(pSidAdmins); } if (pSidUsers) { FreeSid(pSidUsers); } return NULL; }
dwAclLength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + GetLengthSid(pSidAdmins) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + GetLengthSid(pSidUsers) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + GetLengthSid(pSidLocalService);
pAcl = GlobalAlloc (GPTR, dwAclLength); if (pAcl != NULL) { if (!InitializeAcl( pAcl, dwAclLength, ACL_REVISION) || !AddAccessAllowedAce ( pAcl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE, pSidLocalService ) || !AddAccessAllowedAce ( pAcl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE, pSidAdmins ) || !AddAccessAllowedAce ( pAcl, ACL_REVISION, (GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_CREATE_PIPE_INSTANCE)), pSidUsers )) { GlobalFree(pAcl); pAcl = NULL; } }
FreeSid(pSidAdmins); FreeSid(pSidUsers); FreeSid(pSidLocalService);
return pAcl; }
void FreeGenericACL( PACL pAcl) { if (pAcl != NULL) GlobalFree(pAcl); }
DWORD WINAPI svrPipeThread (LPVOID threadParam) { // This thread creates a named pipe instance and
// blocks waiting for a client connection. When
// client connects, the pipe handle is added to the
// list of trap notification pipes.
// It then waits for another connection.
DWORD nInBufLen = sizeof(SNMP_TRAP); DWORD nOutBufLen = sizeof(SNMP_TRAP) * MAX_OUT_BUFS; SECURITY_ATTRIBUTES S_Attrib; SECURITY_DESCRIPTOR S_Desc; PACL pAcl; DWORD dwRead; // construct security decsriptor
InitializeSecurityDescriptor (&S_Desc, SECURITY_DESCRIPTOR_REVISION);
if ((pAcl = AllocGenericACL()) == NULL || !SetSecurityDescriptorDacl (&S_Desc, TRUE, pAcl, FALSE)) { FreeGenericACL(pAcl); return (0); }
S_Attrib.nLength = sizeof(SECURITY_ATTRIBUTES); S_Attrib.lpSecurityDescriptor = &S_Desc; S_Attrib.bInheritHandle = TRUE;
while (TRUE) { HANDLE hPipe; svrPipeListEntry *item; BOOL bSuccess;
// eliminate the TerminateThread call in CLOSE_OUT of svcMainFunction
if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0)) { SNMPTRAPDBG(("svrPipeThread: exit 0.\n")); break; } hPipe = CreateNamedPipe (SNMPMGRTRAPPIPE, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, (PIPE_WAIT | PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE), PIPE_UNLIMITED_INSTANCES, nOutBufLen, nInBufLen, 0, &S_Attrib);
if (hPipe == INVALID_HANDLE_VALUE) { SNMPTRAPDBG(("svrPipeThread: CreateNamedPipe failed 0x%08lx.\n", GetLastError())); break; } else { bSuccess = ConnectNamedPipe(hPipe, &g_ol); if (!bSuccess && GetLastError() == ERROR_IO_PENDING) { // blocking wait until g_ol.hEvent signaled by system for a new client
// connection request or by our own termination.
SNMPTRAPDBG(("svrPipeThread: before GetOverlappedResult.\n")); bSuccess = GetOverlappedResult(hPipe, &g_ol, &dwRead, TRUE); SNMPTRAPDBG(("svrPipeThread: after GetOverlappedResult.\n")); if (WAIT_OBJECT_0 == WaitForSingleObject (hExitEvent, 0)) { SNMPTRAPDBG(("svrPipeThread: exit 1.\n")); CloseHandle(hPipe); break; } // reset event to non-signaled state for next I/O
ResetEvent(g_ol.hEvent); } // check return from either ConnectNamedPipe or GetOverlappedResult.
// If a client managed to connect between the CreateNamedPipe and
// ConnectNamedPipe calls, ERROR_PIPE_CONNECTED will result
if (!bSuccess && GetLastError() != ERROR_PIPE_CONNECTED) { // something went wrong, close instance and try again
SNMPTRAPDBG(("svrPipeThread: ConnectNamedPipe 0x%08lx.\n", GetLastError())); CloseHandle(hPipe); continue; } } if (!(item = (svrPipeListEntry *) GlobalAlloc (GPTR, sizeof(svrPipeListEntry)))) { SNMPTRAPDBG(("svrPipeThread: E_OUTOFMEMORY\n")); DisconnectNamedPipe(hPipe); CloseHandle(hPipe); break; } else { ll_node *crt; item->hPipe = hPipe;
SNMPTRAPDBG(("svrPipeThread: add connected client to pipe list\n"));
EnterCriticalSection (&cs_PIPELIST); ll_adde(item, pSvrPipeListHead); crt = pSvrPipeListHead;
// scan all the pipe instances to detect the ones that are disconnected
while (crt = ll_next(crt, pSvrPipeListHead)) { DWORD dwError;
// subsequent ConnectNamePipe() on a handle already connected return:
// - ERROR_PIPE_CONNECTED if the client is still there
// - ERROR_NO_DATA if the client has disconnected
ConnectNamedPipe( ((svrPipeListEntry *)crt)->hPipe, NULL);
dwError = GetLastError();
// For anything else but ERROR_PIPE_CONNECTED, conclude there has been
// something wrong with the client/pipe so disconect and close the handle
// and release the memory
if (dwError != ERROR_PIPE_CONNECTED) { ll_node *hold;
SNMPTRAPDBG(("svrPipeThread: disconnect client pipe handle 0x%08lx.\n", ((svrPipeListEntry *)crt)->hPipe)); if (!DisconnectNamedPipe(((svrPipeListEntry *)crt)->hPipe)) { ; // Placeholder for error handling
} if (!CloseHandle(((svrPipeListEntry *)crt)->hPipe)) { ; // Placeholder for error handling
}
hold = ll_prev(crt); ll_rmv(crt); GlobalFree(crt); // check for errors?
crt = hold; } // end_if
}
LeaveCriticalSection (&cs_PIPELIST); } // end_else
} // end_while TRUE
FreeGenericACL(pAcl); return(0);
} // end_svrPipeThread()
void FreeSvrPipeEntryList(ll_node* head) { if (head) { ll_node* current; current = head; while (current = ll_next(current, head)) { ll_node *hold; if (!DisconnectNamedPipe(((svrPipeListEntry *)current)->hPipe)) { ; // Placeholder for error handling
} if (!CloseHandle(((svrPipeListEntry *)current)->hPipe)) { ; // Placeholder for error handling
}
hold = ll_prev(current); ll_rmv(current); GlobalFree(current); // check for errors?
current = hold; } GlobalFree(head); } }
#if DBG
// modified from snmp\common\dll\dbg.c
#define MAX_LOG_ENTRY_LEN 512
VOID WINAPI SnmpTrapDbgPrint( LPSTR szFormat, ... )
/*++
Routine Description:
Prints debug message.
Arguments:
szFormat - formatting string (see printf).
Return Values:
None.
--*/
{ va_list arglist;
// 640 octets should be enough to encode oid's of 128 sub-ids.
// (one subid can be encoded on at most 5 octets; there can be at
// 128 sub-ids per oid. MAX_LOG_ENTRY_LEN = 512
char szLogEntry[4*MAX_LOG_ENTRY_LEN];
time_t now;
// initialize variable args
va_start(arglist, szFormat);
time(&now); strftime(szLogEntry, MAX_LOG_ENTRY_LEN, "%H:%M:%S :", localtime(&now));
// transfer variable args to buffer
vsprintf(szLogEntry + strlen(szLogEntry), szFormat, arglist);
// output entry to debugger
OutputDebugStringA(szLogEntry); } #endif
|