|
|
/*++
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; } } }
|