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.
687 lines
18 KiB
687 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1992-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
snmpthrd.c
|
|
|
|
Abstract:
|
|
|
|
Contains routines for master agent network thread.
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
10-Feb-1997 DonRyan
|
|
Rewrote to implement SNMPv2 support.
|
|
|
|
--*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Include files //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include <tchar.h>
|
|
#include <stdio.h>
|
|
#include "globals.h"
|
|
#include "contexts.h"
|
|
#include "regions.h"
|
|
#include "snmpmgrs.h"
|
|
#include "trapmgrs.h"
|
|
#include "trapthrd.h"
|
|
#include "network.h"
|
|
#include "varbinds.h"
|
|
#include "snmpmgmt.h"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Global variables //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
UINT g_nTransactionId = 0;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Private definitions //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MAX_IPX_ADDR_LEN 64
|
|
#define MAX_COMMUNITY_LEN 255
|
|
|
|
#define ERRMSG_TRANSPORT_IP _T("IP")
|
|
#define ERRMSG_TRANSPORT_IPX _T("IPX")
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Private procedures //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
LPSTR
|
|
AddrToString(
|
|
struct sockaddr * pSockAddr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts sockaddr to display string.
|
|
|
|
Arguments:
|
|
|
|
pSockAddr - pointer to socket address.
|
|
|
|
Return Values:
|
|
|
|
Returns pointer to string.
|
|
|
|
--*/
|
|
|
|
{
|
|
static CHAR ipxAddr[MAX_IPX_ADDR_LEN];
|
|
|
|
// determine family
|
|
if (pSockAddr->sa_family == AF_INET) {
|
|
|
|
struct sockaddr_in * pSockAddrIn;
|
|
|
|
// obtain pointer to protocol specific structure
|
|
pSockAddrIn = (struct sockaddr_in * )pSockAddr;
|
|
|
|
// forward to winsock conversion function
|
|
return inet_ntoa(pSockAddrIn->sin_addr);
|
|
|
|
} else if (pSockAddr->sa_family == AF_IPX) {
|
|
|
|
struct sockaddr_ipx * pSockAddrIpx;
|
|
|
|
// obtain pointer to protocol specific structure
|
|
pSockAddrIpx = (struct sockaddr_ipx * )pSockAddr;
|
|
|
|
// transfer ipx address to static buffer
|
|
sprintf(ipxAddr,
|
|
"%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x",
|
|
(BYTE)pSockAddrIpx->sa_netnum[0],
|
|
(BYTE)pSockAddrIpx->sa_netnum[1],
|
|
(BYTE)pSockAddrIpx->sa_netnum[2],
|
|
(BYTE)pSockAddrIpx->sa_netnum[3],
|
|
(BYTE)pSockAddrIpx->sa_nodenum[0],
|
|
(BYTE)pSockAddrIpx->sa_nodenum[1],
|
|
(BYTE)pSockAddrIpx->sa_nodenum[2],
|
|
(BYTE)pSockAddrIpx->sa_nodenum[3],
|
|
(BYTE)pSockAddrIpx->sa_nodenum[4],
|
|
(BYTE)pSockAddrIpx->sa_nodenum[5]
|
|
);
|
|
|
|
// return addr
|
|
return ipxAddr;
|
|
}
|
|
|
|
// failure
|
|
return NULL;
|
|
}
|
|
|
|
|
|
LPSTR
|
|
CommunityOctetsToString(
|
|
AsnOctetString *pAsnCommunity,
|
|
BOOL bUnicode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts community octet string to display string.
|
|
|
|
Arguments:
|
|
|
|
pAsnCommunity - pointer to community octet string.
|
|
|
|
Return Values:
|
|
|
|
Returns pointer to string.
|
|
|
|
--*/
|
|
|
|
{
|
|
static CHAR Community[MAX_COMMUNITY_LEN+1];
|
|
LPSTR pCommunity = Community;
|
|
|
|
// terminate string
|
|
*pCommunity = '\0';
|
|
|
|
// validate pointer
|
|
if (pAsnCommunity != NULL)
|
|
{
|
|
DWORD nChars = 0;
|
|
|
|
// determine number of characters to transfer
|
|
nChars = min(pAsnCommunity->length, MAX_COMMUNITY_LEN);
|
|
|
|
if (bUnicode)
|
|
{
|
|
WCHAR wCommunity[MAX_COMMUNITY_LEN+1];
|
|
|
|
// tranfer memory into buffer
|
|
memset(wCommunity, 0, nChars+sizeof(WCHAR));
|
|
memcpy(wCommunity, pAsnCommunity->stream, nChars);
|
|
SnmpUtilUnicodeToAnsi(&pCommunity, wCommunity, FALSE);
|
|
}
|
|
else
|
|
{
|
|
memcpy(Community, pAsnCommunity->stream, nChars);
|
|
Community[nChars] = '\0';
|
|
}
|
|
}
|
|
|
|
// success
|
|
return pCommunity;
|
|
}
|
|
|
|
|
|
LPSTR
|
|
StaticUnicodeToString(
|
|
LPWSTR wszUnicode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts null terminated UNICODE string to static LPSTR
|
|
|
|
Arguments:
|
|
|
|
pOctets - pointer to community octet string.
|
|
|
|
Return Values:
|
|
|
|
Returns pointer to string.
|
|
|
|
--*/
|
|
|
|
{
|
|
static CHAR szString[MAX_COMMUNITY_LEN+1];
|
|
LPSTR pszString = szString;
|
|
|
|
// terminate string
|
|
*pszString = '\0';
|
|
|
|
// validate pointer
|
|
if (wszUnicode != NULL)
|
|
{
|
|
WCHAR wcBreak;
|
|
BOOL bNeedBreak;
|
|
|
|
bNeedBreak = (wcslen(wszUnicode) > MAX_COMMUNITY_LEN);
|
|
|
|
if (bNeedBreak)
|
|
{
|
|
wcBreak = wszUnicode[MAX_COMMUNITY_LEN];
|
|
wszUnicode[MAX_COMMUNITY_LEN] = L'\0';
|
|
}
|
|
|
|
SnmpUtilUnicodeToAnsi(&pszString, wszUnicode, FALSE);
|
|
|
|
if (bNeedBreak)
|
|
wszUnicode[MAX_COMMUNITY_LEN] = wcBreak;
|
|
}
|
|
|
|
// success
|
|
return pszString;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ValidateManager(
|
|
PNETWORK_LIST_ENTRY pNLE
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks access rights of given manager.
|
|
|
|
Arguments:
|
|
|
|
pNLE - pointer to network list entry.
|
|
|
|
Return Values:
|
|
|
|
Returns true if manager allowed access.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fAccessOk = FALSE;
|
|
PMANAGER_LIST_ENTRY pMLE = NULL;
|
|
|
|
fAccessOk = IsManagerAddrLegal((struct sockaddr_in *)&pNLE->SockAddr) &&
|
|
(FindManagerByAddr(&pMLE, &pNLE->SockAddr) ||
|
|
IsListEmpty(&g_PermittedManagers)
|
|
);
|
|
|
|
if (!fAccessOk &&
|
|
snmpMgmtBase.AsnIntegerPool[IsnmpEnableAuthenTraps].asnValue.number)
|
|
GenerateAuthenticationTrap();
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: %s request from %s.\n",
|
|
fAccessOk
|
|
? "accepting"
|
|
: "rejecting"
|
|
,
|
|
AddrToString(&pNLE->SockAddr)
|
|
));
|
|
|
|
return fAccessOk;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ProcessSnmpMessage(
|
|
PNETWORK_LIST_ENTRY pNLE
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse SNMP message and dispatch to subagents.
|
|
|
|
Arguments:
|
|
|
|
pNLE - pointer to network list entry.
|
|
|
|
Return Values:
|
|
|
|
Returns true if successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fOk = FALSE;
|
|
|
|
// decode request
|
|
if (ParseMessage(pNLE))
|
|
{
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: %s request, community %s, %d variable(s).\n",
|
|
PDUTYPESTRING(pNLE->Pdu.nType),
|
|
CommunityOctetsToString(&(pNLE->Community), FALSE),
|
|
pNLE->Pdu.Vbl.len
|
|
));
|
|
|
|
if (ProcessVarBinds(pNLE))
|
|
{
|
|
// initialize buffer length
|
|
pNLE->Buffer.len = NLEBUFLEN;
|
|
|
|
// reset pdu type to response
|
|
pNLE->Pdu.nType = SNMP_PDU_RESPONSE;
|
|
|
|
// encode response
|
|
fOk = BuildMessage(
|
|
pNLE->nVersion,
|
|
&pNLE->Community,
|
|
&pNLE->Pdu,
|
|
pNLE->Buffer.buf,
|
|
&pNLE->Buffer.len
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pNLE->fAccessOk)
|
|
{
|
|
// BUG# 552295
|
|
// authentication succeeded or not done yet,
|
|
// the error is due to BER decoding failure
|
|
|
|
// register BER decoding failure into the management structures
|
|
mgmtCTick(CsnmpInASNParseErrs);
|
|
}
|
|
}
|
|
|
|
// release pdu
|
|
UnloadPdu(pNLE);
|
|
|
|
return fOk;
|
|
}
|
|
|
|
|
|
void CALLBACK
|
|
RecvCompletionRoutine(
|
|
IN DWORD dwStatus,
|
|
IN DWORD dwBytesTransferred,
|
|
IN LPWSAOVERLAPPED pOverlapped,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback for completing asynchronous reads.
|
|
|
|
Arguments:
|
|
|
|
Status - completion status for the overlapped operation.
|
|
|
|
BytesTransferred - number of bytes transferred.
|
|
|
|
pOverlapped - pointer to overlapped structure.
|
|
|
|
Flags - receive flags.
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PNETWORK_LIST_ENTRY pNLE;
|
|
|
|
EnterCriticalSection(&g_RegCriticalSectionA);
|
|
|
|
// retreive pointer to network list entry from overlapped structure
|
|
pNLE = CONTAINING_RECORD(pOverlapped, NETWORK_LIST_ENTRY, Overlapped);
|
|
|
|
// copy receive completion information
|
|
pNLE->nTransactionId = ++g_nTransactionId;
|
|
pNLE->dwBytesTransferred = dwBytesTransferred;
|
|
pNLE->dwStatus = dwStatus;
|
|
pNLE->dwFlags = dwFlags;
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: --- transaction %d begin ---\n",
|
|
pNLE->nTransactionId
|
|
));
|
|
|
|
// validate status
|
|
if (dwStatus == NOERROR) {
|
|
|
|
// register incoming packet into the management structure
|
|
mgmtCTick(CsnmpInPkts);
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: received %d bytes from %s.\n",
|
|
pNLE->dwBytesTransferred,
|
|
AddrToString(&pNLE->SockAddr)
|
|
));
|
|
|
|
// check manager address
|
|
if (ValidateManager(pNLE)) {
|
|
|
|
// process snmp message
|
|
if (ProcessSnmpMessage(pNLE)) {
|
|
|
|
// synchronous send
|
|
dwStatus = WSASendTo(
|
|
pNLE->Socket,
|
|
&pNLE->Buffer,
|
|
1,
|
|
&pNLE->dwBytesTransferred,
|
|
pNLE->dwFlags,
|
|
&pNLE->SockAddr,
|
|
pNLE->SockAddrLenUsed,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
// register outgoing packet into the management structure
|
|
mgmtCTick(CsnmpOutPkts);
|
|
// register outgoing Response PDU
|
|
mgmtCTick(CsnmpOutGetResponses);
|
|
|
|
// validate return code
|
|
if (dwStatus != SOCKET_ERROR) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: sent %d bytes to %s.\n",
|
|
pNLE->dwBytesTransferred,
|
|
AddrToString(&pNLE->SockAddr)
|
|
));
|
|
|
|
} else {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d sending response.\n",
|
|
WSAGetLastError()
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d receiving snmp request.\n",
|
|
dwStatus
|
|
));
|
|
}
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: --- transaction %d end ---\n",
|
|
pNLE->nTransactionId
|
|
));
|
|
|
|
LeaveCriticalSection(&g_RegCriticalSectionA);
|
|
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Public procedures //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
ProcessSnmpMessages(
|
|
PVOID pParam
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Thread procedure for processing SNMP PDUs.
|
|
|
|
Arguments:
|
|
|
|
pParam - unused.
|
|
|
|
Return Values:
|
|
|
|
Returns true if successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus;
|
|
PLIST_ENTRY pLE;
|
|
PNETWORK_LIST_ENTRY pNLE;
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: Loading Registry Parameters.\n"
|
|
));
|
|
|
|
// fire cold start trap
|
|
GenerateColdStartTrap();
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: starting pdu processing thread.\n"
|
|
));
|
|
|
|
ReportSnmpEvent(
|
|
SNMP_EVENT_SERVICE_STARTED,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
// loop
|
|
for (;;)
|
|
{
|
|
// obtain pointer to first transport
|
|
pLE = g_IncomingTransports.Flink;
|
|
|
|
// loop through incoming transports
|
|
while (pLE != &g_IncomingTransports)
|
|
{
|
|
// retreive pointer to network list entry from link
|
|
pNLE = CONTAINING_RECORD(pLE, NETWORK_LIST_ENTRY, Link);
|
|
|
|
// make sure recv is not pending
|
|
if (pNLE->dwStatus != WSA_IO_PENDING)
|
|
{
|
|
// reset completion status
|
|
pNLE->dwStatus = WSA_IO_PENDING;
|
|
|
|
// intialize address structure size
|
|
pNLE->SockAddrLenUsed = pNLE->SockAddrLen;
|
|
|
|
// initialize buffer length
|
|
pNLE->Buffer.len = NLEBUFLEN;
|
|
|
|
// re-initialize
|
|
pNLE->dwFlags = 0;
|
|
|
|
// post receive buffer
|
|
dwStatus = WSARecvFrom(
|
|
pNLE->Socket,
|
|
&pNLE->Buffer,
|
|
1, // dwBufferCount
|
|
&pNLE->dwBytesTransferred,
|
|
&pNLE->dwFlags,
|
|
&pNLE->SockAddr,
|
|
&pNLE->SockAddrLenUsed,
|
|
&pNLE->Overlapped,
|
|
RecvCompletionRoutine
|
|
);
|
|
|
|
// handle network failures
|
|
if (dwStatus == SOCKET_ERROR)
|
|
{
|
|
// retrieve last error
|
|
dwStatus = WSAGetLastError();
|
|
|
|
// if WSA_IO_PENDING everything is ok, just waiting for incoming traffic. Otherwise...
|
|
if (dwStatus != WSA_IO_PENDING)
|
|
{
|
|
// WSAECONNRESET means the last 'WSASendTo' (the one from RecvCompletionRoutine) failed
|
|
// most probably because the manager closed the socket (so we got back 'unreacheable destination port')
|
|
if (dwStatus == WSAECONNRESET)
|
|
{
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: Benign error %d posting receive buffer. Retry...\n",
|
|
dwStatus
|
|
));
|
|
|
|
// just go one more time and setup the port. It shouldn't ever loop continuously
|
|
// and hence hog the CPU..
|
|
pNLE->dwStatus = ERROR_SUCCESS;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// prepare the event log insertion string
|
|
LPTSTR pMessage = (pNLE->SockAddr.sa_family == AF_INET) ?
|
|
ERRMSG_TRANSPORT_IP :
|
|
ERRMSG_TRANSPORT_IPX;
|
|
|
|
// another error occurred. We don't know how to handle it so it is a fatal
|
|
// error for this transport. Will shut it down.
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: Fatal error %d posting receive buffer. Skip transport.\n",
|
|
dwStatus
|
|
));
|
|
|
|
ReportSnmpEvent(
|
|
SNMP_EVNT_INCOMING_TRANSPORT_CLOSED,
|
|
1,
|
|
&pMessage,
|
|
dwStatus);
|
|
|
|
// first step next with the pointer
|
|
pLE = pLE->Flink;
|
|
|
|
// delete this transport from the incoming transports list
|
|
UnloadTransport(pNLE);
|
|
|
|
// go on further
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pLE = pLE->Flink;
|
|
}
|
|
|
|
// we might want to shut the service down if no incoming transport remains.
|
|
// we might as well consider letting the service up in order to keep sending outgoing traps.
|
|
// for now, keep the service up (code below commented)
|
|
//if (IsListEmpty(&g_IncomingTransports))
|
|
//{
|
|
// ReportSnmpEvent(...);
|
|
// ProcessControllerRequests(SERVICE_CONTROL_STOP);
|
|
//}
|
|
|
|
// wait for incoming requests or indication of process termination
|
|
dwStatus = WaitForSingleObjectEx(g_hTerminationEvent, INFINITE, TRUE);
|
|
|
|
// validate return code
|
|
if (dwStatus == WAIT_OBJECT_0) {
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_TRACE,
|
|
"SNMP: SVC: exiting pdu processing thread.\n"
|
|
));
|
|
|
|
// success
|
|
return NOERROR;
|
|
|
|
} else if (dwStatus != WAIT_IO_COMPLETION) {
|
|
|
|
// retrieve error
|
|
dwStatus = GetLastError();
|
|
|
|
SNMPDBG((
|
|
SNMP_LOG_ERROR,
|
|
"SNMP: SVC: error %d waiting for request.\n",
|
|
dwStatus
|
|
));
|
|
|
|
// failure
|
|
return dwStatus;
|
|
}
|
|
}
|
|
}
|