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

888 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:
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);
dwValueSize = sizeof(szValue);
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);
}