/*++ Copyright (c) 1997 Microsoft Corporation Module Name: mgmtapi.c Abstract: SNMP Management API (wrapped around WinSNMP API). Environment: User Mode - Win32 Revision History: 05-Feb-1997 DonRyan Rewrote functions to be wrappers around WinSNMP. --*/ /////////////////////////////////////////////////////////////////////////////// // // // Include Files // // // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// // // // Private Definitions // // // /////////////////////////////////////////////////////////////////////////////// typedef struct _SNMP_MGR_SESSION { SOCKET UnusedSocket; // WARNING: Previous versions of the struct sockaddr UnusedDestAddr; // MGMTAPI.H header file exposed the LPSTR UnusedCommunity; // SNMP_MGR_SESSION structure which INT UnusedTimeout; // unfortunately encouraged people to INT UnusedNumRetries; // muck with it. Since this structure AsnInteger UnusedRequestId; // has now changed we must protect it. CRITICAL_SECTION SessionLock; // multiple threads may share session HSNMP_SESSION hSnmpSession; // handle to winsnmp session HSNMP_ENTITY hAgentEntity; // handle to agent entity HSNMP_ENTITY hManagerEntity; // handle to manager entity HSNMP_CONTEXT hViewContext; // handle to view context HSNMP_PDU hPdu; // handle to snmp pdu HSNMP_VBL hVbl; // handle to snmp pdu HWND hWnd; // handle to window smiINT32 nPduType; // current pdu type smiINT32 nRequestId; // current request id smiINT32 nErrorIndex; // error index from pdu smiINT32 nErrorStatus; // error status from pdu smiINT32 nLastError; // last system error SnmpVarBindList * pVarBindList; // pointer to varbind list } SNMP_MGR_SESSION, *PSNMP_MGR_SESSION; typedef struct _TRAP_LIST_ENTRY { LIST_ENTRY Link; // linked-list link AsnObjectIdentifier EnterpriseOID; // generating enterprise AsnNetworkAddress AgentAddress; // generating agent addr AsnNetworkAddress SourceAddress; // generating network addr AsnInteger nGenericTrap; // generic trap type AsnInteger nSpecificTrap; // enterprise specific type AsnOctetString Community; // generating community AsnTimeticks TimeStamp; // time stamp SnmpVarBindList VarBindList; // variable bindings } TRAP_LIST_ENTRY, * PTRAP_LIST_ENTRY; #define IPADDRLEN 4 #define IPXADDRLEN 10 #define MAXENTITYSTRLEN 128 #define MINVARBINDLEN 2 #define SYSUPTIMEINDEX 0 #define SNMPTRAPOIDINDEX 1 #define DEFAULT_ADDRESS_IP "127.0.0.1" #define DEFAULT_ADDRESS_IPX "00000000.000000000000" #define NOTIFICATION_CLASS "MGMTAPI Notification Class" #define WM_WSNMP_INCOMING (WM_USER + 1) #define WM_WSNMP_DONE (WM_USER + 2) #define WSNMP_FAILED(s) ((s) == SNMPAPI_FAILURE) #define WSNMP_SUCCEEDED(s) ((s) != SNMPAPI_FAILURE) #define WSNMP_ASSERT(s) ASSERT((s)) /////////////////////////////////////////////////////////////////////////////// // // // Global Variables // // // /////////////////////////////////////////////////////////////////////////////// HINSTANCE g_hDll; // module handle HANDLE g_hTrapEvent = NULL; // trap event handle HANDLE g_hTrapThread = NULL; // trap thread handle HANDLE g_hTrapRegisterdEvent = NULL; // event to sync. SnmpMgrTrapListen BOOL g_fIsSnmpStarted = FALSE; // indicates winsnmp inited BOOL g_fIsSnmpListening = FALSE; // indicates trap thread on BOOL g_fIsTrapRegistered = FALSE; // indicates trap registered DWORD g_dwRequestId = 1; // unique pdu request id LIST_ENTRY g_IncomingTraps; // incoming trap queue CRITICAL_SECTION g_GlobalLock; // process resource lock SNMP_MGR_SESSION g_TrapSMS; // process trap session /////////////////////////////////////////////////////////////////////////////// // // // Private Procedures // // // /////////////////////////////////////////////////////////////////////////////// DWORD GetRequestId( ) /*++ Routine Description: Retrieve next global request id. Arguments: None. Return Values: Returns request id. --*/ { DWORD dwRequestId; // obtain exclusive access to request id EnterCriticalSection(&g_GlobalLock); // obtain copy of request id dwRequestId = g_dwRequestId++; // obtain exclusive access to request id LeaveCriticalSection(&g_GlobalLock); return dwRequestId; } BOOL TransferVb( PSNMP_MGR_SESSION pSMS, SnmpVarBind * pVarBind ) /*++ Routine Description: Transfer VarBind structure to WinSNMP structure. Arguments: pSMS - pointer to mgmtapi session structure. pVarBind - pointer to varbind to transfer. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; SNMPAPI_STATUS status; smiVALUE tmpValue; smiOID tmpOID; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // validate pointers if ((pVarBind != NULL) && (pVarBind->name.ids != NULL) && (pVarBind->name.idLength != 0)) { // re-init fOk = TRUE; // transfer oid information tmpOID.len = pVarBind->name.idLength; tmpOID.ptr = pVarBind->name.ids; // only initialize value if set if (pSMS->nPduType == SNMP_PDU_SET) { // syntax values are equivalent tmpValue.syntax = (smiINT32)(BYTE)pVarBind->value.asnType; // determine type switch (pVarBind->value.asnType) { case ASN_INTEGER32: // transfer signed int tmpValue.value.sNumber = pVarBind->value.asnValue.number; break; case ASN_UNSIGNED32: case ASN_COUNTER32: case ASN_GAUGE32: case ASN_TIMETICKS: // transfer unsigned int tmpValue.value.uNumber = pVarBind->value.asnValue.unsigned32; break; case ASN_COUNTER64: // transfer 64-bit counter tmpValue.value.hNumber.lopart = pVarBind->value.asnValue.counter64.LowPart; tmpValue.value.hNumber.hipart = pVarBind->value.asnValue.counter64.HighPart; break; case ASN_OPAQUE: case ASN_IPADDRESS: case ASN_OCTETSTRING: case ASN_BITS: // transfer octet string tmpValue.value.string.len = pVarBind->value.asnValue.string.length; tmpValue.value.string.ptr = pVarBind->value.asnValue.string.stream; break; case ASN_OBJECTIDENTIFIER: // transfer object id tmpValue.value.oid.len = pVarBind->value.asnValue.object.idLength; tmpValue.value.oid.ptr = pVarBind->value.asnValue.object.ids; break; case ASN_NULL: case SNMP_EXCEPTION_NOSUCHOBJECT: case SNMP_EXCEPTION_NOSUCHINSTANCE: case SNMP_EXCEPTION_ENDOFMIBVIEW: // initialize empty byte tmpValue.value.empty = 0; break; default: // failure fOk = FALSE; break; } } if (fOk) { // register varbind status = SnmpSetVb( pSMS->hVbl, 0, // index &tmpOID, (pSMS->nPduType == SNMP_PDU_SET) ? &tmpValue : NULL ); // validate return code if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpSetVb returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // failure fOk = FALSE; } } } return fOk; } BOOL AllocateVbl( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Transfer VarBindList structure to WinSNMP structure. Arguments: pSMS - pointer to mgmtapi session structure. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; SNMPAPI_STATUS status; SnmpVarBind * pVarBind; DWORD cVarBind; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // validate parameters WSNMP_ASSERT(pSMS->pVarBindList != NULL); WSNMP_ASSERT(pSMS->pVarBindList->len != 0); WSNMP_ASSERT(pSMS->pVarBindList->list != NULL); // allocate resources for variable bindings list pSMS->hVbl = SnmpCreateVbl(pSMS->hSnmpSession, NULL, NULL); // validate varbind handle if (WSNMP_SUCCEEDED(pSMS->hVbl)) { // re-init fOk = TRUE; // initialize varbind pointer pVarBind = pSMS->pVarBindList->list; // initialize varbind count cVarBind = pSMS->pVarBindList->len; // process each varbind while (fOk && cVarBind--) { // transfer variable binding fOk = TransferVb(pSMS, pVarBind++); } if (!fOk) { // release varbind list handle status = SnmpFreeVbl(pSMS->hVbl); // validate return code if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpFreeVbl returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); } // re-initialize pSMS->hVbl = (HSNMP_VBL)NULL; } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpCreateVbl returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); } return fOk; } BOOL FreeVbl( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Cleanup VarBind resources from WinSNMP structure. Arguments: pSMS - pointer to mgmtapi session structure. Return Values: Returns true if successful. --*/ { BOOL fOk = TRUE; SNMPAPI_STATUS status; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // validate handle if (pSMS->hVbl != (HSNMP_VBL)NULL) { // actually release vbl handle status = SnmpFreeVbl(pSMS->hVbl); // validate return code if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpFreeVbl returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // failure fOk = FALSE; } // re-initialize handle pSMS->hVbl = (HSNMP_VBL)NULL; } return fOk; } BOOL AllocatePdu( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Initialize session structure for sending request. Arguments: pSMS - pointer to mgmtapi session structure. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // transfer varbinds if (AllocateVbl(pSMS)) { // grab next shared request id pSMS->nRequestId = GetRequestId(); // create request pdu pSMS->hPdu = SnmpCreatePdu( pSMS->hSnmpSession, pSMS->nPduType, pSMS->nRequestId, 0, // errorStatus 0, // errorIndex pSMS->hVbl ); // validate return status if (WSNMP_SUCCEEDED(pSMS->hPdu)) { // success fOk = TRUE; } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpCreatePdu returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // free resources FreeVbl(pSMS); } } return fOk; } BOOL FreePdu( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Cleanup session structure after processing response. Arguments: pSMS - pointer to mgmtapi session structure. Return Values: Returns true if successful. --*/ { BOOL fOk = TRUE; SNMPAPI_STATUS status; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // validate handle if (pSMS->hPdu != (HSNMP_PDU)NULL) { // free vbl FreeVbl(pSMS); // actually release pdu handle status = SnmpFreePdu(pSMS->hPdu); // validate return code if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpFreePdu returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // failure fOk = FALSE; } // re-initialize handle pSMS->hPdu = (HSNMP_PDU)NULL; } return fOk; } BOOL CopyOid( AsnObjectIdentifier * pDstOID, smiLPOID pSrcOID ) /*++ Routine Description: Copies object identifier from WinSNMP format to MGMTAPI format. Arguments: pDstOID - points to MGMTAPI structure to receive OID. pSrcOID - points to WinSNMP structure to copy. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; // validate pointers WSNMP_ASSERT(pDstOID != NULL); WSNMP_ASSERT(pSrcOID != NULL); WSNMP_ASSERT(pSrcOID->len != 0); WSNMP_ASSERT(pSrcOID->ptr != NULL); // store the number of subids pDstOID->idLength = pSrcOID->len; // allocate memory for subidentifiers pDstOID->ids = SnmpUtilMemAlloc(pDstOID->idLength * sizeof(DWORD)); // validate pointer if (pDstOID->ids != NULL) { // transfer memory memcpy(pDstOID->ids, pSrcOID->ptr, pDstOID->idLength * sizeof(DWORD) ); // success fOk = TRUE; } // now release memory for original oid SnmpFreeDescriptor(SNMP_SYNTAX_OID, (smiLPOPAQUE)pSrcOID); return fOk; } BOOL CopyOctets( AsnOctetString * pDstOctets, smiLPOCTETS pSrcOctets ) /*++ Routine Description: Copies octet string from WinSNMP format to MGMTAPI format. Arguments: pDstOctets - points to MGMTAPI structure to receive octets. pSrcOctets - points to WinSNMP structure to copy. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; SNMPAPI_STATUS status; // validate pointers WSNMP_ASSERT(pDstOctets != NULL); WSNMP_ASSERT(pSrcOctets != NULL); // it is legitimate that // 1. pSrcOctets->len == 0 // 2. pSrcOctets->ptr == NULL if (pSrcOctets->len == 0 || pSrcOctets->ptr == NULL) { pDstOctets->dynamic = FALSE; pDstOctets->length = 0; pDstOctets->stream = NULL; fOk = TRUE; } else { // allocate memory for octet string pDstOctets->stream = SnmpUtilMemAlloc(pSrcOctets->len); // validate pointer if (pDstOctets->stream != NULL) { // octet string allocated pDstOctets->dynamic = TRUE; // store the number of bytes pDstOctets->length = pSrcOctets->len; // transfer memory memcpy(pDstOctets->stream, pSrcOctets->ptr, pDstOctets->length ); // success fOk = TRUE; } } // now release memory for original string SnmpFreeDescriptor(SNMP_SYNTAX_OCTETS, (smiLPOPAQUE)pSrcOctets); return fOk; } CopyVb( PSNMP_MGR_SESSION pSMS, DWORD iVarBind, SnmpVarBind * pVarBind ) /*++ Routine Description: Copy variable binding from WinSNMP structure to MGMTAPI structure. Arguments: pSMS - pointer to mgmtapi session structure. iVarBind - index of varbind structure to copy. pVarBind - pointer to varbind structure. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; SNMPAPI_STATUS status; smiOID tmpOID; smiVALUE tmpValue; // validate session ptr WSNMP_ASSERT(pSMS != NULL); WSNMP_ASSERT(pVarBind != NULL); // attempt to retrieve varbind data from winsnmp structure status = SnmpGetVb(pSMS->hVbl, iVarBind, &tmpOID, &tmpValue); // validate return code if (WSNMP_SUCCEEDED(status)) { // transfer object identifier value fOk = CopyOid(&pVarBind->name, &tmpOID); // syntax values are equivalent pVarBind->value.asnType = (BYTE)(smiINT32)tmpValue.syntax; // determine syntax switch (tmpValue.syntax) { case SNMP_SYNTAX_INT32: // transfer signed int pVarBind->value.asnValue.number = tmpValue.value.sNumber; break; case SNMP_SYNTAX_UINT32: case SNMP_SYNTAX_CNTR32: case SNMP_SYNTAX_GAUGE32: case SNMP_SYNTAX_TIMETICKS: // transfer unsigned int pVarBind->value.asnValue.unsigned32 = tmpValue.value.uNumber; break; case SNMP_SYNTAX_CNTR64: // transfer 64-bit counter pVarBind->value.asnValue.counter64.LowPart = tmpValue.value.hNumber.lopart; pVarBind->value.asnValue.counter64.HighPart = tmpValue.value.hNumber.hipart; break; case SNMP_SYNTAX_OPAQUE: case SNMP_SYNTAX_IPADDR: case SNMP_SYNTAX_OCTETS: case SNMP_SYNTAX_BITS: // transfer octet string if (!CopyOctets(&pVarBind->value.asnValue.string, &tmpValue.value.string)) { // re-initialize pVarBind->value.asnType = ASN_NULL; // failure fOk = FALSE; } break; case SNMP_SYNTAX_OID: // transfer object identifier if (!CopyOid(&pVarBind->value.asnValue.object, &tmpValue.value.oid)) { // re-initialize pVarBind->value.asnType = ASN_NULL; // failure fOk = FALSE; } break; case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: break; // do nothing... default: SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpGetVb returned invalid type.\n" )); // re-initialize pVarBind->value.asnType = ASN_NULL; // failure fOk = FALSE; break; } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpGetVb returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); } return fOk; } BOOL CopyVbl( PSNMP_MGR_SESSION pSMS, SnmpVarBindList * pVarBindList ) /*++ Routine Description: Copy variable bindings from WinSNMP structure to MGMTAPI structure. Arguments: pSMS - pointer to mgmtapi session structure. pVarBindList - pointer to varbind list structure. Return Values: Returns true if successful. --*/ { BOOL fOk = TRUE; // validate session ptr WSNMP_ASSERT(pSMS != NULL); WSNMP_ASSERT(pVarBindList != NULL); // initialize pVarBindList->len = 0; pVarBindList->list = NULL; // validate varbind list handle if (pSMS->hVbl != (HSNMP_VBL)NULL) { // determine number of varbinds pVarBindList->len = SnmpCountVbl(pSMS->hVbl); // validate number of varbinds if (WSNMP_SUCCEEDED(pVarBindList->len)) { // allocate memory for varbinds pVarBindList->list = SnmpUtilMemAlloc( pVarBindList->len * sizeof(SnmpVarBind) ); // validate pointer if (pVarBindList->list != NULL) { DWORD cVarBind = 1; SnmpVarBind * pVarBind; // save pointer to varbinds pVarBind = pVarBindList->list; // process varbinds in the list while (fOk && (cVarBind <= pVarBindList->len)) { // copy varbind from winsnmp to mgmtapi fOk = CopyVb(pSMS, cVarBind++, pVarBind++); } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Could not allocate VBL.\n" )); // re-initialize pVarBindList->len = 0; // failure fOk = FALSE; } } else if (pVarBindList->len != SNMPAPI_NOOP) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpCountVbl returned %s.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // re-initialize pVarBindList->len = 0; // failure fOk = FALSE; } } if (!fOk) { // cleanup any varbinds allocated SnmpUtilVarBindListFree(pVarBindList); } return fOk; } BOOL ParseVbl( PSNMP_MGR_SESSION pSMS, PTRAP_LIST_ENTRY pTLE ) /*++ Routine Description: Parse varbind list for trap-related varbinds. Arguments: pSMS - pointer to MGMTAPI session structure. pTLE - pointer to trap list entry. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; SnmpVarBind * pVarBind; AsnObjectIdentifier * pOID; AsnNetworkAddress * pAgentAddress = NULL; AsnObjectIdentifier * pEnterpriseOID = NULL; // object identifiers to convert snmpv2 trap format static UINT _sysUpTime[] = { 1, 3, 6, 1, 2, 1, 1, 3 }; static UINT _snmpTrapOID[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1 }; static UINT _snmpAddress[] = { 1, 3, 6, 1, 3, 1057, 1 }; static UINT _snmpTrapEnterprise[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 3 }; static UINT _snmpTraps[] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 }; static AsnObjectIdentifier sysUpTime = DEFINE_OID(_sysUpTime); static AsnObjectIdentifier snmpTrapOID = DEFINE_OID(_snmpTrapOID); static AsnObjectIdentifier snmpAddress = DEFINE_OID(_snmpAddress); static AsnObjectIdentifier snmpTrapEnterprise = DEFINE_OID(_snmpTrapEnterprise); static AsnObjectIdentifier snmpTraps = DEFINE_OID(_snmpTraps); // validate pointers WSNMP_ASSERT(pSMS != NULL); WSNMP_ASSERT(pTLE != NULL); // validate vbl have minimum entries if (pTLE->VarBindList.len >= MINVARBINDLEN) { // point to sysUpTime varbind structure pVarBind = &pTLE->VarBindList.list[SYSUPTIMEINDEX]; // verify variable is sysUpTime if ((pVarBind->value.asnType == ASN_TIMETICKS) && !SnmpUtilOidNCmp(&pVarBind->name, &sysUpTime, sysUpTime.idLength)) { // transfer sysUpTime value to trap entry pTLE->TimeStamp = pVarBind->value.asnValue.ticks; } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Could not find sysUpTime.\n" )); goto cleanup; // bail... } // see if any additional varbinds present if (pTLE->VarBindList.len > MINVARBINDLEN) { // point to snmpTrapEnterprise varbind structure (maybe) pVarBind = &pTLE->VarBindList.list[pTLE->VarBindList.len - 1]; // verify variable is snmpTrapEnterprise if ((pVarBind->value.asnType == ASN_OBJECTIDENTIFIER) && !SnmpUtilOidNCmp(&pVarBind->name, &snmpTrapEnterprise, snmpTrapEnterprise.idLength)) { // transfer enterprise oid to list entry pTLE->EnterpriseOID = pVarBind->value.asnValue.object; // store enterprise oid for later pEnterpriseOID = &pTLE->EnterpriseOID; // modify type to avoid deallocation pVarBind->value.asnType = ASN_NULL; } else { SNMPDBG(( SNMP_LOG_TRACE, "MGMTAPI: Could not find snmpTrapEnterprise.\n" )); } } // see if the agent address is present if (pTLE->VarBindList.len > MINVARBINDLEN+1) { // point to snmpAddress varbind structure (maybe) pVarBind = &pTLE->VarBindList.list[pTLE->VarBindList.len - 2]; // verify variable is snmpAddress if ((pVarBind->value.asnType == SNMP_SYNTAX_IPADDR) && !SnmpUtilOidNCmp(&pVarBind->name, &snmpAddress, snmpAddress.idLength)) { // transfer agent address oid to list entry pTLE->AgentAddress = pVarBind->value.asnValue.address; // store agent address for later pAgentAddress = &pTLE->AgentAddress; // modify type to avoid deallocation pVarBind->value.asnType = ASN_NULL; } else { SNMPDBG(( SNMP_LOG_TRACE, "MGMTAPI: Could not find snmpAddress.\n" )); } } // point to snmpTrapOID varbind structure pVarBind = &pTLE->VarBindList.list[SNMPTRAPOIDINDEX]; // verify variable is snmpTrapOID if ((pVarBind->value.asnType == ASN_OBJECTIDENTIFIER) && !SnmpUtilOidNCmp(&pVarBind->name, &snmpTrapOID, snmpTrapOID.idLength)) { // retrieve pointer to oid pOID = &pVarBind->value.asnValue.object; // check for generic trap if (!SnmpUtilOidNCmp(pOID, &snmpTraps, snmpTraps.idLength)) { // validate size is one greater than root if (pOID->idLength == (snmpTraps.idLength + 1)) { // retrieve trap id // --ft:10/01/98 (bug #231344): WINSNMP gives up the V2 syntax => pOID->ids[snmpTraps.idLength] = [1..6] // --ft:10/01/98 (bug #231344): as MGMTAPI turns back to V1, we need to decrement this value. pTLE->nGenericTrap = (pOID->ids[snmpTraps.idLength])-1; // re-initialize pTLE->nSpecificTrap = 0; } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Invalid snmpTrapOID.\n" )); goto cleanup; // bail... } // check for specific trap } else if ((pEnterpriseOID != NULL) && !SnmpUtilOidNCmp(pOID, pEnterpriseOID, pEnterpriseOID->idLength)) { // validate size is two greater than root if (pOID->idLength == (pEnterpriseOID->idLength + 2)) { // validate separator sub-identifier WSNMP_ASSERT(pOID->ids[pEnterpriseOID->idLength] == 0); // retrieve trap id pTLE->nSpecificTrap = pOID->ids[pEnterpriseOID->idLength + 1]; // re-initialize pTLE->nGenericTrap = SNMP_GENERICTRAP_ENTERSPECIFIC; } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Invalid snmpTrapOID.\n" )); goto cleanup; // bail... } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Could not identify snmpTrapOID.\n" )); goto cleanup; // bail... } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Could not find snmpTrapOID.\n" )); goto cleanup; // bail... } // check for enterprise oid if (pEnterpriseOID != NULL) { // release snmpTrapEnterprise varbind structure SnmpUtilVarBindFree(&pTLE->VarBindList.list[pTLE->VarBindList.len - 1]); // decrement the list length as the last varbind was freed pTLE->VarBindList.len--; } // check for agent address if (pAgentAddress != NULL) { // release snmpAgentAddress varbind structure SnmpUtilVarBindFree(&pTLE->VarBindList.list[pTLE->VarBindList.len - 1]); // decrement the list length as the last varbind was again freed pTLE->VarBindList.len--; } // release sysUpTime varbind structure SnmpUtilVarBindFree(&pTLE->VarBindList.list[SYSUPTIMEINDEX]); // release snmpTrapOID varbind structure SnmpUtilVarBindFree(&pTLE->VarBindList.list[SNMPTRAPOIDINDEX]); // subtract released varbinds pTLE->VarBindList.len -= MINVARBINDLEN; // check if all varbinds freed if (pTLE->VarBindList.len == 0) { // release memory for list SnmpUtilMemFree(pTLE->VarBindList.list); // re-initialize pTLE->VarBindList.list = NULL; } else { // shift varbind list up two spaces memmove((LPBYTE)(pTLE->VarBindList.list), (LPBYTE)(pTLE->VarBindList.list + MINVARBINDLEN), (pTLE->VarBindList.len * sizeof(SnmpVarBind)) ); } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Too few subidentifiers.\n" )); } // success return TRUE; cleanup: // failure return FALSE; } BOOL FreeTle( PTRAP_LIST_ENTRY pTLE ) /*++ Routine Description: Release memory used for trap entry. Arguments: pTLE - pointer to trap list entry. Return Values: Returns true if successful. --*/ { // validate pointer WSNMP_ASSERT(pTLE != NULL); // release memory for enterprise oid SnmpUtilOidFree(&pTLE->EnterpriseOID); // release memory for community string SnmpUtilMemFree(pTLE->Community.stream); // release memory used in varbind list SnmpUtilVarBindListFree(&pTLE->VarBindList); // release list entry SnmpUtilMemFree(pTLE); return TRUE; } BOOL AllocateTle( PSNMP_MGR_SESSION pSMS, PTRAP_LIST_ENTRY * ppTLE, HSNMP_ENTITY hAgentEntity, HSNMP_CONTEXT hViewContext ) /*++ Routine Description: Allocate memory for trap entry. Arguments: pSMS - pointer to MGMTAPI session structure. ppTLE - pointer to pointer to trap list entry. hAgentEntity - handle to agent sending trap. hViewContext - handle to view context of trap. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; PTRAP_LIST_ENTRY pTLE; SNMPAPI_STATUS status; smiOCTETS CommunityStr; CHAR SourceStrAddr[MAXENTITYSTRLEN+1]; struct sockaddr SourceSockAddr; // validate pointers WSNMP_ASSERT(pSMS != NULL); WSNMP_ASSERT(ppTLE != NULL); // allocate memory from list entry pTLE = SnmpUtilMemAlloc(sizeof(TRAP_LIST_ENTRY)); // validate pointer if (pTLE == NULL) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Could not allocate trap entry.\n" )); return FALSE; // bail... } // initialize *ppTLE = NULL; // copy varbinds to trap list entry if (!CopyVbl(pSMS, &pTLE->VarBindList)) { goto cleanup; // bail... } // parse trap-related varbinds if (!ParseVbl(pSMS, pTLE)) { goto cleanup; // bail... } // check if source address is specified if (hAgentEntity != (HSNMP_ENTITY)NULL) { // convert addr to string status = SnmpEntityToStr( hAgentEntity, sizeof(SourceStrAddr), SourceStrAddr ); // validate error code if (WSNMP_SUCCEEDED(status)) { DWORD AddrLen = 0; LPBYTE AddrPtr = NULL; // convert string to socket address structure SnmpSvcAddrToSocket(SourceStrAddr, &SourceSockAddr); // validate address family if (SourceSockAddr.sa_family == AF_INET) { // assign ip values AddrLen = IPADDRLEN; AddrPtr = (LPBYTE)&(((struct sockaddr_in *) (&SourceSockAddr))->sin_addr); } else if (SourceSockAddr.sa_family == AF_IPX) { // assign ipx values AddrLen = IPXADDRLEN; AddrPtr = (LPBYTE)&(((struct sockaddr_ipx *) (&SourceSockAddr))->sa_netnum); } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Ignoring invalid address.\n" )); goto cleanup; // bail... } // allocate address to return (if specified) pTLE->SourceAddress.stream = SnmpUtilMemAlloc(AddrLen); // validate pointer if (pTLE->SourceAddress.stream != NULL) { // initialize length values pTLE->SourceAddress.length = AddrLen; pTLE->SourceAddress.dynamic = TRUE; // transfer agent address information memcpy(pTLE->SourceAddress.stream, AddrPtr, AddrLen); } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpEntityToStr returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); goto cleanup; // bail... } } // check if community specified if (hViewContext != (HSNMP_CONTEXT)NULL) { // convert agent entity to string status = SnmpContextToStr(hViewContext, &CommunityStr); // validate error code if (WSNMP_SUCCEEDED(status)) { // copy octet string, memory allocated in CommunityStr is also freed CopyOctets(&pTLE->Community, &CommunityStr); } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpContextToStr returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); goto cleanup; // bail... } } // transfer *ppTLE = pTLE; // success return TRUE; cleanup: // release FreeTle(pTLE); // failure return FALSE; } BOOL NotificationCallback( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Callback for processing notification messages. Arguments: pSMS - pointer to mgmtapi session structure. Return Values: Returns true if processing finished. --*/ { BOOL fDone = TRUE; SNMPAPI_STATUS status; HSNMP_ENTITY hAgentEntity = (HSNMP_ENTITY)NULL; HSNMP_ENTITY hManagerEntity = (HSNMP_ENTITY)NULL; HSNMP_CONTEXT hViewContext = (HSNMP_CONTEXT)NULL; smiINT32 nPduType; smiINT32 nRequestId; // validate pointer WSNMP_ASSERT(pSMS != NULL); // retrieve message status = SnmpRecvMsg( pSMS->hSnmpSession, &hAgentEntity, &hManagerEntity, &hViewContext, &pSMS->hPdu ); // validate return code if (WSNMP_SUCCEEDED(status)) { // retrieve pdu data status = SnmpGetPduData( pSMS->hPdu, &nPduType, &nRequestId, &pSMS->nErrorStatus, &pSMS->nErrorIndex, &pSMS->hVbl ); // validate return code if (WSNMP_SUCCEEDED(status)) { // process reponse to request if (nPduType == SNMP_PDU_RESPONSE) { // validate context information if ((pSMS->nRequestId == nRequestId) && (pSMS->hViewContext == hViewContext) && (pSMS->hAgentEntity == hAgentEntity) && (pSMS->hManagerEntity == hManagerEntity)) { // validate returned error status if (pSMS->nErrorStatus == SNMP_ERROR_NOERROR) { SnmpVarBindList VarBindList; // copy variable binding list if (CopyVbl(pSMS, &VarBindList)) { // release existing varbind list SnmpUtilVarBindListFree(pSMS->pVarBindList); // manually copy new varbind list *pSMS->pVarBindList = VarBindList; } else { // modify last error status pSMS->nLastError = SNMPAPI_ALLOC_ERROR; } } } else { SNMPDBG(( SNMP_LOG_TRACE, "MGMTAPI: Ignoring invalid context.\n" )); // continue fDone = FALSE; } } else if (nPduType == SNMP_PDU_TRAP) { PTRAP_LIST_ENTRY pTLE; // allocate trap list entry (transfers varbinds etc.) if (AllocateTle(pSMS, &pTLE, hAgentEntity, hViewContext)) { // obtain exclusive access EnterCriticalSection(&g_GlobalLock); // insert new trap into the incoming queue InsertTailList(&g_IncomingTraps, &pTLE->Link); // alert user SetEvent(g_hTrapEvent); // release exclusive access LeaveCriticalSection(&g_GlobalLock); } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Ignoring invalid pdu type %d.\n", nPduType )); // continue fDone = FALSE; } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpGetPduData returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // retrieve last error status from winsnmp pSMS->nLastError = SnmpGetLastError(pSMS->hSnmpSession); } // release temporary entity SnmpFreeEntity(hAgentEntity); // release temporary entity SnmpFreeEntity(hManagerEntity); // release temporary context SnmpFreeContext(hViewContext); } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpRecvMsg returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // retrieve last error status from winsnmp pSMS->nLastError = SnmpGetLastError(pSMS->hSnmpSession); } // release pdu FreePdu(pSMS); return fDone; } LRESULT CALLBACK NotificationWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) /*++ Routine Description: Callback that processes WinSNMP notifications. Arguments: hWnd - window handle. uMsg - message identifier. wParam - first message parameter. lParam - second message parameter. Return Values: The return value is the result of the message processing and depends on the message sent. --*/ { // check for winsnmp notification and transport timeout if (uMsg == WM_WSNMP_INCOMING && wParam == SNMPAPI_TL_TIMEOUT) { PSNMP_MGR_SESSION pSMS; // retrieve mgmtapi session pointer from window pSMS = (PSNMP_MGR_SESSION)GetWindowLongPtr(hWnd, 0); // validate session ptr WSNMP_ASSERT(pSMS != NULL); // translate winsnmp error to mgmtapi error pSMS->nLastError = SNMP_MGMTAPI_TIMEOUT; // post message to break out of message pump PostMessage(pSMS->hWnd, WM_WSNMP_DONE, (WPARAM)0, (LPARAM)0); return (LRESULT)0; } // check for winsnmp notification else if (uMsg == WM_WSNMP_INCOMING) { PSNMP_MGR_SESSION pSMS; // retrieve mgmtapi session pointer from window pSMS = (PSNMP_MGR_SESSION)GetWindowLongPtr(hWnd, 0); // validate session ptr WSNMP_ASSERT(pSMS != NULL); // process notification message if (NotificationCallback(pSMS)) { // post message to break out of message pump PostMessage(pSMS->hWnd, WM_WSNMP_DONE, (WPARAM)0, (LPARAM)0); } return (LRESULT)0; } else { // forward all other messages to windows return DefWindowProc(hWnd, uMsg, wParam, lParam); } } BOOL RegisterNotificationClass( ) /*++ Routine Description: Register notification class for sessions. Arguments: None. Return Values: Returns true if successful. --*/ { BOOL fOk; WNDCLASS wc; // initialize notification window class wc.lpfnWndProc = (WNDPROC)NotificationWndProc; wc.lpszClassName = NOTIFICATION_CLASS; wc.lpszMenuName = NULL; wc.hInstance = g_hDll; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.cbWndExtra = sizeof(PSNMP_MGR_SESSION); wc.cbClsExtra = 0; wc.style = 0; // register class fOk = RegisterClass(&wc); if (!fOk) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: RegisterClass returned %d.\n", GetLastError() )); } return fOk; } BOOL UnregisterNotificationClass( ) /*++ Routine Description: Unregister notification class. Arguments: None. Return Values: Returns true if successful. --*/ { BOOL fOk; // unergister notification window class fOk = UnregisterClass(NOTIFICATION_CLASS, g_hDll); if (!fOk) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: UnregisterClass returned %d.\n", GetLastError() )); } return fOk; } BOOL StartSnmpIfNecessary( ) /*++ Routine Description: Initialize WinSNMP DLL if necessary. Arguments: None. Return Values: Returns true if successful. --*/ { BOOL fOk = TRUE; // serialize access to startup code EnterCriticalSection(&g_GlobalLock); // see if already started if (g_fIsSnmpStarted != TRUE) { SNMPAPI_STATUS status; // initialize start params smiUINT32 nMajorVersion = 0; smiUINT32 nMinorVersion = 0; smiUINT32 nLevel = 0; smiUINT32 nTranslateMode = 0; smiUINT32 nRetransmitMode = 0; // start winsnmp status = SnmpStartup( &nMajorVersion, &nMinorVersion, &nLevel, &nTranslateMode, &nRetransmitMode ); // validate return code if (WSNMP_SUCCEEDED(status)) { SNMPDBG(( SNMP_LOG_TRACE, "MGMTAPI: SnmpStartup succeeded:\n" "MGMTAPI:\tnMajorVersion = %d\n" "MGMTAPI:\tnMinorVersion = %d\n" "MGMTAPI:\tnLevel = %d\n" "MGMTAPI:\tnTranslateMode = %d\n" "MGMTAPI:\tnRetransmitMode = %d\n", nMajorVersion, nMinorVersion, nLevel, nTranslateMode, nRetransmitMode )); // allocate global trap available event g_hTrapEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // allocate global event to sync. SnmpMgrTrapListen g_hTrapRegisterdEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // make sure translate mode is snmp v1 SnmpSetTranslateMode(SNMPAPI_UNTRANSLATED_V1); // make sure retransmit mode is on SnmpSetRetransmitMode(SNMPAPI_ON); // register notification class RegisterNotificationClass(); // save new status g_fIsSnmpStarted = TRUE; // success fOk = TRUE; } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpStartup returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); // failure fOk = FALSE; } } // serialize access to startup code LeaveCriticalSection(&g_GlobalLock); return fOk; } BOOL CleanupIfNecessary( ) /*++ Routine Description: Cleanup WinSNMP DLL if necessary. Arguments: None. Return Values: Returns true if successful. --*/ { BOOL fOk = TRUE; // serialize access to startup code EnterCriticalSection(&g_GlobalLock); // see if already started if (g_fIsSnmpStarted == TRUE) { SNMPAPI_STATUS status; // shutdown winsnmp status = SnmpCleanup(); // validate return code if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpCleanup returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); // failure fOk = FALSE; } // unregister notification class UnregisterNotificationClass(); // save new status g_fIsSnmpStarted = FALSE; } // check trap handle if (g_hTrapEvent != NULL) { // close trap handle CloseHandle(g_hTrapEvent); // re-initialize g_hTrapEvent = NULL; } // serialize access to startup code LeaveCriticalSection(&g_GlobalLock); return fOk; } BOOL CreateNotificationWindow( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Create notification window for session. Arguments: pSMS - pointer to MGMTAPI session structure. Return Values: Returns true if successful. --*/ { BOOL fOk; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // create notification window pSMS->hWnd = CreateWindow( NOTIFICATION_CLASS, NULL, // pointer to window name 0, // window style 0, // horizontal position of window 0, // vertical position of window 0, // window width 0, // window height NULL, // handle to parent or owner window NULL, // handle to menu or child-window identifier g_hDll, // handle to application instance NULL // pointer to window-creation data ); // validate window handle if (pSMS->hWnd != NULL) { // store pointer to session in window SetWindowLongPtr(pSMS->hWnd, 0, (LONG_PTR)pSMS); // success fOk = TRUE; } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: CreateWindow returned %d.\n", GetLastError() )); // failure fOk = FALSE; } return fOk; } BOOL DestroyNotificationWindow( HWND hWnd ) /*++ Routine Description: Destroy notification window for session. Arguments: hWnd - window handle for session. Return Values: Returns true if successful. --*/ { BOOL fOk; // destroy notification window fOk = DestroyWindow(hWnd); if (!fOk) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: DestroyWindow returned %d.\n", GetLastError() )); } return fOk; } BOOL CloseSession( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Close WinSNMP session associated with MGMTAPI session. Arguments: pSMS - pointer to MGMTAPI session structure. Return Values: Returns true if successful. --*/ { BOOL fOk = TRUE; SNMPAPI_STATUS status; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // check if window opened if (pSMS->hWnd != (HWND)NULL) { // destroy notification window fOk = DestroyNotificationWindow(pSMS->hWnd); } // check if agent entity allocated if (pSMS->hAgentEntity != (HSNMP_ENTITY)NULL) { // close the entity handle status = SnmpFreeEntity(pSMS->hAgentEntity); // validate status if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpFreeEntity returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); // failure fOk = FALSE; } // re-initialize pSMS->hAgentEntity = (HSNMP_ENTITY)NULL; } // check if manager entity allocated if (pSMS->hManagerEntity != (HSNMP_ENTITY)NULL) { // close the entity handle status = SnmpFreeEntity(pSMS->hManagerEntity); // validate status if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpFreeEntity returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); // failure fOk = FALSE; } // re-initialize pSMS->hManagerEntity = (HSNMP_ENTITY)NULL; } // check if session allocated if (pSMS->hSnmpSession != (HSNMP_SESSION)NULL) { // close the winsnmp session status = SnmpClose(pSMS->hSnmpSession); // validate status if (WSNMP_FAILED(status)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpClose returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); // failure fOk = FALSE; } // re-initialize pSMS->hSnmpSession = (HSNMP_SESSION)NULL; } return fOk; } //SNMPAPI_STATUS SNMPAPI_CALL // SnmpConveyAgentAddress (SNMPAPI_STATUS mode); BOOL OpenSession( PSNMP_MGR_SESSION pSMS, LPSTR pAgentAddress, LPSTR pAgentCommunity, INT nTimeOut, INT nRetries ) /*++ Routine Description: Open WinSNMP session and associate with MGMTAPI session. Arguments: pSMS - pointer to MGMTAPI session structure. pAgentAddress - points to a null-terminated string specifying either a dotted-decimal IP address or a host name that can be resolved to an IP address, an IPX address (in 8.12 notation), or an ethernet address. pAgentCommunity - points to a null-terminated string specifying the SNMP community name used when communicating with the agent specified in the lpAgentAddress parameter nTimeOut - specifies the communications time-out in milliseconds. nRetries - specifies the communications retry count. Return Values: Returns true if successful. --*/ { BOOL fOk; struct sockaddr AgentSockAddr; CHAR AgentStrAddr[MAXENTITYSTRLEN+1]; smiOCTETS smiCommunity; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // initialize notification window if (!CreateNotificationWindow(pSMS)) { return FALSE; // bail... } // open a winsnmp session which corresponds to mgmtapi session pSMS->hSnmpSession = SnmpOpen(pSMS->hWnd, WM_WSNMP_INCOMING); // --ft // we need to turn this on in order to have WINSNMP to pass back not // only the entity standing for the source Ip address but also the // agent address as it was sent into the V1 Trap Pdu. Without it, // SnmpMgrGetTrapEx() will return a NULL address for the pSourceAddress // paramter. However, SnmpMgrGetTrapEx() is not documented!!! //SnmpConveyAgentAddress(SNMPAPI_ON); // Move this into wsnmp_cf.c:SnmpStartup // to avoid missing entry point problem when wsnmp32.dll is from other vendors // validate session handle returned if (WSNMP_FAILED(pSMS->hSnmpSession)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpOpen returned %d.\n", SnmpGetLastError((HSNMP_SESSION)NULL) )); // re-initialize pSMS->hSnmpSession = (HSNMP_SESSION)NULL; goto cleanup; // bail... } // validate pointer if (pAgentAddress != NULL) { // use snmpapi.dll to do convert to sockets structure if (!SnmpSvcAddrToSocket(pAgentAddress, &AgentSockAddr)) { goto cleanup; // bail... } // check address family of agent if (AgentSockAddr.sa_family == AF_INET) { LPSTR pAgentStrAddr; struct sockaddr_in * pAgentSockAddr; // cast generic socket address structure to inet pAgentSockAddr = (struct sockaddr_in *)&AgentSockAddr; // obtain exclusive access to api EnterCriticalSection(&g_GlobalLock); // attempt to convert address into string pAgentStrAddr = inet_ntoa(pAgentSockAddr->sin_addr); // copy to stack variable strcpy(AgentStrAddr, pAgentStrAddr); // release exclusive access to api LeaveCriticalSection(&g_GlobalLock); } else if (AgentSockAddr.sa_family == AF_IPX) { // simply copy original string strcpy(AgentStrAddr, pAgentAddress); } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Incorrect address family.\n" )); goto cleanup; // bail... } // create remote agent entity pSMS->hAgentEntity = SnmpStrToEntity( pSMS->hSnmpSession, AgentStrAddr ); // validate agent entity returned if (WSNMP_FAILED(pSMS->hAgentEntity)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpStrToEntity returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // re-initialize pSMS->hAgentEntity = (HSNMP_ENTITY)NULL; goto cleanup; // bail... } // attach timeout specified with agent SnmpSetTimeout(pSMS->hAgentEntity, nTimeOut / 10); // attach retries specified with agent SnmpSetRetry(pSMS->hAgentEntity, nRetries); // create local manager entity pSMS->hManagerEntity = SnmpStrToEntity( pSMS->hSnmpSession, (AgentSockAddr.sa_family == AF_INET) ? DEFAULT_ADDRESS_IP : DEFAULT_ADDRESS_IPX ); // validate manager entity returned if (WSNMP_FAILED(pSMS->hManagerEntity)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpStrToEntity returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // re-initialize pSMS->hManagerEntity = (HSNMP_ENTITY)NULL; goto cleanup; // bail... } // attach timeout specified with manager SnmpSetTimeout(pSMS->hManagerEntity, nTimeOut / 10); // attach retries specified with manager SnmpSetRetry(pSMS->hManagerEntity, nRetries); } // validate pointer if (pAgentCommunity != NULL) { // transfer community string smiCommunity.ptr = (smiLPBYTE)pAgentCommunity; smiCommunity.len = pAgentCommunity ? lstrlen(pAgentCommunity) : 0; // obtain context from community string pSMS->hViewContext = SnmpStrToContext( pSMS->hSnmpSession, &smiCommunity ); // validate context handle if (WSNMP_FAILED(pSMS->hViewContext)) { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpStrToContext returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // re-initialize pSMS->hViewContext = (HSNMP_CONTEXT)NULL; goto cleanup; // bail... } } // success return TRUE; cleanup: // cleanup resources CloseSession(pSMS); // failure return FALSE; } BOOL AllocateSession( PSNMP_MGR_SESSION * ppSMS ) /*++ Routine Description: Allocate mgmtapi session structure. Arguments: ppSMS - pointer to session pointer to return. Return Values: Returns true if successful. --*/ { PSNMP_MGR_SESSION pSMS = NULL; __try { // allocate new session table entry pSMS = SnmpUtilMemAlloc(sizeof(SNMP_MGR_SESSION)); // validate pointer if (pSMS != NULL) { // initialize session level lock InitializeCriticalSection(&pSMS->SessionLock); } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Could not allocate session.\n" )); // notify application of error SetLastError(SNMP_MEM_ALLOC_ERROR); } // transfer *ppSMS = pSMS; } __except(EXCEPTION_EXECUTE_HANDLER) { if (pSMS != NULL) { SnmpUtilMemFree(pSMS); pSMS = NULL; } } // return status return (pSMS != NULL); } VOID FreeSession( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Frees mgmtapi session structure. Arguments: pSMS - pointer to mgmtapi session structure. Return Values: None. --*/ { // is session valid? if (pSMS != NULL) { // destroy the session level lock DeleteCriticalSection(&pSMS->SessionLock); // free session object SnmpUtilMemFree(pSMS); } } BOOL ProcessAgentResponse( PSNMP_MGR_SESSION pSMS ) /*++ Routine Description: Message pump for notification window. Arguments: pSMS - pointer to MGMTAPI session structure. Return Values: Returns true if agent responded. --*/ { MSG msg; BOOL fOk = FALSE; BOOL fRet; // validate session ptr WSNMP_ASSERT(pSMS != NULL); // get the next message for this session while ((fRet = GetMessage(&msg, pSMS->hWnd, 0, 0))) { if (fRet == -1) { // If there is an error, GetMessage returns -1 pSMS->nLastError = SNMPAPI_OTHER_ERROR; SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: ProcessAgentResponse: GetMessage returns -1.\n" )); break; } // check for private message if (msg.message != WM_WSNMP_DONE) { // translate message TranslateMessage(&msg); // dispatch message DispatchMessage(&msg); } else { // success fOk = TRUE; break; } } return fOk; } DWORD WINAPI TrapThreadProc( LPVOID lpParam ) /*++ Routine Description: Trap processing procedure. Arguments: lpParam - unused thread parameter. Return Values: Returns NOERROR if successful. --*/ { SNMPAPI_STATUS status; PSNMP_MGR_SESSION pSMS; SNMPDBG(( SNMP_LOG_TRACE, "MGMTAPI: Trap thread starting...\n" )); // obtain pointer pSMS = &g_TrapSMS; // re-initialize pSMS->nLastError = 0; g_fIsTrapRegistered = FALSE; // init to failure. Note that there will // be only 1 instance of this thread // initialize winsnmp trap session if (OpenSession(pSMS, NULL, NULL, 0, 0)) { // register status = SnmpRegister( pSMS->hSnmpSession, (HSNMP_ENTITY)NULL, // hAgentEntity (HSNMP_ENTITY)NULL, // hManagerEntity (HSNMP_CONTEXT)NULL, // hViewContext (smiLPCOID)NULL, // notification SNMPAPI_ON ); // validate return code if (WSNMP_SUCCEEDED(status)) { // signal main thread that Trap has been registered with WinSNMP g_fIsTrapRegistered = TRUE; SetEvent(g_hTrapRegisterdEvent); // loop processing responses while (ProcessAgentResponse(pSMS)) { // // processing done in window procedure... // } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpRegister returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); // transfer last error to global structure pSMS->nLastError = SnmpGetLastError(pSMS->hSnmpSession); // signal main thread that there is an error // in registering Trap with WinSNMP SetEvent(g_hTrapRegisterdEvent); } } else { // transfer last error to global structure pSMS->nLastError = SnmpGetLastError((HSNMP_SESSION)NULL); // signal main thread that there is an error // in registering Trap with WinSNMP SetEvent(g_hTrapRegisterdEvent); } // free session CloseSession(pSMS); // obtain exclusive access EnterCriticalSection(&g_GlobalLock); // signal successful start g_fIsSnmpListening = FALSE; // release exclusive access LeaveCriticalSection(&g_GlobalLock); SNMPDBG(( SNMP_LOG_TRACE, "MGMTAPI: Trap thread exiting...\n" )); // success return NOERROR; } BOOL StartTrapsIfNecessary( HANDLE * phTrapAvailable ) /*++ Routine Description: Initializes global structures for trap listening. Arguments: phTrapAvailable - pointer to event for signalling traps. Return Values: Returns true if successful (must be called only once). --*/ { BOOL fOk = FALSE; DWORD dwTrapThreadId; DWORD dwWaitTrapRegisterd; // validate pointer if (phTrapAvailable != NULL) { // obtain exclusive access EnterCriticalSection(&g_GlobalLock); // transfer trap event to app *phTrapAvailable = g_hTrapEvent; // only start listening once if (g_fIsSnmpListening == FALSE) { // spawn client trap thread g_hTrapThread = CreateThread( NULL, // lpThreadAttributes 0, // dwStackSize TrapThreadProc, NULL, // lpParameter 0, // dwCreationFlags &dwTrapThreadId ); // signal successful start g_fIsSnmpListening = TRUE; // release exclusive access LeaveCriticalSection(&g_GlobalLock); // WinSE bug 6182 // wait for TrapThreadProc to signal sucessful or failure dwWaitTrapRegisterd = WaitForSingleObject(g_hTrapRegisterdEvent, INFINITE); if (dwWaitTrapRegisterd == WAIT_OBJECT_0) { if (g_fIsTrapRegistered == TRUE) fOk = TRUE; // success else { SetLastError(SNMP_MGMTAPI_TRAP_ERRORS); SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Traps are not accessible.\n" )); } } else { SetLastError(SNMP_MGMTAPI_TRAP_ERRORS); SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Traps are not accessible.\n" )); } } else { // whine about having called this before SetLastError(SNMP_MGMTAPI_TRAP_DUPINIT); SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: Duplicate registration detected.\n" )); // release exclusive access LeaveCriticalSection(&g_GlobalLock); } } return fOk; } /////////////////////////////////////////////////////////////////////////////// // // // Dll Entry Point // // // /////////////////////////////////////////////////////////////////////////////// BOOL DllMain( HANDLE hDll, DWORD dwReason, LPVOID lpReserved ) /*++ Routine Description: Dll entry point. Arguments: hDll - module handle. dwReason - reason DllMain is being called. lpReserved - unused. Return Values: None. --*/ { BOOL bOk = TRUE; __try { // determine reason for being called if (dwReason == DLL_PROCESS_ATTACH) { // initialize startup critical section InitializeCriticalSection(&g_GlobalLock); // initialize list of incoming traps InitializeListHead(&g_IncomingTraps); // optimize thread startup DisableThreadLibraryCalls(hDll); // save handle g_hDll = hDll; } else if (dwReason == DLL_PROCESS_DETACH) { // cleanup winsnmp CleanupIfNecessary(); // nuke startup critical section DeleteCriticalSection(&g_GlobalLock); } } __except(EXCEPTION_EXECUTE_HANDLER) { bOk = FALSE; } return bOk; } /////////////////////////////////////////////////////////////////////////////// // // // Public Procedures // // // /////////////////////////////////////////////////////////////////////////////// LPSNMP_MGR_SESSION SNMP_FUNC_TYPE SnmpMgrOpen( LPSTR pAgentAddress, LPSTR pAgentCommunity, INT nTimeOut, INT nRetries ) /*++ Routine Description: Initializes resources necessary for communication with specified agent. Arguments: pAgentAddress - points to a null-terminated string specifying either a dotted-decimal IP address or a host name that can be resolved to an IP address, an IPX address (in 8.12 notation), or an ethernet address. pAgentCommunity - points to a null-terminated string specifying the SNMP community name used when communicating with the agent specified in the lpAgentAddress parameter nTimeOut - specifies the communications time-out in milliseconds. nRetries - specifies the communications retry count. Return Values: Returns session handle if successful. --*/ { PSNMP_MGR_SESSION pSMS = NULL; // initialize winsnmp if (StartSnmpIfNecessary()) { // allocate mgmtapi session if (AllocateSession(&pSMS)) { // open session if (!OpenSession( pSMS, pAgentAddress, pAgentCommunity, nTimeOut, nRetries)) { // free session FreeSession(pSMS); // reset pSMS = NULL; } } } // return opaque pointer return (LPSNMP_MGR_SESSION)pSMS; } BOOL SNMP_FUNC_TYPE SnmpMgrCtl( LPSNMP_MGR_SESSION session, // pointer to the MGMTAPI session DWORD dwCtlCode, // control code for the command requested LPVOID lpvInBuffer, // buffer with the input parameters for the operation DWORD cbInBuffer, // size of lpvInBuffer in bytes LPVOID lpvOUTBuffer, // buffer for all the output parameters of the command DWORD cbOUTBuffer, // size of lpvOUTBuffer LPDWORD lpcbBytesReturned // space used from lpvOutBuffer ) /*++ Routine Description: Operates several control operations over the MGMTAPI session Arguments: pSession - pointer to the session to Return Values: --*/ { BOOL bOk = FALSE; PSNMP_MGR_SESSION pSMS = (PSNMP_MGR_SESSION)session; switch(dwCtlCode) { case MGMCTL_SETAGENTPORT: if (pSMS == NULL) SetLastError(SNMP_MGMTAPI_INVALID_SESSION); else if (lpvInBuffer == NULL || cbInBuffer < sizeof(UINT)) SetLastError(SNMP_MGMTAPI_INVALID_BUFFER); else if (WSNMP_FAILED(SnmpSetPort(pSMS->hAgentEntity, *(UINT*)lpvInBuffer))) SetLastError(SnmpGetLastError(pSMS->hSnmpSession)); else bOk = TRUE; break; default: SetLastError(SNMP_MGMTAPI_INVALID_CTL); break; } return bOk; } BOOL SNMP_FUNC_TYPE SnmpMgrClose( LPSNMP_MGR_SESSION session ) /*++ Routine Description: Cleanups resources needed for communication with specified agent. Arguments: session - points to an internal structure that specifies which session to close. Return Values: Returns true if successful. --*/ { PSNMP_MGR_SESSION pSMS = (PSNMP_MGR_SESSION)session; // validate pointer if (pSMS != NULL) { // close session CloseSession(pSMS); // free session FreeSession(pSMS); } return TRUE; } SNMPAPI SNMP_FUNC_TYPE SnmpMgrRequest( LPSNMP_MGR_SESSION session, BYTE requestType, SnmpVarBindList * pVarBindList, AsnInteger * pErrorStatus, AsnInteger * pErrorIndex ) /*++ Routine Description: Requests the specified operation be performed with the specified agent. Arguments: session - points to an internal structure that specifies the session that will perform the request. requestType - specifies the SNMP request type. pVarBindList - points to the variable bindings list pErrorStatus - points to a variable in which the error status result will be returned. pErrorIndex - points to a variable in which the error index result will be returned. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; SNMPAPI_STATUS status; PSNMP_MGR_SESSION pSMS = (PSNMP_MGR_SESSION)session; // validate pointers if ((pSMS != NULL) && (pErrorIndex != NULL) && (pErrorStatus != NULL) && (pVarBindList != NULL) && (pVarBindList->len != 0) && (pVarBindList->list != NULL)) { // obtain exclusive access to session EnterCriticalSection(&pSMS->SessionLock); // initialize session structure pSMS->pVarBindList = pVarBindList; pSMS->nPduType = (smiINT32)(BYTE)requestType; pSMS->hVbl = (HSNMP_VBL)NULL; pSMS->hPdu = (HSNMP_PDU)NULL; pSMS->nErrorStatus = 0; pSMS->nErrorIndex = 0; pSMS->nLastError = 0; // allocate resources if (AllocatePdu(pSMS)) { // actually send status = SnmpSendMsg( pSMS->hSnmpSession, pSMS->hManagerEntity, pSMS->hAgentEntity, pSMS->hViewContext, pSMS->hPdu ); // release now FreePdu(pSMS); // validate return code if (WSNMP_SUCCEEDED(status)) { // process agent response if (ProcessAgentResponse(pSMS) && (pSMS->nLastError == SNMP_ERROR_NOERROR)) { // update error status and index *pErrorStatus = pSMS->nErrorStatus; *pErrorIndex = pSMS->nErrorIndex; // success fOk = TRUE; } else { // set error to winsnmp error SetLastError(pSMS->nLastError); // failure fOk = FALSE; } } else { SNMPDBG(( SNMP_LOG_ERROR, "MGMTAPI: SnmpSendMsg returned %d.\n", SnmpGetLastError(pSMS->hSnmpSession) )); } } // release exclusive access to session LeaveCriticalSection(&pSMS->SessionLock); } return fOk; } BOOL SNMP_FUNC_TYPE SnmpMgrStrToOid( LPSTR pString, AsnObjectIdentifier * pOID ) /*++ Routine Description: Converts a string object identifier or object descriptor representation to an internal object identifier. Arguments: pString - points to a null-terminated string to be converted. pOID - points to an object identifier variable that will receive the converted value. Return Values: Returns true if successful. --*/ { // validate pointer to oid and string if ((pOID != NULL) && (pString != NULL)) { // forward to mibcc code for now return SnmpMgrText2Oid(pString, pOID); } return FALSE; } BOOL SNMP_FUNC_TYPE SnmpMgrOidToStr( AsnObjectIdentifier * pOID, LPSTR * ppString ) /*++ Routine Description: Converts an internal object identifier to a string object identifier or object descriptor representation. Arguments: pOID - pointers to object identifier to be converted. ppString - points to string pointer to receive converted value. Return Values: Returns true if successful. --*/ { // validate pointer to oid and string if ((pOID != NULL) && (ppString != NULL)) { // forward to mibcc code for now return SnmpMgrOid2Text(pOID, ppString); } return FALSE; } BOOL SNMP_FUNC_TYPE SnmpMgrTrapListen( HANDLE * phTrapAvailable ) /*++ Routine Description: Registers the ability of a manager application to receive SNMP traps. Arguments: phTrapAvailable - points to an event handle that will be used to indicate that there are traps available Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; // startup winsnmp if (StartSnmpIfNecessary()) { // spawn only one trap client thread if (StartTrapsIfNecessary(phTrapAvailable)) { // success fOk = TRUE; } } return fOk; } BOOL SNMP_FUNC_TYPE SnmpMgrGetTrap( AsnObjectIdentifier * pEnterpriseOID, AsnNetworkAddress * pAgentAddress, AsnInteger * pGenericTrap, AsnInteger * pSpecificTrap, AsnTimeticks * pTimeStamp, SnmpVarBindList * pVarBindList ) /*++ Routine Description: Returns outstanding trap data that the caller has not received if trap reception is enabled. Arguments: pEnterpriseOID - points to an object identifier that specifies the enterprise that generated the SNMP trap pAgentAddress - points to the address of the agent that generated the SNMP trap (retrieved from PDU). pGenericTrap - points to an indicator of the generic trap id. pSpecificTrap - points to an indicator of the specific trap id. pTimeStamp - points to a variable to receive the time stamp. pVarBindList - points to the associated variable bindings. Return Values: Returns true if successful. --*/ { // forward to new api return SnmpMgrGetTrapEx( pEnterpriseOID, pAgentAddress, NULL, pGenericTrap, pSpecificTrap, NULL, pTimeStamp, pVarBindList ); } BOOL SNMP_FUNC_TYPE SnmpMgrGetTrapEx( AsnObjectIdentifier * pEnterpriseOID, AsnNetworkAddress * pAgentAddress, AsnNetworkAddress * pSourceAddress, AsnInteger * pGenericTrap, AsnInteger * pSpecificTrap, AsnOctetString * pCommunity, AsnTimeticks * pTimeStamp, SnmpVarBindList * pVarBindList ) /*++ Routine Description: Returns outstanding trap data that the caller has not received if trap reception is enabled. Arguments: pEnterpriseOID - points to an object identifier that specifies the enterprise that generated the SNMP trap pAgentAddress - points to the address of the agent that generated the SNMP trap (retrieved from PDU). pSourceAddress - points to the address of the agent that generated the SNMP trap (retrieved from network transport). pGenericTrap - points to an indicator of the generic trap id. pSpecificTrap - points to an indicator of the specific trap id. pCommunity - points to structure to receive community string. pTimeStamp - points to a variable to receive the time stamp. pVarBindList - points to the associated variable bindings. Return Values: Returns true if successful. --*/ { BOOL fOk = FALSE; PLIST_ENTRY pLE = NULL; PTRAP_LIST_ENTRY pTLE = NULL; smiINT32 nLastError; // obtain exclusive access EnterCriticalSection(&g_GlobalLock); // make sure list has entries if (!IsListEmpty(&g_IncomingTraps)) { // remove first item from list pLE = RemoveHeadList(&g_IncomingTraps); } else { // check for trap thread failure nLastError = g_TrapSMS.nLastError; } // release exclusive access LeaveCriticalSection(&g_GlobalLock); // validate pointer if (pLE != NULL) { // retrieve pointer to trap list entry pTLE = CONTAINING_RECORD(pLE, TRAP_LIST_ENTRY, Link); // validate pointer if (pEnterpriseOID != NULL) { // manually copy enterprise oid *pEnterpriseOID = pTLE->EnterpriseOID; // re-initialize list entry pTLE->EnterpriseOID.ids = NULL; pTLE->EnterpriseOID.idLength = 0; } // validate pointer if (pCommunity != NULL) { // transfer string info *pCommunity = pTLE->Community; // re-initialize list entry pTLE->Community.length = 0; pTLE->Community.stream = NULL; pTLE->Community.dynamic = FALSE; } // validate pointer if (pVarBindList != NULL) { // transfer varbindlist *pVarBindList = pTLE->VarBindList; // re-initialize list entry pTLE->VarBindList.len = 0; pTLE->VarBindList.list = NULL; } // validate pointer if (pAgentAddress != NULL) { // copy structure memcpy(pAgentAddress, &pTLE->AgentAddress, sizeof(pTLE->AgentAddress) ); } // validate pointer if (pSourceAddress != NULL) { // copy structure memcpy(pSourceAddress, &pTLE->SourceAddress, sizeof(pTLE->SourceAddress) ); } // validate pointer if (pGenericTrap != NULL) { // transfer generic trap info *pGenericTrap = pTLE->nGenericTrap; } // validate pointer if (pSpecificTrap != NULL) { // transfer generic trap info *pSpecificTrap = pTLE->nSpecificTrap; } // validate pointer if (pTimeStamp != NULL) { // transfer time info *pTimeStamp = pTLE->TimeStamp; } // release FreeTle(pTLE); // success fOk = TRUE; } else if (nLastError != NOERROR) { // indicate there was an thread error SetLastError(SNMP_MGMTAPI_TRAP_ERRORS); } else { // indicate there are no traps SetLastError(SNMP_MGMTAPI_NOTRAPS); } return fOk; } VOID serverTrapThread( LPVOID pUnused ) /*++ Routine Description: Old thread procedure used by the SNMP Trap Service. Arguments: pUnused - unused parameter. Return Values: None. --*/ { // // do nothing here... // }