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

848 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}
LPDWORD
RefErrStatus(
PSNMP_PDU pPdu
)
/*++
Routine Description:
Returns address of the Error Code in a PDU data structure
Arguments:
pPdu - PDU to check
Return Values:
Returns Error Code address, returns NULL if no Error Code
--*/
{
switch (pPdu->nType) {
case SNMP_PDU_GET:
case SNMP_PDU_GETNEXT:
case SNMP_PDU_SET:
return &pPdu->Pdu.NormPdu.nErrorStatus;
break;
case SNMP_PDU_GETBULK:
return &pPdu->Pdu.BulkPdu.nErrorStatus;
break;
default:
return NULL;
break;
}
}
BOOL
ValidateContext(
PNETWORK_LIST_ENTRY pNLE
)
/*++
Routine Description:
Checks access rights of given context.
Arguments:
pNLE - pointer to network list entry.
Return Values:
Returns true if manager allowed access.
--*/
{
BOOL fAccessOk = TRUE;
BOOL fOk = FALSE;
PCOMMUNITY_LIST_ENTRY pCLE = NULL;
AsnOctetString unicodeCommunity;
LPWSTR pUnicodeName;
if (pNLE->Community.length != 0)
{
unicodeCommunity.length = pNLE->Community.length * sizeof(WCHAR);
unicodeCommunity.stream = SnmpUtilMemAlloc(unicodeCommunity.length);
unicodeCommunity.dynamic = TRUE;
if (unicodeCommunity.stream == NULL)
return FALSE;
fAccessOk = (MultiByteToWideChar(
CP_ACP,
MB_PRECOMPOSED,
pNLE->Community.stream,
pNLE->Community.length,
(LPWSTR)(unicodeCommunity.stream),
unicodeCommunity.length) != 0);
}
else
{
unicodeCommunity.length = 0;
unicodeCommunity.stream = NULL;
unicodeCommunity.dynamic = FALSE;
}
// search for community string
if (fAccessOk && FindValidCommunity(&pCLE, &unicodeCommunity))
{
// check access per pdu type
if (pNLE->Pdu.nType == SNMP_PDU_SET) {
// check flags for write privileges
fAccessOk = (pCLE->dwAccess >= SNMP_ACCESS_READ_WRITE);
} else {
// check flags for read privileges
fAccessOk = (pCLE->dwAccess >= SNMP_ACCESS_READ_ONLY);
}
if (!fAccessOk) {
// Community does not have the right access
// RefErrStatus returns a pointer to the ErrorStatus field from the SNMP_PDU structure.
// It returns NULL if the PDU is in fact SNMP_TRAP_PDU. This doesn't happen here as far
// as ValidateContext is called only after ParseMessage() which is filtering out
// SNMP_TRAP_PDU.
*RefErrStatus(&pNLE->Pdu) = SNMP_ERRORSTATUS_NOSUCHNAME;
// register wrong operation for specified community into management structure
mgmtCTick(CsnmpInBadCommunityUses);
fOk = TRUE;
}
}
else
{
fAccessOk = FALSE;
// register community name failure into the management structure
mgmtCTick(CsnmpInBadCommunityNames);
}
// see if access attempt should be logged
if (!fAccessOk && snmpMgmtBase.AsnIntegerPool[IsnmpEnableAuthenTraps].asnValue.number) {
// send authentication trap
GenerateAuthenticationTrap();
}
SNMPDBG((
SNMP_LOG_TRACE,
"SNMP: SVC: %s request from community %s.\n",
fAccessOk
? "accepting"
: "rejecting"
,
CommunityOctetsToString(&(pNLE->Community), FALSE)
));
SnmpUtilOctetsFree(&unicodeCommunity);
return (fOk || fAccessOk);
}
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->nVersion,
&pNLE->Community,
&pNLE->Pdu,
pNLE->Buffer.buf,
pNLE->dwBytesTransferred
)) {
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
));
// validate context
if (ValidateContext(pNLE)) {
// process varbinds
// RefErrStatus returns a pointer to the ErrorStatus field from SNMP_PDU structure.
// The return value is NULL only if SNMP_PDU is in fact an SNMP_TRAP_PDU. Which is
// not the case here, as far as it would have been filtered out by ParseMessage().
if ((*RefErrStatus(&pNLE->Pdu) != SNMP_ERRORSTATUS_NOERROR) ||
(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 {
// 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;
}
}
}