// wsnmp_no.c // // WinSNMP Notification Functions and helpers // Copyright 1995-1997 ACE*COMM Corp // Rleased to Microsoft under Contract // Beta 1 version, 970228 // Bob Natale (bnatale@acecomm.com) // // 970310 - Added callback session support // - Added v2 to v1 trap mapping // - Refined v1 to v2 trap mapping // #include "winsnmp.inc" // BOOL DispatchTrap (LPSAS host, smiLPOCTETS community, LPPDUS pdu); BOOL MapV1TrapV2 (LPPDUS pdu); smiUINT32 ParsePduHdr (smiLPBYTE msgPtr, smiUINT32 msgLen, smiLPUINT32 version, smiLPINT type, smiLPUINT32 reqID); BOOL SetPduType (smiLPBYTE msgPtr, smiUINT32 msgLen, int pduType); THR_TYPE WINAPI thrNotify (LPVOID); smiUINT32 sysUpTimeValue[9] = {1,3,6,1,2,1,1,3,0}; smiOID sysUpTimeName = {9, sysUpTimeValue}; smiUINT32 snmpTrapOidValue[11] = {1,3,6,1,6,3,1,1,4,1,0}; smiOID snmpTrapOidName = {11, snmpTrapOidValue}; smiUINT32 snmpTrapsValue[10] = {1,3,6,1,6,3,1,1,5,999}; smiOID snmpTrapsName = {10, snmpTrapsValue}; smiUINT32 snmpTrapEntValue[11] = {1,3,6,1,6,3,1,1,4,3,0}; smiOID snmpTrapEntName = {11, snmpTrapEntValue}; smiUINT32 snmpTrapAddrValue[7] = {1,3,6,1,3,1057,1}; smiOID snmpTrapAddrName = {7, snmpTrapAddrValue}; void MsgNotify (smiUINT32 msgType, smiLPBYTE msgAddr, smiUINT32 msgLen, smiUINT32 nAgent, LPSAS host) { DWORD nFound; DWORD nMsg; smiUINT32 version; smiINT pduType; smiUINT32 reqID; LPSESSION pSession; LPSNMPMSG pMsg; nMsg = ParsePduHdr (msgAddr, msgLen, &version, &pduType, &reqID); if (nMsg != 0) // ParsePduHdr returns 0 == no_error { if (msgAddr) GlobalFree (msgAddr); return; } switch (msgType) { case NP_TRAP: if (TrapDescr.Used && // Discard traps if no registrations (pduType == SNMP_PDU_INFORM || pduType == SNMP_PDU_TRAP || pduType == SNMP_PDU_V1TRAP)) { smiLPOCTETS community; LPPDUS pdu; BOOL bConvert; pdu = GlobalAlloc (GPTR, sizeof(PDUS)); if (pdu == NULL) goto DONE_TRAP; nMsg = ParseMessage (msgAddr, msgLen, &version, &community, pdu); if (nMsg != 0) // ParseMessage returns 0 == no_error goto DONE_PDU; if (pduType == SNMP_PDU_INFORM) { // Send the Inform acknowledgment response SOCKET s; SetPduType (msgAddr, msgLen, SNMP_PDU_RESPONSE); if (host->ipx.sa_family == AF_IPX) s = TaskData.ipxSock; else s = TaskData.ipSock; sendto (s, msgAddr, msgLen, 0, (LPSOCKADDR)host, sizeof(SOCKADDR)); SetPduType (msgAddr, msgLen, SNMP_PDU_INFORM); } bConvert = TRUE; if (pduType == SNMP_PDU_V1TRAP) // If v1 trap... bConvert = MapV1TrapV2 (pdu); // convert to v2 trap if (bConvert) DispatchTrap (host, community, pdu); // always v2 here // Cleanup is the same regardless of success or failure FreeVarBindList (pdu->VBL_addr); // Checks for NULL FreeV1Trap (pdu->v1Trap); // ditto FreeOctetString (community); // ditto DONE_PDU: GlobalFree (pdu); } // end_if Trap_or_Inform PDU DONE_TRAP: GlobalFree (msgAddr); return; // end_case NP_TRAP case NP_RESPONSE: if (pduType != SNMP_PDU_RESPONSE) { GlobalFree (msgAddr); return; } EnterCriticalSection (&cs_MSG); for (nFound=0, nMsg=0; nFoundSession) continue; nFound++; if ((pMsg->Status == NP_SENT) && // Must have been sent! (pMsg->dllReqId == reqID)) // Must match up! { pMsg->Status = NP_RCVD; // ResponsePDU! // Release sent packet message if (pMsg->Addr) GlobalFree (pMsg->Addr); // Point to received packet message pMsg->Addr = msgAddr; pMsg->Size = msgLen; LeaveCriticalSection (&cs_MSG); pSession = snmpGetTableEntry(&SessDescr, HandleToUlong(pMsg->Session) - 1); if (pSession->fCallBack) { // callback session notification mode EnterCriticalSection (&cs_SESSION); if (pSession->thrHandle) { if (pSession->thrCount != 0xFFFFFFFF) pSession->thrCount++; SetEvent (pSession->thrEvent); } else FreeMsg (nMsg); LeaveCriticalSection (&cs_SESSION); } else { // window/message session notification mode if (IsWindow(pSession->hWnd)) { pMsg->Status = NP_READY; PostMessage (pSession->hWnd, pSession->wMsg, 0, pMsg->appReqId); } else FreeMsg (nMsg); } return; // Matched response with request } // end_if } // end_for // If we fall through the for loop without finding a match, // this must be a spurious message from agent...discard GlobalFree (msgAddr); LeaveCriticalSection (&cs_MSG); return; // end_case NP_RESPONSE case NP_REQUEST: // To allow for AgentX Master Agents and Mid-Level-Managers // any type of PDU may be accepted on this channel - BobN 4/8/97 // Get a msg slot EnterCriticalSection (&cs_MSG); if (snmpAllocTableEntry(&MsgDescr, &nMsg) != SNMPAPI_SUCCESS) { LeaveCriticalSection(&cs_MSG); GlobalFree (msgAddr); return; } pMsg = snmpGetTableEntry(&MsgDescr, nMsg); pMsg->Session = ((LPAGENT)snmpGetTableEntry(&AgentDescr, nAgent))->Session; pMsg->Status = NP_RCVD; // In-bound request pMsg->Type = pduType; pMsg->Addr = msgAddr; pMsg->Size = msgLen; pMsg->appReqId = pMsg->dllReqId = reqID; CopyMemory (&(pMsg->Host), host, sizeof(SAS)); LeaveCriticalSection (&cs_MSG); pSession = snmpGetTableEntry(&SessDescr, HandleToUlong(pMsg->Session) - 1); if (pSession->fCallBack) { // callback session notification mode EnterCriticalSection (&cs_SESSION); if (pSession->thrHandle) { if (pSession->thrCount != 0xFFFFFFFF) pSession->thrCount++; SetEvent (pSession->thrEvent); } else { FreeMsg (nMsg); } LeaveCriticalSection (&cs_SESSION); } else { if (IsWindow(pSession->hWnd)) { pMsg->Status = NP_READY; PostMessage (pSession->hWnd, pSession->wMsg, 0, pMsg->appReqId); } else FreeMsg (nMsg); } break; default: GlobalFree (msgAddr); break; } // end_switch msgType return; } // end_MsgNotify THR_TYPE WINAPI thrNotify (LPVOID cbSessionSlot) { DWORD nSes = (DWORD)((DWORD_PTR)cbSessionSlot); HSNMP_SESSION hSession = (HSNMP_SESSION) ULongToPtr(nSes + 1); DWORD nUsed, nMsg; WPARAM wParam; LPARAM lParam; BOOL bFound, bWillBlock; LPSESSION pSession; LPSNMPMSG pMsg; // pSession->thrCount counts the number of requests. External threads increment it // each time they know something has changed in the message table (message expired or received) // thrNotify decrements it each time it scans the message table. do { EnterCriticalSection (&cs_SESSION); pSession = snmpGetTableEntry(&SessDescr, nSes); if (pSession->thrCount != 0xFFFFFFFF && pSession->thrCount != 0) pSession->thrCount-- ; bWillBlock = pSession->thrCount == 0; LeaveCriticalSection (&cs_SESSION); // The thread will block only if the pSession->thrCount was 0 (tested in critical // section). It will be unblocked by external threads, from inside the same critical section. if (bWillBlock) WaitForSingleObject (pSession->thrEvent, INFINITE); // termination is requested, just break the loop if (pSession->thrCount == 0xFFFFFFFF) break; bFound = FALSE; // Find a waiting Msg for this session to process EnterCriticalSection (&cs_MSG); for (nUsed=0, nMsg=0; nUsedSession == hSession && (pMsg->Status == NP_RCVD || pMsg->Status == NP_EXPIRED)) { // the message was found. It might be already received, or it might // be timed out. Either case, the notification function has to be called. wParam = pMsg->Status == NP_RCVD ? 0 : SNMPAPI_TL_TIMEOUT ; lParam = pMsg->appReqId; if (wParam == SNMPAPI_TL_TIMEOUT) FreeMsg(nMsg); // no more need for this expired bugger else pMsg->Status = NP_READY; // mark it for SnmpRecvMsg() bFound = TRUE; // as the message was found, no reason to loop further break; } // update nFound to avoid searching more than the messages available nUsed += (pMsg->Session != 0); } LeaveCriticalSection (&cs_MSG); if (bFound) { //if a message was found for this session, call the notification function (*(pSession->fCallBack)) (hSession, pSession->hWnd, pSession->wMsg, wParam, lParam, pSession->lpClientData); } } while (TRUE); _endthreadex(0); return (0); } // end_thrNotify THR_TYPE WINAPI thrManager (LPVOID xSock) { DWORD iBytes; int nSock; fd_set readFDS; SOCKET *pSock = (SOCKET*)xSock; SOCKET tSock = *pSock; SAS host; smiLPBYTE rMsgPtr; int fLen; struct timeval tvTimeout; // timeout for select // select with timeout so that we can response to hExitEvent in signaled state tvTimeout.tv_sec = 1; // 1 sec. timeout value tvTimeout.tv_usec = 0; // When select returns, the contents of the // tvTimeout structure are not altered. while (TRUE) { if (WAIT_OBJECT_0 == WaitForSingleObject (TaskData.hExitEvent, 0)) { goto DONE; // we're asked to exit } FD_ZERO (&readFDS); // Note: strategy used in this block to assign a value // to "fLen" is important for Solaris and benign for Win32 FD_SET (tSock, &readFDS); fLen = (int)tSock; fLen++; // Must preserve value of fLen across loops nSock = select (fLen, &readFDS, NULL, NULL, &tvTimeout); if (0 == nSock) { // timeout continue; } else if (nSock == SOCKET_ERROR || *pSock == INVALID_SOCKET) { goto DONE; // terminate thread } // Only one socket monitored per thread, hence // FD_ISSET can be safely assumed at this point nSock = ioctlsocket (tSock, FIONREAD, &iBytes); if (nSock == SOCKET_ERROR || *pSock == INVALID_SOCKET) goto DONE; // Find the message buffer address... rMsgPtr = GlobalAlloc (GPTR, iBytes); if (rMsgPtr == NULL) { // No space error...throw away the message... recvfrom (tSock, (LPSTR)&nSock, 1, 0, NULL, NULL); if (*pSock == INVALID_SOCKET) goto DONE; // ...and call it quits. continue; } nSock = sizeof(SAS); // get the datagram and the address of the host that sent it iBytes = recvfrom (tSock, rMsgPtr, iBytes, 0, (LPSOCKADDR)&host, &nSock); if (iBytes != SOCKET_ERROR && *pSock != INVALID_SOCKET) MsgNotify (NP_RESPONSE, rMsgPtr, iBytes, 0, &host); else GlobalFree(rMsgPtr); // BUG# 458343 } // end_while DONE: return (0); } // end_thrManager THR_TYPE WINAPI thrTimer (LPVOID nTask) { // Clean-up any timed-out messages BOOL bFree; DWORD lTicks, nMsg; DWORD nFound; SOCKET tSock; LPSNMPMSG pMsg; // This thread won't be needed immediately upon creation. // It sleeps/suspends itself as appropriate. // SnmpSendMsg() resumes it for each message sent // SnmpCleanup() resumes it to signal termination. while (TRUE) {// Once per second granularity Sleep (1000); // Check for termination request if (TaskData.timerThread == NULL) goto DONE; // If no msgs, go back to sleep if (MsgDescr.Used == 0) continue; EnterCriticalSection (&cs_MSG); for (nMsg=0, nFound=0; nFoundSession) // Skip unused slots continue; nFound++; // Signal break when last used slot is processed if (pMsg->Status != NP_SENT) // Skip pending and continue; // received messages lTicks = GetTickCount(); // update current time // Following test revised on 10/18/96 by BobN // Message "tick-time" rather than "TTL" now stored in MSG // to enable test for Windows timer wrap (49.7 days) // pMsg->PolicyTries - pMsg->Tries is the number of retries so far if ((pMsg->PolicyTries - pMsg->Tries+1)*pMsg->Wait + pMsg->Ticks > lTicks && // MSG TTL test pMsg->Ticks <= lTicks) // Timer wrap test continue; // Retain the message bFree = TRUE; // Prepare to free the message slot if (pMsg->nRetransmitMode) { if (pMsg->Tries) { LPENTITY pEntity; //WriteSocket (nMsg); // Determine which socket to use if (pMsg->Host.ipx.sa_family == AF_IPX) tSock = TaskData.ipxSock; else tSock = TaskData.ipSock; // Send the data sendto (tSock, pMsg->Addr, pMsg->Size, 0, (LPSOCKADDR)&(pMsg->Host), sizeof(SAS)); // Need to check for SOCKET_ERROR! // end_WriteSocket //pMsg->Ticks is the time for the first SnmpSendMessage pMsg->Tries--; // Record the attempt // update the destination entity's nActualRetry EnterCriticalSection (&cs_ENTITY); // locate the agent (source) entity here pEntity = snmpGetTableEntry(&EntsDescr, HandleToUlong(pMsg->agentEntity)-1); // update the nActualRetry param of the agent (source) entity pEntity->nActualRetry = pMsg->PolicyTries - pMsg->Tries; LeaveCriticalSection (&cs_ENTITY); if (!pMsg->Tries) // No further retries? { // Release buffer space GlobalFree (pMsg->Addr); pMsg->Addr = NULL; pMsg->Size = 0; } bFree = FALSE; // Retain the message slot } else { LPSESSION pSession; pSession = snmpGetTableEntry(&SessDescr, HandleToUlong(pMsg->Session) - 1); if (pSession->fCallBack) { // callback session notification mode EnterCriticalSection (&cs_SESSION); if (pSession->thrHandle) { bFree = FALSE; // thrNotify will free it pMsg->Status = NP_EXPIRED; if (pSession->thrCount != 0xFFFFFFFF) pSession->thrCount++; SetEvent (pSession->thrEvent); } LeaveCriticalSection (&cs_SESSION); } else { // windows/message session notification mode if (IsWindow(pSession->hWnd)) { PostMessage (pSession->hWnd, pSession->wMsg, SNMPAPI_TL_TIMEOUT, pMsg->appReqId); } } } // end_else (no retry left) } // end_if (retransmitMode) if (bFree) FreeMsg (nMsg); } // end_for LeaveCriticalSection (&cs_MSG); } // end_while DONE: return (0); } // end_thrTimer // Client side trap processing for Win32 THR_TYPE WINAPI thrTrap (LPVOID lpTask) { #define TRAPBUFSIZE 4096 typedef struct { SOCKADDR Addr; int AddrLen; UINT TrapBufSz; char TrapBuf[TRAPBUFSIZE]; } SNMP_TRAP, *PSNMP_TRAP; SNMP_TRAP recvTrap; DWORD iBytes; smiLPBYTE rMsgPtr; BOOL bSuccess; // // Approach differs for NT (SNMPTRAP) vs '95 // if (TaskData.sEnv.dwPlatformId == VER_PLATFORM_WIN32_NT) { // IMPORTANT NOTE FOR NT: // This code must be consistent with the SNMPTRAP code // wrt TRAPBUFSIZE, SNMPTRAPPIPE, and the SNMP_TRAP struct while (TRUE) { // check if thread termination required, Bug# 270672 if (WAIT_OBJECT_0 == WaitForSingleObject (TaskData.trapEvent, 0)) { break; } if (ReadFile( TaskData.trapPipe, (LPBYTE)&recvTrap, sizeof(SNMP_TRAP) - sizeof(recvTrap.TrapBuf), // message header size &iBytes, &TaskData.trapOl)) { // It is impossible for ReadFile to succeed because // the specified number of bytes to read is less than // the size of the next message. break; } else { if (ERROR_IO_PENDING == GetLastError()) { // block to wait until the reading of pipe message header completes // or we need to exit this thread bSuccess = GetOverlappedResult( TaskData.trapPipe, &TaskData.trapOl, &iBytes, TRUE); // Block // check if thread termination required if (WAIT_OBJECT_0 == WaitForSingleObject (TaskData.trapEvent, 0)) { break; } // reset event to non-signaled state for next I/O ResetEvent(TaskData.trapOl.hEvent); if (!bSuccess) { if (ERROR_MORE_DATA != GetLastError()) { break; } // we are here because the last overlapped read completes but we supplied // a smaller buffer than the pipe message, we'll read the rest of the message // below. } } else if (ERROR_MORE_DATA != GetLastError()) { break; } } // Find the message buffer address... rMsgPtr = GlobalAlloc (GPTR, 2*recvTrap.TrapBufSz); if (rMsgPtr == NULL) { // No space error: E_OUTOFMEMORY break; } // read the rest of the pipe message if ( !ReadFile( TaskData.trapPipe, (LPBYTE)rMsgPtr, 2*recvTrap.TrapBufSz, &iBytes, &TaskData.trapOl)) { if (ERROR_IO_PENDING == GetLastError()) { // block to wait until the reading of pipe message body completes // or we need to exit this thread bSuccess = GetOverlappedResult( TaskData.trapPipe, &TaskData.trapOl, &iBytes, TRUE); // Block // check if thread termination required if (WAIT_OBJECT_0 == WaitForSingleObject (TaskData.trapEvent, 0)) { GlobalFree(rMsgPtr); break; } // reset event to non-signaled state for next I/O ResetEvent(TaskData.trapOl.hEvent); if (!bSuccess) { GlobalFree(rMsgPtr); break; } } else { GlobalFree(rMsgPtr); break; } } // get the datagram and the address of the host that sent it MsgNotify (NP_TRAP, rMsgPtr, iBytes, 0, (LPSAS)&recvTrap.Addr); } // end while() } // end_NT_thrTrap return (0); } // end_Win32_thrTrap THR_TYPE WINAPI thrAgent (LPVOID newAgent) { DWORD iBytes; int iLen; fd_set readFDS; SAS host; smiLPBYTE rMsgPtr; DWORD nAgent = (DWORD)((DWORD_PTR)newAgent); SOCKET sAgent = ((LPAGENT)snmpGetTableEntry(&AgentDescr, nAgent))->Socket; // fLen logic needed for Solaris; ignored in Win32 int fLen = (int)sAgent + 1; while (TRUE) { FD_ZERO (&readFDS); FD_SET (sAgent, &readFDS); // Must preserve value of fLen acroos loops iLen = select (fLen, &readFDS, NULL, NULL, NULL); if (iLen == SOCKET_ERROR) goto DONE; // Only one socket per thread, therefore // we can safely assume FD_ISSET here iLen = ioctlsocket (sAgent, FIONREAD, &iBytes); if (iLen == SOCKET_ERROR) goto DONE; if (iBytes == 0) continue; // Find the message buffer address... rMsgPtr = GlobalAlloc (GPTR, iBytes); if (rMsgPtr == NULL) { // No space error...throw away the message... recvfrom (sAgent, (LPSTR)&iLen, 1, 0, NULL, NULL); // ...and call it quits. continue; } iLen = sizeof(SAS); // get the datagram and the address of the host that sent it iBytes = recvfrom (sAgent, rMsgPtr, iBytes, 0, (LPSOCKADDR)&host, &iLen); if (iBytes != SOCKET_ERROR) MsgNotify (NP_REQUEST, rMsgPtr, iBytes, nAgent, &host); else GlobalFree(rMsgPtr); } // end_while DONE: return (0); } // end_thrAgent BOOL MapV1TrapV2 (LPPDUS pdu) { LPVARBIND VbTicks = NULL; LPVARBIND VbTrap = NULL; LPVARBIND VbAddress = NULL; LPVARBIND VbEnterprise = NULL; LPVARBIND endPtr; smiLPUINT32 ptrTrap; smiUINT32 lenTrap; if (!pdu) return (FALSE); // Adjust "generic" for v2 values pdu->v1Trap->generic_trap++; // as oid in v2 is v1 # +1 if (pdu->v1Trap->generic_trap == 7) // specific? pdu->v1Trap->generic_trap = 0; // v2 // rfc1908:(2) If a Trap-PDU is received, then it is mapped into a // SNMPv2-Trap-PDU. This is done by prepending onto the variable-bindings // field two new bindings: sysUpTime.0 [12], which takes its value from the // timestamp field of the Trap-PDU; and....... if (!(VbTicks = (LPVARBIND)GlobalAlloc (GPTR, sizeof(VARBIND)))) return (FALSE); if (SnmpOidCopy (&sysUpTimeName, &VbTicks->name) == SNMPAPI_FAILURE) goto ERROROUT; VbTicks->value.syntax = SNMP_SYNTAX_TIMETICKS; VbTicks->value.value.uNumber = pdu->v1Trap->time_ticks; // ..... snmpTrapOID.0 [13], which is calculated thusly: if the value of // generic-trap field is `enterpriseSpecific', then the value used is the // concatenation of the enterprise field from the Trap-PDU with two additional // sub-identifiers, `0', and the value of the specific-trap field; otherwise, // the value of the corresponding trap defined in [13] is used. (For example, // if the value of the generic-trap field is `coldStart', then the coldStart // trap [13] is used.) if (!(VbTrap = (LPVARBIND)GlobalAlloc (GPTR, sizeof(VARBIND)))) goto ERROROUT; if (SnmpOidCopy (&snmpTrapOidName, &VbTrap->name) == SNMPAPI_FAILURE) goto ERROROUT; VbTrap->value.syntax = SNMP_SYNTAX_OID; if (snmpTrapsValue[9] = pdu->v1Trap->generic_trap) // Deliberate assignment { // SNMP_TRAP_GENERIC lenTrap = sizeof(snmpTrapsValue); VbTrap->value.value.oid.len = lenTrap / sizeof(smiUINT32); ptrTrap = snmpTrapsValue; } else { // SNMP_TRAP_ENTERPRISE lenTrap = pdu->v1Trap->enterprise.len * sizeof(smiUINT32); VbTrap->value.value.oid.len = pdu->v1Trap->enterprise.len + 2; ptrTrap = pdu->v1Trap->enterprise.ptr; } if (!(VbTrap->value.value.oid.ptr = (smiLPUINT32)GlobalAlloc (GPTR, VbTrap->value.value.oid.len * sizeof(smiUINT32)))) goto ERROROUT; CopyMemory (VbTrap->value.value.oid.ptr, ptrTrap, lenTrap); if (!pdu->v1Trap->generic_trap) { // SNMP_TRAP_ENTERPRISE VbTrap->value.value.oid.ptr[pdu->v1Trap->enterprise.len+1] = pdu->v1Trap->specific_trap; } // Special code to retain v1Trap AgentAddress info in an experimental object // This is *not* part of the WinSNMP v2.0 standard at this time (6/25/98) if (TaskData.conveyAddress != SNMPAPI_ON) goto DO_ENTERPRISE; if (!(VbAddress = (LPVARBIND)GlobalAlloc (GPTR, sizeof(VARBIND)))) goto ERROROUT; if (SnmpOidCopy (&snmpTrapAddrName, &VbAddress->name) == SNMPAPI_FAILURE) goto ERROROUT; VbAddress->value.syntax = SNMP_SYNTAX_IPADDR; // *Re-use* this OID parsed in WSNMP_BN code VbAddress->value.value.string.len = pdu->v1Trap->agent_addr.len; VbAddress->value.value.string.ptr = pdu->v1Trap->agent_addr.ptr; pdu->v1Trap->agent_addr.len = 0; // Setting .ptr to NULL required pdu->v1Trap->agent_addr.ptr = NULL; // for later call to FreeV1Trap() DO_ENTERPRISE: // Then,......one new binding is appended onto the variable-bindings field: // snmpTrapEnterpriseOID.0 [13], which takes its value from the enterprise field // of the Trap-PDU. // // WINSNMP specs in SnmpRecvMsg specifies this append for both the generic and // specific traps and not only for specific as RFC 1452 does. if (!(VbEnterprise = (LPVARBIND)GlobalAlloc (GPTR, sizeof(VARBIND)))) goto ERROROUT; if (SnmpOidCopy (&snmpTrapEntName, &VbEnterprise->name) == SNMPAPI_FAILURE) goto ERROROUT; VbEnterprise->value.syntax = SNMP_SYNTAX_OID; // *Re-use* this OID parsed in WSNMP_BN code VbEnterprise->value.value.oid.len = pdu->v1Trap->enterprise.len; VbEnterprise->value.value.oid.ptr = pdu->v1Trap->enterprise.ptr; pdu->v1Trap->enterprise.len = 0; // Setting .ptr to NULL required pdu->v1Trap->enterprise.ptr = NULL; // for later call to FreeV1Trap() //We have all the variables set, just need to link them together //backup the head of the original vbs endPtr = pdu->VBL_addr; // setup the new head of the list pdu->VBL_addr = VbTicks; VbTicks->next_var = VbTrap; VbTrap->next_var = endPtr; // position endPtr on the last varbind from the list if (endPtr != NULL) { // set it on the last varbind from the original V1 trap while (endPtr->next_var != NULL) endPtr = endPtr->next_var; } else { // set it on VbTrap if no varbinds were passed in the V1 trap endPtr = VbTrap; } // append VbAddress if any and set endPtr on the new ending if (VbAddress != NULL) { endPtr->next_var = VbAddress; endPtr = VbAddress; } // append VbEnterprise endPtr->next_var = VbEnterprise; VbEnterprise->next_var = NULL; // // make it say it's an SNMPv2 Trap PDU pdu->type = SNMP_PDU_TRAP; // Assign a RequestID (not in SNMPv1 Trap PDUs (no need to lock) pdu->appReqId = ++(TaskData.nLastReqId); return (TRUE); // ERROROUT: // Free only those resources created in this function // FreeVarBind is a noop on NULLs, so no need to check first FreeVarBind (VbEnterprise); FreeVarBind (VbAddress); FreeVarBind (VbTrap); FreeVarBind (VbTicks); return (FALSE); } // end_MapV1TrapV2 BOOL DispatchTrap (LPSAS host, smiLPOCTETS community, LPPDUS pdu) { #define MAXSLOTS 10 // maximum active trap receivers DWORD nCmp; DWORD nFound = 0; DWORD nTrap = 0; DWORD nTraps[MAXSLOTS]; DWORD nMsg = 0; LPSNMPMSG pMsg; LPTRAPNOTICE pTrap; EnterCriticalSection (&cs_TRAP); for (nTrap = 0; nTrap < TrapDescr.Allocated; nTrap++) { pTrap = snmpGetTableEntry(&TrapDescr, nTrap); if (!pTrap->Session) continue; // Active trap registration and if (pTrap->notification.len) // all Traps? { // Nope, specific test SNMPAPI_STATUS lError; // Next line is critical...do not remove...BN 3/8/96 pTrap->notification.ptr = &(pTrap->notificationValue[0]); // 2nd param below assumes well-formed trap/inform...BN 1/21/97 lError = SnmpOidCompare (&(pTrap->notification), &pdu->VBL_addr->next_var->value.value.oid, pTrap->notification.len, &nCmp); if (lError != SNMPAPI_SUCCESS || nCmp) continue; // not equal... } if (pTrap->agentEntity) // Specific agent? { int nResult; LPENTITY pEntity = snmpGetTableEntry(&EntsDescr, HandleToUlong(pTrap->agentEntity) - 1); if (host->ipx.sa_family == AF_IPX) nResult = memcmp (&host->ipx.sa_netnum, &(pEntity->addr.ipx.sa_netnum), AF_IPX_ADDR_SIZE); else // AF_IPX nResult = memcmp (&host->inet.sin_addr, &(pEntity->addr.inet.sin_addr), AF_INET_ADDR_SIZE); if (nResult) continue; // not equal... } if (pTrap->Context) // Specific context? { LPCTXT pCtxt = snmpGetTableEntry(&CntxDescr, HandleToUlong(pTrap->Context) - 1); if (community->len != pCtxt->commLen) continue; // not equal...lengths if (memcmp (community->ptr, pCtxt->commStr, (size_t)community->len)) continue; // not equal...values } nTraps[nFound] = nTrap; // Got a match! nFound++; // Count the number found and check it against maximums if ((nFound == (MAXSLOTS)) || (nFound == TrapDescr.Used)) break; } // end_for LeaveCriticalSection (&cs_TRAP); if (nFound == 0) // Nothing to do return (SNMPAPI_FAILURE); // nCmp = nFound; // Save count for later user EnterCriticalSection (&cs_MSG); while (nFound) { DWORD lError; lError = snmpAllocTableEntry(&MsgDescr, &nMsg); if (lError != SNMPAPI_SUCCESS) { LeaveCriticalSection(&cs_MSG); return lError; } pMsg = snmpGetTableEntry(&MsgDescr, nMsg); --nFound; nTrap = nTraps[nFound]; nTraps[nFound] = nMsg; // Need for later use pTrap = snmpGetTableEntry(&TrapDescr, nTrap); pMsg->Session = pTrap->Session; pMsg->Status = NP_RCVD; pMsg->Type = pdu->type; // 960522 - BN... // Need to increment the eventual "dstEntity" if // one was specified on the SnmpRegister() filter (unusual). // Deliberate assignment... if (pMsg->ourEntity = pTrap->ourEntity) { LPENTITY pEntity = snmpGetTableEntry(&EntsDescr, HandleToUlong(pTrap->ourEntity)-1); pEntity->refCount++; } // end_960522 - BN pMsg->dllReqId = pMsg->appReqId = pdu->appReqId; pMsg->Ticks = pMsg->Tries = pMsg->PolicyTries = 0; CopyMemory (&(pMsg->Host), host, sizeof(SAS)); if (!(BuildMessage (1, community, pdu, pdu->appReqId, &(pMsg->Addr), &(pMsg->Size)))) { // rollback for all messages from nCmp to nFound while (nCmp != nFound) { nMsg = nTraps[--nCmp]; FreeMsg (nMsg); } LeaveCriticalSection (&cs_MSG); return (SNMPAPI_PDU_INVALID); } } // end_while (nFound) LeaveCriticalSection (&cs_MSG); // // The next while loop actually sends the one or more trap messages // to the application(s)... // This is due to the fact that we "clone" the incoming trap msg // if there are multiple registrations for it. BobN 2/20/95 while (nCmp) // Saved message count { // Now actually send the message(s) LPSESSION pSession; nMsg = nTraps[--nCmp]; pMsg = snmpGetTableEntry(&MsgDescr, nMsg); pSession = snmpGetTableEntry(&SessDescr, HandleToUlong(pMsg->Session) - 1); if (pSession->fCallBack) { // callback session notification mode EnterCriticalSection (&cs_SESSION); if (pSession->thrHandle) { if (pSession->thrCount != 0xFFFFFFFF) pSession->thrCount++; SetEvent (pSession->thrEvent); } else FreeMsg (nMsg); LeaveCriticalSection (&cs_SESSION); } else { // window/message session notification mode if (IsWindow(pSession->hWnd)) { pMsg->Status = NP_READY; PostMessage (pSession->hWnd, pSession->wMsg, 0, 0L); } else FreeMsg (nMsg); } } // end_while (nCmp) return (SNMPAPI_SUCCESS); } // end_DispatchTrap LPPDUS MapV2TrapV1 (HSNMP_PDU hPdu) { // Convert SNMPv2 trap to SNMPv1 trap, for sending only HSNMP_VBL hNewVbl = NULL; LPPDUS oldPdu = NULL; LPPDUS newPdu = NULL; smiUINT32 lCount; smiUINT32 lCmp; smiUINT32 i; smiLPBYTE tmpPtr = NULL; smiOID sName; smiVALUE sValue; // if (hPdu == NULL) return (NULL); oldPdu = snmpGetTableEntry(&PDUsDescr, HandleToUlong(hPdu)-1); if (oldPdu->type != SNMP_PDU_TRAP) return (NULL); if (SnmpGetPduData (hPdu, NULL, NULL, NULL, NULL, &hNewVbl) == SNMPAPI_FAILURE) return (NULL); if (hNewVbl == NULL) return (NULL); newPdu = GlobalAlloc (GPTR, sizeof(PDUS)); if (newPdu == NULL) goto ERR_OUT; // From RFC 2089 // 3.3 Processing an outgoing SNMPv2 TRAP // // If SNMPv2 compliant instrumentation presents an SNMPv2 trap to the // SNMP engine and such a trap passes all regular checking and then is // to be sent to an SNMPv1 destination, then the following steps must be // followed to convert such a trap to an SNMPv1 trap. This is basically // the reverse of the SNMPv1 to SNMPv2 mapping as described in RFC1908 // [3]. newPdu->type = SNMP_PDU_V1TRAP; newPdu->v1Trap = GlobalAlloc (GPTR, sizeof(V1TRAP)); if (newPdu->v1Trap == NULL) goto ERR_OUT; // // 1. If any of the varBinds in the varBindList has an SNMPv2 syntax // of Counter64, then such varBinds are implicitly considered to // be not in view, and so they are removed from the varBindList to // be sent with the SNMPv1 trap. // // We will do that step later, but check the VB count for now: lCount = SnmpCountVbl (hNewVbl); // SNMPAPI_FAILURE is 0 // Need at least 2 for sysUptime and snmpTrapOID! if (lCount < 2) goto ERR_OUT; // // 2. The 3 special varBinds in the varBindList of an SNMPv2 trap // (sysUpTime.0 (TimeTicks), snmpTrapOID.0 (OBJECT IDENTIFIER) and // optionally snmpTrapEnterprise.0 (OBJECT IDENTIFIER)) are // removed from the varBindList to be sent with the SNMPv1 trap. // These 2 (or 3) varBinds are used to decide how to set other // fields in the SNMPv1 trap PDU as follows: // // a. The value of sysUpTime.0 is copied into the timestamp field // of the SNMPv1 trap. // if (SnmpGetVb (hNewVbl, 1, &sName, &sValue) == SNMPAPI_FAILURE) goto ERR_OUT; if (SnmpOidCompare (&sysUpTimeName, &sName, 0, &lCmp) == SNMPAPI_FAILURE) { // sValue is a number, no need to free SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sName); goto ERR_OUT; } SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sName); // ignore error if (lCmp != 0) goto ERR_OUT; newPdu->v1Trap->time_ticks = sValue.value.uNumber; if (SnmpDeleteVb (hNewVbl, 1) == SNMPAPI_FAILURE) goto ERR_OUT; lCount--; // // b. If the snmpTrapOID.0 value is one of the standard traps // the specific-trap field is set to zero and the generic // trap field is set according to this mapping: // // value of snmpTrapOID.0 generic-trap // =============================== ============ // 1.3.6.1.6.3.1.1.5.1 (coldStart) 0 // 1.3.6.1.6.3.1.1.5.2 (warmStart) 1 // 1.3.6.1.6.3.1.1.5.3 (linkDown) 2 // 1.3.6.1.6.3.1.1.5.4 (linkUp) 3 // 1.3.6.1.6.3.1.1.5.5 (authenticationFailure) 4 // 1.3.6.1.6.3.1.1.5.6 (egpNeighborLoss) 5 // if (SnmpGetVb (hNewVbl, 1, &sName, &sValue) == SNMPAPI_FAILURE) goto ERR_OUT; if (SnmpOidCompare (&snmpTrapOidName, &sName, 0, &lCmp) == SNMPAPI_FAILURE) { SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sName); SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sValue.value.oid); goto ERR_OUT; } SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sName); // ignore error if (lCmp != 0) { SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sValue.value.oid); goto ERR_OUT; } if (SnmpOidCompare (&snmpTrapsName, &sValue.value.oid, 9, &lCmp) == SNMPAPI_FAILURE) { SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sValue.value.oid); goto ERR_OUT; } if (!lCmp) { newPdu->v1Trap->generic_trap = sValue.value.oid.ptr[9] - 1; newPdu->v1Trap->specific_trap = 0; // The enterprise field is set to the value of // snmpTrapEnterprise.0 if this varBind is present, otherwise // it is set to the value snmpTraps as defined in RFC1907 [4]. i = snmpTrapsName.len - 1; tmpPtr = (smiLPBYTE)snmpTrapsValue; } // // c. If the snmpTrapOID.0 value is not one of the standard // traps, then the generic-trap field is set to 6 and the // specific-trap field is set to the last subid of the // snmpTrapOID.0 value. else { newPdu->v1Trap->generic_trap = 6; i = sValue.value.oid.len; newPdu->v1Trap->specific_trap = sValue.value.oid.ptr[i-1]; tmpPtr = (smiLPBYTE)sValue.value.oid.ptr; // // o If the next to last subid of snmpTrapOID.0 is zero, // then the enterprise field is set to snmpTrapOID.0 value // and the last 2 subids are truncated from that value. if (sValue.value.oid.ptr[i-2] == 0) i -= 2; // o If the next to last subid of snmpTrapOID.0 is not zero, // then the enterprise field is set to snmpTrapOID.0 value // and the last 1 subid is truncated from that value. else i -= 1; // In any event, the snmpTrapEnterprise.0 varBind (if present) // is ignored in this case. } // newPdu->v1Trap->enterprise.len = i; i *= sizeof(smiUINT32); // This allocation might have to be freed later, // if generic trap and SnmpTrapEnterprise.0 is present in the varbindlist. newPdu->v1Trap->enterprise.ptr = GlobalAlloc (GPTR, i); if (newPdu->v1Trap->enterprise.ptr == NULL) { SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sValue.value.oid); goto ERR_OUT; } CopyMemory (newPdu->v1Trap->enterprise.ptr, tmpPtr, i); SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sValue.value.oid); // ignore error if (SnmpDeleteVb (hNewVbl, 1) == SNMPAPI_FAILURE) goto ERR_OUT; lCount--; // i = 1; while (i <= lCount) { if (SnmpGetVb (hNewVbl, i, &sName, &sValue) == SNMPAPI_FAILURE) goto ERR_OUT; if (sValue.syntax == SNMP_SYNTAX_CNTR64) { if (SnmpDeleteVb (hNewVbl, i) == SNMPAPI_FAILURE) goto ERR_LOOP; lCount--; goto LOOP; } if (SnmpOidCompare (&snmpTrapEntName, &sName, 0, &lCmp) == SNMPAPI_FAILURE) goto ERR_LOOP; if (lCmp == 0) { if (newPdu->v1Trap->specific_trap == 0) { if (newPdu->v1Trap->enterprise.ptr) GlobalFree (newPdu->v1Trap->enterprise.ptr); lCmp = sValue.value.oid.len * sizeof(smiUINT32); newPdu->v1Trap->enterprise.ptr = GlobalAlloc (GPTR, lCmp); if (newPdu->v1Trap->enterprise.ptr == NULL) goto ERR_LOOP; newPdu->v1Trap->enterprise.len = sValue.value.oid.len; CopyMemory (newPdu->v1Trap->enterprise.ptr, sValue.value.oid.ptr, lCmp); } if (SnmpDeleteVb (hNewVbl, i) == SNMPAPI_FAILURE) goto ERR_LOOP; lCount--; goto LOOP; } i++; LOOP: SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sName); SnmpFreeDescriptor (sValue.syntax, (smiLPOPAQUE)&sValue.value.oid); } goto DONE_LOOP; // jump over error handling code ERR_LOOP: SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&sName); SnmpFreeDescriptor (sValue.syntax, (smiLPOPAQUE)&sValue.value.oid); goto ERR_OUT; DONE_LOOP: if (lCount > 0) { LPVBLS pVbl = snmpGetTableEntry(&VBLsDescr, HandleToUlong(hNewVbl)-1); // Retain existing varbindlist remainder newPdu->VBL_addr = pVbl->vbList; // Flag it as gone for subsequent call to SnmpFreeVbl pVbl->vbList = NULL; } SnmpFreeVbl (hNewVbl); // // 3. The agent-addr field is set with the appropriate address of the // the sending SNMP entity, which is the IP address of the sending // entity of the trap goes out over UDP; otherwise the agent-addr // field is set to address 0.0.0.0. newPdu->v1Trap->agent_addr.len = sizeof(DWORD); newPdu->v1Trap->agent_addr.ptr = GlobalAlloc (GPTR, sizeof(DWORD)); if (newPdu->v1Trap->agent_addr.ptr == NULL) goto ERR_OUT; if (TaskData.localAddress == 0) { // Get the local machine address (for outgoing v1 traps) char szLclHost [MAX_HOSTNAME]; LPHOSTENT lpstHostent; if (gethostname (szLclHost, MAX_HOSTNAME) != SOCKET_ERROR) { lpstHostent = gethostbyname ((LPSTR)szLclHost); if (lpstHostent) TaskData.localAddress = *((LPDWORD)(lpstHostent->h_addr)); } } *(LPDWORD)newPdu->v1Trap->agent_addr.ptr = TaskData.localAddress; // end_RFC2089 return (newPdu); // ERR_OUT: SnmpFreeVbl (hNewVbl); if (newPdu) { FreeVarBindList (newPdu->VBL_addr); // Checks for NULL FreeV1Trap (newPdu->v1Trap); // Checks for NULL GlobalFree (newPdu); } return (NULL); } // end_MapV2TrapV1