/*++ Copyright (c) 1992-1997 Microsoft Corporation Module Name: trapthrd.c Abstract: Contains routines for trap processing thread. Environment: User Mode - Win32 Revision History: 10-Feb-1997 DonRyan Rewrote to implement SNMPv2 support. --*/ /////////////////////////////////////////////////////////////////////////////// // // // Include files // // // /////////////////////////////////////////////////////////////////////////////// #include "globals.h" #include "trapthrd.h" #include "subagnts.h" #include "snmppdus.h" #include "trapmgrs.h" #include "snmpmgrs.h" #include "network.h" #include "snmpmgmt.h" /////////////////////////////////////////////////////////////////////////////// // // // Global variables // // // /////////////////////////////////////////////////////////////////////////////// static SnmpVarBindList g_NullVbl = { NULL, 0 }; /////////////////////////////////////////////////////////////////////////////// // // // Private procedures // // // /////////////////////////////////////////////////////////////////////////////// BOOL LoadWaitObjects( DWORD * pnWaitObjects, PHANDLE * ppWaitObjects, PSUBAGENT_LIST_ENTRY ** pppNLEs ) /*++ Routine Description: Loads arrays with necessary wait object information. Arguments: pnWaitObjects - pointer to receive count of wait objects. ppWaitObjects - pointer to receive wait object handles. pppNLEs - pointer to receive array of associated subagents pointers. Return Values: Returns true if successful. --*/ { PLIST_ENTRY pLE; PSUBAGENT_LIST_ENTRY pNLE; PSUBAGENT_LIST_ENTRY * ppNLEs = NULL; PHANDLE pWaitObjects = NULL; DWORD nWaitObjects = 2; BOOL fOk = FALSE; EnterCriticalSection(&g_RegCriticalSectionB); // point to first subagent pLE = g_Subagents.Flink; // process each subagent while (pLE != &g_Subagents) { // retreive pointer to subagent list entry from link pNLE = CONTAINING_RECORD(pLE, SUBAGENT_LIST_ENTRY, Link); // check for subagent trap event if (pNLE->hSubagentTrapEvent != NULL) { // increment nWaitObjects++; } // next entry pLE = pLE->Flink; } // attempt to allocate array of subagent pointers ppNLEs = AgentMemAlloc(nWaitObjects * sizeof(PSUBAGENT_LIST_ENTRY)); // validate pointers if (ppNLEs != NULL) { // attempt to allocate array of event handles pWaitObjects = AgentMemAlloc(nWaitObjects * sizeof(HANDLE)); // validate pointer if (pWaitObjects != NULL) { // success fOk = TRUE; } else { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: could not allocate handle array.\n" )); // release array AgentMemFree(ppNLEs); // re-init ppNLEs = NULL; } } else { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: could not allocate subagent pointers.\n" )); } if (fOk) { // initialize DWORD dwIndex = 0; // point to first subagent pLE = g_Subagents.Flink; // process each subagent and check for overflow while ((pLE != &g_Subagents) && (dwIndex < nWaitObjects - 1)) { // retreive pointer to subagent list entry from link pNLE = CONTAINING_RECORD(pLE, SUBAGENT_LIST_ENTRY, Link); // check for subagent trap event if (pNLE->hSubagentTrapEvent != NULL) { // copy subagent trap event handle pWaitObjects[dwIndex] = pNLE->hSubagentTrapEvent; // copy subagent pointer ppNLEs[dwIndex] = pNLE; // next dwIndex++; } // next entry pLE = pLE->Flink; } // copy registry update event into second last entry pWaitObjects[dwIndex++] = g_hRegistryEvent; // copy termination event into last entry pWaitObjects[dwIndex++] = g_hTerminationEvent; // validate number of items if (dwIndex != nWaitObjects) { SNMPDBG(( SNMP_LOG_WARNING, "SNMP: SVC: updating number of events from %d to %d.\n", nWaitObjects, dwIndex )); // use latest number nWaitObjects = dwIndex; } } // transfer wait object information *pnWaitObjects = fOk ? nWaitObjects : 0; *ppWaitObjects = pWaitObjects; *pppNLEs = ppNLEs; LeaveCriticalSection(&g_RegCriticalSectionB); return fOk; } BOOL UnloadWaitObjects( PHANDLE pWaitObjects, PSUBAGENT_LIST_ENTRY * ppNLEs ) /*++ Routine Description: Loads arrays with necessary wait object information. Arguments: pWaitObjects - pointer to wait object handles. ppNLEs - pointer to array of associated subagents pointers. Return Values: Returns true if successful. --*/ { // release array AgentMemFree(pWaitObjects); // release array AgentMemFree(ppNLEs); return TRUE; } BOOL GenerateExtensionTrap( AsnObjectIdentifier * pEnterpriseOid, AsnInteger32 nGenericTrap, AsnInteger32 nSpecificTrap, AsnTimeticks nTimeStamp, SnmpVarBindList * pVbl ) /* Routine Description: Generates trap for subagent. Arguments: pEnterpriseOid - pointer to EnterpriseOid OID. nGenericTrap - generic trap identifier. nSpecificTrap - EnterpriseOid specific trap identifier. nTimeStamp - timestamp to include in trap. pVbl - pointer to optional variables. Return Values: Returns true if successful. */ { SNMP_PDU Pdu; BOOL fOk = FALSE; // note this is in older format Pdu.nType = SNMP_PDU_V1TRAP; // validate pointer if (pVbl != NULL) { // copy varbinds Pdu.Vbl = *pVbl; } else { // initialize Pdu.Vbl.len = 0; Pdu.Vbl.list = NULL; } // validate enterprise oid if ((pEnterpriseOid != NULL) && (pEnterpriseOid->ids != NULL) && (pEnterpriseOid->idLength != 0)) { // transfer specified enterprise oid Pdu.Pdu.TrapPdu.EnterpriseOid = *pEnterpriseOid; } else { // transfer microsoft enterprise oid // Note: transfer the AsnObjectIdentifier structure as a whole, but no new memory is allocated // for the 'ids' buffer. Hence, Pdu....EnterpriseOid should not be 'SnmpUtilFreeOid'!! Pdu.Pdu.TrapPdu.EnterpriseOid = snmpMgmtBase.AsnObjectIDs[OsnmpSysObjectID].asnValue.object; } // make sure that the system uptime is consistent by overriding Pdu.Pdu.TrapPdu.nTimeticks = nTimeStamp ? SnmpSvcGetUptime() : 0; // transfer the remaining parameters Pdu.Pdu.TrapPdu.nGenericTrap = nGenericTrap; Pdu.Pdu.TrapPdu.nSpecificTrap = nSpecificTrap; // initialize agent address structure Pdu.Pdu.TrapPdu.AgentAddr.dynamic = FALSE; Pdu.Pdu.TrapPdu.AgentAddr.stream = NULL; Pdu.Pdu.TrapPdu.AgentAddr.length = 0; // send trap to managers return GenerateTrap(&Pdu); } /////////////////////////////////////////////////////////////////////////////// // // // Public procedures // // // /////////////////////////////////////////////////////////////////////////////// BOOL ProcessSubagentEvents( ) /*++ Routine Description: Processes subagent trap events. Arguments: None. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; PSUBAGENT_LIST_ENTRY * ppNLEs = NULL; PHANDLE pWaitObjects = NULL; DWORD nWaitObjects = 0; DWORD dwIndex; // attempt to load waitable objects into array if (LoadWaitObjects(&nWaitObjects, &pWaitObjects, &ppNLEs)) { // loop for (;;) { // subagent event or termination dwIndex = WaitForMultipleObjects( nWaitObjects, pWaitObjects, FALSE, INFINITE ); // check for process termination event first // note: g_hTerminationEvent is a manual reset event if (WAIT_OBJECT_0 == WaitForSingleObject(g_hTerminationEvent, 0)) { SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: shutting down trap thread.\n" )); break; // bail... // check for registry update event next } else if (dwIndex == (WAIT_OBJECT_0 + nWaitObjects - 2)) { SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: recalling LoadWaitObjects.\n" )); if (!LoadWaitObjects(&nWaitObjects, &pWaitObjects, &ppNLEs)) break; // check for subagent trap notification event } else if (dwIndex < (WAIT_OBJECT_0 + nWaitObjects - 1)) { AsnObjectIdentifier EnterpriseOid; AsnInteger nGenericTrap; AsnInteger nSpecificTrap; AsnInteger nTimeStamp; SnmpVarBindList Vbl; PFNSNMPEXTENSIONTRAP pfnSnmpExtensionTrap; // retrieve pointer to subagent trap entry point pfnSnmpExtensionTrap = ppNLEs[dwIndex]->pfnSnmpExtensionTrap; // validate function pointer if (pfnSnmpExtensionTrap != NULL) { __try { // loop until false is returned while ((*pfnSnmpExtensionTrap)( &EnterpriseOid, &nGenericTrap, &nSpecificTrap, &nTimeStamp, &Vbl)) { // send extension trap GenerateExtensionTrap( &EnterpriseOid, nGenericTrap, nSpecificTrap, nTimeStamp, &Vbl ); SnmpUtilVarBindListFree(&Vbl); // check for process termination event while we are in this while loop if (WAIT_OBJECT_0 == WaitForSingleObject(g_hTerminationEvent, 0)) { SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: shutting down trap thread in \"while((*pfnSnmpExtensionTrap)\" loop.\n" )); break; // bail... } } } __except (EXCEPTION_EXECUTE_HANDLER) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: exception 0x%08lx polling %s.\n", GetExceptionCode(), ppNLEs[dwIndex]->pPathname )); } } } } // release memory for wait objects UnloadWaitObjects(pWaitObjects, ppNLEs); } return fOk; } BOOL GenerateTrap( PSNMP_PDU pPdu ) /* Routine Description: Generates trap for agent. Arguments: pPdu - pointer to initialized TRAP or TRAPv1 PDU. Return Values: Returns true if successful. */ { BOOL fOk = TRUE; PLIST_ENTRY pLE1; PLIST_ENTRY pLE2; PLIST_ENTRY pLE3; PNETWORK_LIST_ENTRY pNLE; PMANAGER_LIST_ENTRY pMLE; PTRAP_DESTINATION_LIST_ENTRY pTLE; AsnOctetString CommunityOctets; DWORD dwStatus; DWORD dwIPAddr; EnterCriticalSection(&g_RegCriticalSectionC); // obtain first trap destination pLE1 = g_TrapDestinations.Flink; // process each trap destination while (pLE1 != &g_TrapDestinations) { // retrieve pointer to outgoing transport structure pTLE = CONTAINING_RECORD(pLE1, TRAP_DESTINATION_LIST_ENTRY, Link); // copy community string into octet structure CommunityOctets.length = strlen(pTLE->pCommunity); CommunityOctets.stream = pTLE->pCommunity; CommunityOctets.dynamic = FALSE; // obtain first manager pLE2 = pTLE->Managers.Flink; // process each receiving manager while (pLE2 != &pTLE->Managers) { // retrieve pointer to next manager pMLE = CONTAINING_RECORD(pLE2, MANAGER_LIST_ENTRY, Link); // refresh addr UpdateMLE(pMLE); // don't send traps to addresses that are DEAD or NULL if (pMLE->dwAge == MGRADDR_DEAD || !IsValidSockAddr(&pMLE->SockAddr)) { pLE2 = pLE2->Flink; continue; } // obtain first outgoing transport pLE3 = g_OutgoingTransports.Flink; // process each outgoing transport while (pLE3 != &g_OutgoingTransports) { // retrieve pointer to outgoing transport structure pNLE = CONTAINING_RECORD(pLE3, NETWORK_LIST_ENTRY, Link); // initialize buffer length pNLE->Buffer.len = NLEBUFLEN; // can only send on same protocol if (pNLE->SockAddr.sa_family != pMLE->SockAddr.sa_family) { pLE3 = pLE3->Flink; continue; } // modify agent address if (pNLE->SockAddr.sa_family == AF_INET) { struct sockaddr_in * pSockAddrIn; DWORD szSockToBind; // see if the trap destination address is valid and if the // card to use for sending the trap could be determined if (WSAIoctl(pNLE->Socket, SIO_ROUTING_INTERFACE_QUERY, &pMLE->SockAddr, sizeof(pMLE->SockAddr), &pNLE->SockAddr, sizeof(pNLE->SockAddr), &szSockToBind, NULL, NULL) == SOCKET_ERROR) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: cannot determine interface to use for trap destination %s [err=%d].\n", inet_ntoa(((struct sockaddr_in *)&pMLE->SockAddr)->sin_addr), WSAGetLastError() )); // if we can't determine on what interface the trap will be sent from, just bail. pLE3 = pLE3->Flink; continue; } // obtain pointer to protocol specific structure pSockAddrIn = (struct sockaddr_in * )&pNLE->SockAddr; // copy agent address into temp buffer dwIPAddr = pSockAddrIn->sin_addr.s_addr; // initialize agent address structure pPdu->Pdu.TrapPdu.AgentAddr.dynamic = FALSE; pPdu->Pdu.TrapPdu.AgentAddr.stream = (LPBYTE)&dwIPAddr; pPdu->Pdu.TrapPdu.AgentAddr.length = sizeof(dwIPAddr); } else { // re-initialize agent address structure pPdu->Pdu.TrapPdu.AgentAddr.dynamic = FALSE; pPdu->Pdu.TrapPdu.AgentAddr.stream = NULL; pPdu->Pdu.TrapPdu.AgentAddr.length = 0; } // build message if (BuildMessage( SNMP_VERSION_1, &CommunityOctets, pPdu, pNLE->Buffer.buf, &pNLE->Buffer.len )) { // synchronous send dwStatus = WSASendTo( pNLE->Socket, &pNLE->Buffer, 1, &pNLE->dwBytesTransferred, pNLE->dwFlags, &pMLE->SockAddr, pMLE->SockAddrLen, NULL, NULL ); // register outgoing packet into the management structure mgmtCTick(CsnmpOutPkts); // retister outgoing trap into the management structure mgmtCTick(CsnmpOutTraps); // validate return code if (dwStatus == SOCKET_ERROR) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: error code %d on sending trap to %s.\n", WSAGetLastError(), pTLE->pCommunity )); } } // next entry pLE3 = pLE3->Flink; } // next entry pLE2 = pLE2->Flink; } // next entry pLE1 = pLE1->Flink; } LeaveCriticalSection(&g_RegCriticalSectionC); return fOk; } BOOL GenerateColdStartTrap( ) /* Routine Description: Generates cold start trap. Arguments: None. Return Values: Returns true if successful. */ { // generate cold start return GenerateExtensionTrap( NULL, // pEnterpriseOid SNMP_GENERICTRAP_COLDSTART, 0, // nSpecificTrapId 0, // nTimeStamp &g_NullVbl ); } BOOL GenerateAuthenticationTrap( ) /* Routine Description: Generates authentication trap. Arguments: None. Return Values: Returns true if successful. */ { // generate cold start return GenerateExtensionTrap( NULL, // pEnterpriseOid SNMP_GENERICTRAP_AUTHFAILURE, 0, // nSpecificTrapId SnmpSvcGetUptime(), &g_NullVbl ); }