|
|
/*++
Copyright (c) 1992-1997 Microsoft Corporation
Module Name:
snmpmgrs.c
Abstract:
Contains routines for manipulating manager structures.
Environment:
User Mode - Win32
Revision History:
10-Feb-1997 DonRyan Rewrote to implement SNMPv2 support.
--*/ ///////////////////////////////////////////////////////////////////////////////
// //
// Header files //
// //
///////////////////////////////////////////////////////////////////////////////
#include "globals.h"
#include "snmpmgrs.h"
#include "network.h"
///////////////////////////////////////////////////////////////////////////////
// //
// Public procedures //
// //
///////////////////////////////////////////////////////////////////////////////
BOOL AllocMLE( PMANAGER_LIST_ENTRY * ppMLE, LPSTR pManager )
/*++
Routine Description:
Allocates manager structure and initializes.
Arguments:
pManager - pointer to manager string.
ppMLE - pointer to receive pointer to entry.
Return Values:
Returns true if successful.
--*/
{ BOOL fOk = FALSE; PMANAGER_LIST_ENTRY pMLE = NULL; DWORD dwIpAddr; LPSTR pszManager;
// attempt to allocate structure
pMLE = AgentMemAlloc(sizeof(MANAGER_LIST_ENTRY));
// validate
if (pMLE != NULL) {
// allocate memory for manager string
pMLE->pManager = AgentMemAlloc(strlen(pManager)+1);
// validate
if (pMLE->pManager != NULL) {
// transfer manager string
strcpy(pMLE->pManager, pManager);
// Attempt to resolve manager network address
// For IPX addresses, this call always succeeds
// When SnmpSvcAddrToSocket fails, this means we deal with a dynamic IP Address
// for which the gethostbyname() failed.
if (SnmpSvcAddrToSocket(pMLE->pManager, &pMLE->SockAddr)) {
// see if tcpip address
if (pMLE->SockAddr.sa_family == AF_INET) {
// save structure size for later use
pMLE->SockAddrLen = sizeof(struct sockaddr_in);
pszManager = pMLE->pManager;
// attempt to convert address directly
dwIpAddr = inet_addr(pMLE->pManager);
// assume address is dynamic if error occurs
pMLE->fDynamicName = (dwIpAddr == SOCKET_ERROR);
// note time manager addr updated
pMLE->dwLastUpdate = GetCurrentTime();
// success
fOk = TRUE;
} else if (pMLE->SockAddr.sa_family == AF_IPX) {
// save structure size for later use
pMLE->SockAddrLen = sizeof(struct sockaddr_ipx);
// no name lookup for ipx
pMLE->fDynamicName = FALSE;
// success
fOk = TRUE; }
pMLE->dwAge = MGRADDR_ALIVE;
} else { LPTSTR tcsManager;
#ifdef UNICODE
SnmpUtilUTF8ToUnicode(&tcsManager, pMLE->pManager, TRUE); #else
tcsManager=pMLE->pManager; #endif
// at this point the address can be only an IP address!
// so we know pMLE->SockAddrLen as the size of the struct sockaddr_in!
pMLE->SockAddrLen = sizeof(struct sockaddr_in);
// since SnmpSvcAddrToSocket failed, that means inet_addr() failed hence
// we deal with a dynamic IP address
pMLE->fDynamicName = TRUE;
// set 'age' to dying
pMLE->dwAge = snmpMgmtBase.AsnIntegerPool[IsnmpNameResolutionRetries].asnValue.number;
// if the registry parameter is -1 this stands for 'keep retrying forever'
// in this case set the dwAge to the default MGRADDR_DYING(16) and never decrement it
if (pMLE->dwAge == (DWORD)-1) pMLE->dwAge = MGRADDR_DYING;
// report a warning to the system log
ReportSnmpEvent( SNMP_EVENT_NAME_RESOLUTION_FAILURE, 1, &tcsManager, 0);
#ifdef UNICODE
SnmpUtilMemFree(tcsManager); #endif
// success
fOk = TRUE; } } // cleanup
if (!fOk) { // release
FreeMLE(pMLE);
// re-init
pMLE = NULL; } }
// transfer
*ppMLE = pMLE;
return fOk; }
BOOL FreeMLE( PMANAGER_LIST_ENTRY pMLE )
/*++
Routine Description:
Releases manager structure.
Arguments:
pMLE - pointer to manager list entry to be freed.
Return Values:
Returns true if successful.
--*/
{ BOOL fOk = TRUE;
// validate pointer
if (pMLE != NULL) {
// release string
AgentMemFree(pMLE->pManager);
// release structure
AgentMemFree(pMLE); }
return TRUE; }
BOOL UpdateMLE( PMANAGER_LIST_ENTRY pMLE )
/*++
Routine Description:
Updates manager structure. An address will be resolved only if it is not marked as being 'DEAD'. A 'DEAD' address failed to be resolved for more than MGRADDR_DYING times. A 'DEAD' address will no longer be used as a trap destination, but it will still be validating the incoming SNMP requests if it could be resolve at least once since the service started up.
Arguments:
pMLE - pointer to manager list entry to be updated.
Return Values:
Returns true if successful.
--*/
{ BOOL fOk = TRUE; DWORD dwElaspedTime; struct sockaddr SockAddr;
SNMPDBG((SNMP_LOG_TRACE, "SNMP: SVC: Update manager '%s' with age %d.\n", pMLE->pManager, pMLE->dwAge));
// don't try to resolve this address if it is already dead
if (pMLE->dwAge == MGRADDR_DEAD) return FALSE;
// see if name dynamic
if (pMLE->fDynamicName) {
// determine elasped time since last update
dwElaspedTime = GetCurrentTime() - pMLE->dwLastUpdate;
// resolve the address only if it failed to be resolved on last update
// or its update time expired.
if (pMLE->dwAge != MGRADDR_ALIVE || dwElaspedTime > DEFAULT_NAME_TIMEOUT) { // attempt to resolve manager network address
// for IPX addresses, this call always succeeds
fOk = SnmpSvcAddrToSocket(pMLE->pManager, &SockAddr);
// validate
if (fOk) {
// update entry with new address
memcpy(&pMLE->SockAddr, &SockAddr, sizeof(SockAddr));
// note time dynamic name resolved
pMLE->dwLastUpdate = GetCurrentTime();
// make sure manager age is 'ALIVE'
pMLE->dwAge = MGRADDR_ALIVE;
} else if (pMLE->dwAge == MGRADDR_ALIVE) {
// Previously 'ALIVE' address cannot be resolved anymore
// set its age to the one specified by 'NameResolutionRetries' parameter in
// order to give some more chances.
pMLE->dwAge = snmpMgmtBase.AsnIntegerPool[IsnmpNameResolutionRetries].asnValue.number;
// if the registry parameter is -1 this stands for 'keep retrying forever'
// in this case set the dwAge to the default MGRADDR_DYING(16) which will never be decremented
if (pMLE->dwAge == (DWORD)-1) pMLE->dwAge = MGRADDR_DYING;
} else if (pMLE->dwAge != MGRADDR_DEAD) {
// the address could not be resolved before and it still cannot be resolved
// decrement its retry counter only if the 'NameResolutionRetries' parameter says so
if (snmpMgmtBase.AsnIntegerPool[IsnmpNameResolutionRetries].asnValue.number != -1) pMLE->dwAge--; } } }
return fOk; }
BOOL FindManagerByName( PMANAGER_LIST_ENTRY * ppMLE, PLIST_ENTRY pListHead, LPSTR pManager )
/*++
Routine Description:
Locates manager in list.
Arguments:
ppMLE - pointer to receive pointer to entry.
pListHead - pointer to head of manager list.
pManager - pointer to manager to find.
Return Values:
Returns true if successful.
--*/
{ PLIST_ENTRY pLE; PMANAGER_LIST_ENTRY pMLE;
// initialize
*ppMLE = NULL;
// obtain pointer to list head
pLE = pListHead->Flink;
// process all entries in list
while (pLE != pListHead) {
// retrieve pointer to community structure
pMLE = CONTAINING_RECORD(pLE, MANAGER_LIST_ENTRY, Link);
// compare community string with entry
if (!strcmp(pMLE->pManager, pManager)) {
// transfer
*ppMLE = pMLE;
// success
return TRUE; }
// next entry
pLE = pLE->Flink; }
// failure
return FALSE; }
BOOL IsManagerAddrLegal( struct sockaddr_in * pAddr ) { DWORD dwHostMask; DWORD dwAddress = ntohl(pAddr->sin_addr.S_un.S_addr);
// check address legality only for Ip addresses
if (pAddr->sin_family != AF_INET) return TRUE;
// disallow multicast (or future use) source addresses
// local broadcast will be filtered out here as well
if ((dwAddress & 0xe0000000) == 0xe0000000) return FALSE;
// get hostmask for class 'C' addresses
if ((dwAddress & 0xc0000000) == 0xc0000000) dwHostMask = 0x000000ff;
// get hostmask for class 'B' addresses
else if ((dwAddress & 0x80000000) == 0x80000000) dwHostMask = 0x0000ffff;
// get hostidmask for class 'A' addresses
else dwHostMask = 0x00ffffff;
SNMPDBG((SNMP_LOG_TRACE,"SNMP: dwAddress=%08x, dwHostMask=%08x, port=%d\n", dwAddress, dwHostMask, ntohs(pAddr->sin_port)));
return ((dwAddress & dwHostMask) != 0 // check against net address
&& ((dwAddress & dwHostMask) != (0x00ffffff & dwHostMask)) // check against broadcast address
// && ntohs(pAddr->sin_port) >= 1024 // check against reserved port
); }
BOOL FindManagerByAddr( PMANAGER_LIST_ENTRY * ppMLE, struct sockaddr * pSockAddr )
/*++
Routine Description:
Locates permitted manager in list.
Arguments:
ppMLE - pointer to receive pointer to entry.
pSockAddr - pointer to socket address to find.
Return Values:
Returns true if successful.
--*/
{ PLIST_ENTRY pLE; PMANAGER_LIST_ENTRY pMLE; DWORD dwSockAddrLen; enum { SRCH_ALIVE, SRCH_DYING, SRCH_DONE } state;
// initialize
*ppMLE = NULL;
// loop twice through the list of permitted managers
// in the first loop look only through 'ALIVE' managers
// in the second loop look through the 'DYING' or 'DEAD' managers.
// ... this logic minimizes the response time for regular SNMP requests,
// as far as there is a bigger chance to have the request comming from an 'ALIVE' manager.
// otherwise, gethostbyname() called in UpdateMLE() lasts about 1/2 sec!!!
for (state = SRCH_ALIVE, pLE = g_PermittedManagers.Flink; state != SRCH_DONE; pLE=pLE->Flink) { // retrieve pointer to manager structure
pMLE = CONTAINING_RECORD(pLE, MANAGER_LIST_ENTRY, Link);
// if we are in the first loop ..
if (state == SRCH_ALIVE) { // .. but reached its end ..
if (pLE == &g_PermittedManagers) { // .. go further with the second loop
state = SRCH_DYING; continue; }
// .. pass over the managers that are not 'ALIVE'
if (pMLE->dwAge != MGRADDR_ALIVE) continue; }
// if we are in the second loop ..
if (state == SRCH_DYING) { // .. but reached its end ..
if (pLE == &g_PermittedManagers) { // .. mark the end of scanning
state = SRCH_DONE; continue; }
// .. pass over the managers that are 'ALIVE'
if (pMLE->dwAge == MGRADDR_ALIVE || pMLE->dwAge == MGRADDR_DEAD) continue; }
// update name:
// 'DEAD' addresses will no longer be resolved,
// 'DYING' addresses will be given another chance to resolve until they become 'DEAD'
// 'ALIVE' addresses that fail to resolve will become 'DYING'
// next, all managers with a valid address will participate to validation (see below)
UpdateMLE(pMLE);
// compare address families
if (IsValidSockAddr(&pMLE->SockAddr) && pMLE->SockAddr.sa_family == pSockAddr->sa_family) { // determine address family
if (pMLE->SockAddr.sa_family == AF_INET) { struct sockaddr_in * pSockAddrIn1; struct sockaddr_in * pSockAddrIn2;
// obtain pointer to protocol specific structure
pSockAddrIn1= (struct sockaddr_in *)pSockAddr; pSockAddrIn2= (struct sockaddr_in *)&pMLE->SockAddr;
// acknowledge this manager only if its address matches
// a permitted manager with a valid (not NULL) IP address.
// This is tested regardless the 'dwAge' of the permitted manager.
if (!memcmp(&pSockAddrIn1->sin_addr, &pSockAddrIn2->sin_addr, sizeof(pSockAddrIn2->sin_addr))) {
// transfer
*ppMLE = pMLE;
// success
return TRUE; } } else if (pMLE->SockAddr.sa_family == AF_IPX) {
struct sockaddr_ipx * pSockAddrIpx1; struct sockaddr_ipx * pSockAddrIpx2;
// obtain pointer to protocol specific structure
pSockAddrIpx1= (struct sockaddr_ipx *)pSockAddr; pSockAddrIpx2= (struct sockaddr_ipx *)&pMLE->SockAddr;
// acknowledge this manager only if its ipx address matches a
// permitted manager with a valid (nodenum != 0) IPX address.
// This is tested regardless the 'dwAge' of the permitted manager.
if (!memcmp(pSockAddrIpx1->sa_netnum, pSockAddrIpx2->sa_netnum, sizeof(pSockAddrIpx2->sa_netnum)) && !memcmp(pSockAddrIpx1->sa_nodenum, pSockAddrIpx2->sa_nodenum, sizeof(pSockAddrIpx2->sa_nodenum))) {
// transfer
*ppMLE = pMLE;
// success
return TRUE; } } } }
// failure
return FALSE; }
BOOL AddManager( PLIST_ENTRY pListHead, LPSTR pManager )
/*++
Routine Description:
Adds manager structure to list.
Arguments:
pListHead - pointer to head of list.
pManager - pointer to manager to add.
Return Values:
Returns true if successful.
--*/
{ BOOL fOk = FALSE; PMANAGER_LIST_ENTRY pMLE = NULL;
// attempt to locate in list
if (FindManagerByName(&pMLE, pListHead, pManager)) { SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: updating manager %s.\n", pManager ));
// success
fOk = TRUE;
} else {
// allocate manager structure
if (AllocMLE(&pMLE, pManager)) { SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: adding manager %s.\n", pManager ));
// insert into managers list
InsertTailList(pListHead, &pMLE->Link);
// success
fOk = TRUE; } }
return fOk; }
BOOL LoadManagers( HKEY hKey, PLIST_ENTRY pListHead )
/*++
Routine Description:
Constructs list of permitted managers.
Arguments:
hKey - registry key containing manager values.
pListHead - pointer to head of list.
Return Values:
Returns true if successful.
--*/
{ LONG lStatus; DWORD dwIndex; DWORD dwNameSize; DWORD dwValueSize; DWORD dwValueType; CHAR szName[MAX_PATH]; CHAR szValue[MAX_PATH]; // buffer for holding the translation UNICODE->UTF8
BOOL fOk = FALSE; // initialize
dwIndex = 0; lStatus = ERROR_SUCCESS;
// loop until error or end of list
while (lStatus == ERROR_SUCCESS) { // initialize buffer sizes
dwNameSize = sizeof(szName)/sizeof(szName[0]); // size in TCHARs
dwValueSize = sizeof(szValue); // size in bytes
szValue[0] = '\0';
// read next value
lStatus = RegEnumValueA( hKey, dwIndex, szName, &dwNameSize, NULL, &dwValueType, szValue, &dwValueSize );
// validate return code
if (lStatus == ERROR_SUCCESS) { szValue[dwValueSize]='\0';
if (AddManager(pListHead, szValue)) // add valid manager to manager list
dwIndex++; //next
else lStatus = ERROR_NOT_ENOUGH_MEMORY; // reset status to reflect failure
} else if (lStatus == ERROR_NO_MORE_ITEMS) fOk = TRUE; // success
} return fOk; }
BOOL UnloadManagers( PLIST_ENTRY pListHead )
/*++
Routine Description:
Destroys list of permitted managers.
Arguments:
pListHead - pointer to head of list.
Return Values:
Returns true if successful.
--*/
{ PLIST_ENTRY pLE; PMANAGER_LIST_ENTRY pMLE;
// process entries until empty
while (!IsListEmpty(pListHead)) {
// extract next entry
pLE = RemoveHeadList(pListHead);
// retrieve pointer to manager structure
pMLE = CONTAINING_RECORD(pLE, MANAGER_LIST_ENTRY, Link); // release
FreeMLE(pMLE); }
return TRUE; }
BOOL LoadPermittedManagers( BOOL bFirstCall )
/*++
Routine Description:
Constructs list of permitted managers.
Arguments:
None.
Return Values:
Returns true if successful.
--*/
{ HKEY hKey; LONG lStatus; BOOL fPolicy; LPTSTR pszKey; BOOL fOk = FALSE; SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: loading permitted managers.\n" ));
#ifdef _POLICY
// we need to provide precedence to the parameters set through the policy
fPolicy = TRUE; #else
fPolicy = FALSE; #endif
do { // if the policy is to be enforced, check the policy registry location first
pszKey = fPolicy ? REG_POLICY_PERMITTED_MANAGERS : REG_KEY_PERMITTED_MANAGERS;
// open registry subkey
lStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszKey, 0, KEY_READ, &hKey ); // if the call succeeded or we were not checking the policy, break the loop
if (lStatus == ERROR_SUCCESS || !fPolicy) break;
// being at this point, this means we were checking for the policy parameters.
// If and only if the policy is not defined (registry key is missing) we
// reset the error, mark 'fPolicy already tried' and go back into the loop
if (lStatus == ERROR_FILE_NOT_FOUND) { lStatus = ERROR_SUCCESS; fPolicy = FALSE; } } while (lStatus == ERROR_SUCCESS);
// validate return code
if (lStatus == ERROR_SUCCESS) { // call routine to load managers into global list
LoadManagers(hKey, &g_PermittedManagers);
// close key
RegCloseKey(hKey);
// at this point consider success (errors localized at particular managers were logged already)
fOk = TRUE; } else // it doesn't matter how the values are, the key has to exist,
// so mark as bFirstCall in order to log an event if this is not true.
bFirstCall = TRUE; if (!fOk) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error %d processing PermittedManagers subkey.\n", lStatus ));
// report an error only if on first call (service initialization)
// otherwise, due to registry operations through regedit, the event log
// might be flooded with records
if (bFirstCall) // report event
ReportSnmpEvent( SNMP_EVENT_INVALID_REGISTRY_KEY, 1, &pszKey, lStatus ); }
return fOk; }
BOOL UnloadPermittedManagers( )
/*++
Routine Description:
Destroys list of permitted managers.
Arguments:
None.
Return Values:
Returns true if successful.
--*/
{ // call common routine with global list
return UnloadManagers(&g_PermittedManagers); }
|