windows-nt/Source/XPSP1/NT/net/snmp/manager/winsnmp/exe/snmptrap.c
2020-09-26 16:20:57 +08:00

991 lines
35 KiB
C

// 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 (bnatale@acecomm.com)
// 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
//
// 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 = 45000;
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)
{
status.dwCurrentState = SERVICE_STOPPED;
status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
status.dwServiceSpecificExitCode = 2; // 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);
}
status.dwCurrentState = SERVICE_START_PENDING;
status.dwWaitHint = 20000;
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);
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"));
CloseHandle (hPipeThread);
}
if (ipSock != INVALID_SOCKET)
closesocket (ipSock); // unblock any socket call
if (ipThread != NULL)
{
SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ipThreadContext.ol.hEvent.\n"));
SetEvent(g_ipThreadContext.ol.hEvent); // signal to terminate thread
WaitForSingleObject (ipThread, INFINITE);
CloseHandle (ipThread);
}
if (g_ipThreadContext.ol.hEvent)
CloseHandle(g_ipThreadContext.ol.hEvent);
if (ipxSock != INVALID_SOCKET)
closesocket (ipxSock); // unblock any socket call
if (ipxThread != NULL)
{
SNMPTRAPDBG(("svcMainFunction: enter SetEvent g_ipxThreadContext.ol.hEvent.\n"));
SetEvent(g_ipxThreadContext.ol.hEvent); // signal to terminate thread
WaitForSingleObject (ipxThread, INFINITE);
CloseHandle (ipxThread);
}
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;
OSVERSIONINFO osInfo;
SERVICE_TABLE_ENTRY svcStartTable[2] =
{
{(LPTSTR)svcName, svcMainFunction},
{NULL, NULL}
};
osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
fOk = GetVersionEx (&osInfo);
if (fOk && (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT))
{ // 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
//
if (NULL == pThreadContext)
return 0;
fd = pThreadContext->s;
dwLastBigBufferRequestTime = GetTickCount();
while (TRUE)
{
ULONG ulTrapSize = 0;
DWORD dwError = 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);
if (select (0, &readfds, NULL, NULL, NULL) == SOCKET_ERROR)
{
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 accurat 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);
break;
}
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);
break;
}
// 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
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