You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
608 lines
16 KiB
608 lines
16 KiB
//***************************************************************************
|
|
|
|
//
|
|
|
|
// File:
|
|
|
|
//
|
|
|
|
// Module: MS SNMP Provider
|
|
|
|
//
|
|
|
|
// Purpose:
|
|
|
|
//
|
|
|
|
// Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
//***************************************************************************
|
|
|
|
/*---------------------------------------------------------
|
|
Filename: op.cpp
|
|
Written By: B.Rajeev
|
|
----------------------------------------------------------*/
|
|
|
|
#include "precomp.h"
|
|
#include "common.h"
|
|
#include "sync.h"
|
|
#include "encap.h"
|
|
#include "value.h"
|
|
#include "vblist.h"
|
|
#include "vbl.h"
|
|
#include "fs_reg.h"
|
|
#include "error.h"
|
|
#include "sec.h"
|
|
#include "pdu.h"
|
|
#include "pseudo.h"
|
|
|
|
#include "dummy.h"
|
|
#include "flow.h"
|
|
#include "frame.h"
|
|
#include "timer.h"
|
|
#include "message.h"
|
|
#include "ssent.h"
|
|
#include "idmap.h"
|
|
#include "opreg.h"
|
|
|
|
#include "session.h"
|
|
#include "ophelp.h"
|
|
#include "op.h"
|
|
#include "encdec.h"
|
|
|
|
|
|
void SnmpOperation::ReceiveResponse()
|
|
{
|
|
}
|
|
|
|
// the operation uses window messaging encapsulated within the dummy session
|
|
// to make the callbacks to an operation user, asynchronous with respect
|
|
// to the user's call to SendRequest
|
|
// the events currently posted are SEND_ERROR and OPERATION_COMPLETION
|
|
LONG SnmpOperation::ProcessInternalEvent(
|
|
|
|
HWND hWnd,
|
|
UINT user_msg_id,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
LONG rc = 0;
|
|
|
|
CriticalSectionLock access_lock(exclusive_CriticalSection); // TRUE
|
|
|
|
// obtain exclusive access into the system
|
|
if ( !access_lock.GetLock(INFINITE) )
|
|
{
|
|
is_valid = FALSE;
|
|
return rc;
|
|
}
|
|
|
|
// return immediately, if the operation is not valid
|
|
if ( !is_valid )
|
|
return rc;
|
|
|
|
// process the message
|
|
|
|
if ( user_msg_id == Window :: g_SendErrorEvent )
|
|
{
|
|
// in case the attempt to send a frame failed
|
|
VBList *vblist = (VBList *)wParam;
|
|
GeneralException *exception = (GeneralException *)lParam;
|
|
|
|
ReceiveErroredResponse(vblist->GetIndex (),vblist->GetVarBindList(), *exception);
|
|
|
|
delete vblist;
|
|
delete exception;
|
|
}
|
|
else if ( user_msg_id == Window :: g_OperationCompletedEvent )
|
|
{
|
|
// signals completion of the operation
|
|
|
|
frame_state_registry.DestroySecurity();
|
|
in_progress = FALSE;
|
|
ReceiveResponse();
|
|
}
|
|
else
|
|
{
|
|
// predefined window message
|
|
DefWindowProc(hWnd, user_msg_id, wParam, lParam);
|
|
}
|
|
|
|
// give up exclusive access
|
|
access_lock.UnLock();
|
|
|
|
// since this is also a point of entry into the SnmpOperation
|
|
// we must check for deletion of the operation
|
|
CheckOperationDeletion();
|
|
|
|
return rc;
|
|
}
|
|
|
|
// this method may be called to delete the Operation
|
|
// note: the operation is deleted when a public method
|
|
// returns. For this reason, if a public method calls another
|
|
// public method, it must not access any per-class variables
|
|
// after that.
|
|
void SnmpOperation::DestroyOperation()
|
|
{
|
|
delete_operation = TRUE;
|
|
}
|
|
|
|
|
|
// its mandatory for every public method to call this method
|
|
// before returning to the caller
|
|
// it checks if the call sequence included a call to DestroyOperation
|
|
// and if so, deletes "this" before returning
|
|
void SnmpOperation::CheckOperationDeletion()
|
|
{
|
|
if ( delete_operation == TRUE )
|
|
delete this;
|
|
}
|
|
|
|
#pragma warning (disable:4355)
|
|
|
|
// initializes variables and, if successful, registers itself with the session
|
|
SnmpOperation::SnmpOperation(
|
|
|
|
SnmpSession &snmp_session
|
|
|
|
) : session(snmp_session),
|
|
m_OperationWindow(*this),
|
|
helper(*this)
|
|
{
|
|
in_progress = FALSE;
|
|
is_valid = FALSE;
|
|
|
|
if ( !m_OperationWindow() )
|
|
return;
|
|
|
|
varbinds_per_pdu = SnmpImpSession :: VarbindsPerPdu ( session.GetVarbindsPerPdu() ) ;
|
|
|
|
delete_operation = FALSE;
|
|
|
|
session.RegisterOperation(*this);
|
|
|
|
is_valid = TRUE;
|
|
}
|
|
|
|
#pragma warning (default:4355)
|
|
|
|
// on destruction, the operation cancels all outstanding frames,
|
|
// frees the allocated memory for variables and deregisters with the session
|
|
SnmpOperation::~SnmpOperation(void)
|
|
{
|
|
// calls to public functions from this point should not
|
|
// cause repeated deletion - so set the flag to FALSE
|
|
delete_operation = FALSE;
|
|
|
|
// cancel any outstanding frames
|
|
CancelRequest();
|
|
|
|
// deregister with session
|
|
session.DeregisterOperation(*this);
|
|
}
|
|
|
|
|
|
// sends the varbinds in the var bind list packaged in several
|
|
// frames each carrying atmost varbinds_per_pdu varbinds
|
|
// if the security context is not NULL, same is used as the context
|
|
// for all the generated frames
|
|
|
|
void SnmpOperation::SendRequest(
|
|
|
|
IN SnmpVarBindList &varBindList,
|
|
IN SnmpSecurity *security
|
|
)
|
|
{
|
|
// if not valid, return immediately
|
|
if ( !is_valid )
|
|
return;
|
|
|
|
CriticalSectionLock access_lock(exclusive_CriticalSection); // TRUE
|
|
|
|
// obtain exclusive access into the system
|
|
if ( !access_lock.GetLock(INFINITE) )
|
|
{
|
|
is_valid = FALSE;
|
|
return;
|
|
}
|
|
|
|
// if already in progress, we cannot proceed
|
|
if ( in_progress == TRUE )
|
|
return;
|
|
|
|
in_progress = TRUE;
|
|
|
|
// if length of varBindList exceeds varbinds_per_pdu
|
|
// call FrameOverRun()
|
|
if ( varBindList.GetLength() > varbinds_per_pdu )
|
|
FrameOverRun();
|
|
|
|
// check if the send request has been cancelled in the
|
|
// meantime. Proceed only if still in progress
|
|
if ( !in_progress )
|
|
return;
|
|
|
|
// register the security for the duration of SendRequest
|
|
// (until a reply for the last outstanding frame is received)
|
|
frame_state_registry.RegisterSecurity(security);
|
|
|
|
// send the varbind list
|
|
SendVarBindList(varBindList);
|
|
|
|
// if no outstanding frames, post a message for the completion
|
|
// of operation. This message, when processed, shall set the
|
|
// in_progress status, destroy security and call ReceiveResponse
|
|
if ( frame_state_registry.Empty() )
|
|
{
|
|
m_OperationWindow.PostMessage (
|
|
|
|
Window :: g_OperationCompletedEvent ,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
// give up exclusive access
|
|
// access_lock.UnLock(); The lock may be released at this point
|
|
}
|
|
|
|
|
|
void SnmpOperation::SendRequest(IN SnmpVarBindList &varBindList)
|
|
{
|
|
SendRequest(varBindList, NULL);
|
|
CheckOperationDeletion();
|
|
}
|
|
|
|
void SnmpOperation::SendRequest(
|
|
|
|
IN SnmpVarBindList &varBindList,
|
|
IN SnmpSecurity &security
|
|
)
|
|
{
|
|
SendRequest(varBindList, &security);
|
|
CheckOperationDeletion();
|
|
}
|
|
|
|
|
|
// sends the varbinds in the var bind list packaged in several
|
|
// frames each carrying atmost MIN(varbinds_per_pdu, max_size) varbinds
|
|
void SnmpOperation::SendVarBindList(IN SnmpVarBindList &varBindList,
|
|
IN UINT max_size,
|
|
IN ULONG var_index )
|
|
{
|
|
UINT max_varbinds_per_pdu = MIN(varbinds_per_pdu, max_size);
|
|
UINT list_length = varBindList.GetLength();
|
|
|
|
// set list iterator to the start of the list,
|
|
// current_position <- 0
|
|
varBindList.Reset();
|
|
varBindList.Next();
|
|
UINT current_position = 0;
|
|
|
|
// chop up the varBindList into segments atmost max_varbinds_per_pdu
|
|
// in size and send them in separate frames
|
|
while ( current_position < list_length )
|
|
{
|
|
UINT segment_length = MIN((list_length-current_position), max_varbinds_per_pdu);
|
|
|
|
// create copy of the varBindList from
|
|
// current_position (of length segment_length)
|
|
SnmpVarBindList *list_segment = varBindList.CopySegment(segment_length);
|
|
|
|
// create a VBList and call SendFrame with it
|
|
|
|
SendFrame (
|
|
|
|
*(new VBList(session.GetSnmpEncodeDecode (),*list_segment,var_index + current_position + 1))
|
|
);
|
|
|
|
// update current_position
|
|
current_position += segment_length;
|
|
}
|
|
}
|
|
|
|
|
|
// transmits a frame with the var binds in the vblist
|
|
// using the session and registers the frame state
|
|
void SnmpOperation::SendFrame(VBList &vblist)
|
|
{
|
|
try
|
|
{
|
|
SessionFrameId session_frame_id = 0L;
|
|
|
|
helper.TransmitFrame (
|
|
|
|
session_frame_id,
|
|
vblist
|
|
);
|
|
|
|
FrameState *frame_state = new FrameState(session_frame_id,vblist);
|
|
|
|
// insert a frame_state(session_frame_id, vblist)
|
|
frame_state_registry.Insert(session_frame_id, *frame_state );
|
|
}
|
|
catch(GeneralException exception)
|
|
{
|
|
// post a message to signal the error in sending the frame
|
|
// when processed, it shall call ReceiveErroredResponse and
|
|
// delete the vblist
|
|
m_OperationWindow.PostMessage (
|
|
|
|
Window :: g_SendErrorEvent ,
|
|
(WPARAM)&vblist,
|
|
(LPARAM)(new GeneralException(exception))
|
|
);
|
|
}
|
|
}
|
|
|
|
// a sent frame notification from the session signifies one transmission
|
|
// of the frame. atmost one notification per session frame id can
|
|
// signal an error in transmission
|
|
void SnmpOperation::SentFrame(
|
|
|
|
IN const SessionFrameId session_frame_id,
|
|
IN const SnmpErrorReport &error_report
|
|
)
|
|
{
|
|
DebugMacro4(
|
|
|
|
SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine (
|
|
|
|
__FILE__,__LINE__,
|
|
L"Sent %d\n" ,session_frame_id
|
|
) ;
|
|
)
|
|
|
|
// if there was an error in sending, let ReceiveFrame handle it
|
|
// otherwise ignore it and wait for the reply
|
|
if ( error_report.GetError() != Snmp_Success )
|
|
{
|
|
ReceiveFrame (
|
|
|
|
session_frame_id,
|
|
SnmpPdu(),
|
|
error_report
|
|
);
|
|
}
|
|
else
|
|
{
|
|
CheckOperationDeletion();
|
|
}
|
|
|
|
// since ReceiveFrame might have deleted the operation, we
|
|
// must call CheckOperationDeletion only in the else
|
|
}
|
|
|
|
// cancels all the frames whose frame states are currently present in
|
|
// the frame state registry
|
|
void SnmpOperation::CancelRequest()
|
|
{
|
|
// if not valid, return immediately
|
|
if ( !is_valid )
|
|
return;
|
|
|
|
CriticalSectionLock exclusive_lock(exclusive_CriticalSection);
|
|
|
|
// obtain exclusive access
|
|
if ( !exclusive_lock.GetLock(INFINITE) )
|
|
{
|
|
is_valid = FALSE;
|
|
return;
|
|
}
|
|
|
|
// if not in progress, there is nothing to be done
|
|
if ( !in_progress )
|
|
return;
|
|
|
|
// reset the frame_state_registry to set
|
|
// the iterator to the beginning
|
|
frame_state_registry.ResetIterator();
|
|
|
|
// cancel all outstanding frames
|
|
while (1)
|
|
{
|
|
// for each registered frame_state
|
|
|
|
// remove it from the frame_state_registry
|
|
FrameState *frame_state = frame_state_registry.GetNext();
|
|
|
|
// if no more frames, we are done
|
|
if ( frame_state == NULL )
|
|
break;
|
|
|
|
// cancel the corresponding frame
|
|
session.SessionCancelFrame(
|
|
|
|
frame_state->GetSessionFrameId()
|
|
);
|
|
|
|
// destroy frame_state
|
|
delete frame_state;
|
|
}
|
|
|
|
// remove all the associations
|
|
frame_state_registry.RemoveAll();
|
|
|
|
// destroy the security
|
|
frame_state_registry.DestroySecurity();
|
|
|
|
// in_progress <- FALSE
|
|
in_progress = FALSE;
|
|
|
|
m_OperationWindow.PostMessage (
|
|
|
|
Window :: g_OperationCompletedEvent ,
|
|
0,
|
|
0
|
|
);
|
|
|
|
// leave exclusive access
|
|
exclusive_lock.UnLock();
|
|
|
|
CheckOperationDeletion();
|
|
}
|
|
|
|
|
|
void SnmpOperation::ReceiveErroredResponse(
|
|
|
|
ULONG var_index ,
|
|
SnmpVarBindList &errored_list,
|
|
const SnmpErrorReport &error_report
|
|
)
|
|
{
|
|
ULONG t_Index = 0 ;
|
|
errored_list.Reset();
|
|
while( errored_list.Next() )
|
|
{
|
|
const SnmpVarBind *var_bind = errored_list.Get();
|
|
|
|
ReceiveErroredVarBindResponse(
|
|
|
|
var_index + t_Index ,
|
|
*var_bind,
|
|
error_report
|
|
);
|
|
|
|
t_Index ++ ;
|
|
}
|
|
}
|
|
|
|
|
|
// ReceiveFrame is called by the session when a reply is received for
|
|
// an outstanding frame or it has received no response for its
|
|
// retransmissions. It may also be called by the SnmpOperation::SentFrame
|
|
// when the error report shows an error during transmission
|
|
// It decodes the received snmp pdu and processes it or else, if no
|
|
// reply has been received, informs the user of the error report
|
|
// when all no outstanding frames remain, an OPERATION_COMPLETION event
|
|
// is posted to inform the user of the event asynchronously
|
|
|
|
void SnmpOperation::ReceiveFrame(
|
|
|
|
IN const SessionFrameId session_frame_id,
|
|
IN const SnmpPdu &snmpPdu,
|
|
IN const SnmpErrorReport &errorReport
|
|
)
|
|
{
|
|
DebugMacro4(
|
|
|
|
SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine (
|
|
|
|
__FILE__,__LINE__,
|
|
L"SnmpOperation::ReceiveFrame: received(%d), error(%d), status(%d)\n",session_frame_id, errorReport.GetError(), errorReport.GetStatus()
|
|
) ;
|
|
)
|
|
|
|
// if not valid, return immediately
|
|
if ( !is_valid )
|
|
return;
|
|
|
|
CriticalSectionLock exclusive_lock(exclusive_CriticalSection);
|
|
|
|
// obt. exclusive access
|
|
if ( !exclusive_lock.GetLock(INFINITE) )
|
|
{
|
|
is_valid = FALSE;
|
|
return;
|
|
}
|
|
|
|
// if not in progress, nothing needs to be done
|
|
// ignore the
|
|
if ( !in_progress )
|
|
return;
|
|
|
|
// get corresponding frame_state
|
|
FrameState *frame_state = frame_state_registry.Remove(session_frame_id);
|
|
|
|
// if no such frame_state return
|
|
if ( frame_state == NULL )
|
|
return;
|
|
|
|
// decode the frame to extract
|
|
// vbl, error-index, error-status
|
|
|
|
SnmpErrorReport t_SnmpErrorReport ;
|
|
SnmpVarBindList *t_SnmpVarBindList ;
|
|
SnmpCommunityBasedSecurity *t_SnmpCommunityBasedSecurity = NULL ;
|
|
SnmpTransportAddress *t_SrcTransportAddress = NULL ;
|
|
SnmpTransportAddress *t_DstTransportAddress = NULL ;
|
|
SnmpEncodeDecode :: PduType t_PduType = SnmpEncodeDecode :: PduType :: GET;
|
|
RequestId t_RequestId = 0 ;
|
|
|
|
try
|
|
{
|
|
session.GetSnmpEncodeDecode ().DecodeFrame (
|
|
|
|
( SnmpPdu& ) snmpPdu ,
|
|
t_RequestId ,
|
|
t_PduType ,
|
|
t_SnmpErrorReport ,
|
|
t_SnmpVarBindList ,
|
|
t_SnmpCommunityBasedSecurity ,
|
|
t_SrcTransportAddress ,
|
|
t_DstTransportAddress
|
|
);
|
|
}
|
|
catch(GeneralException exception)
|
|
{
|
|
CheckOperationDeletion();
|
|
return;
|
|
}
|
|
|
|
t_SnmpErrorReport = errorReport ;
|
|
|
|
helper.ProcessResponse (
|
|
|
|
frame_state,
|
|
*t_SnmpVarBindList,
|
|
t_SnmpErrorReport
|
|
);
|
|
|
|
// if the registry is empty,
|
|
// destroy security, set in_progress, release exclusive access
|
|
// call ReceiveResponse() to signal completion finally
|
|
if ( frame_state_registry.Empty() )
|
|
{
|
|
frame_state_registry.DestroySecurity();
|
|
in_progress = FALSE;
|
|
|
|
// leave exclusive access: so that the ReceiveResponse
|
|
// call back may be able to make another SendRequest
|
|
exclusive_lock.UnLock();
|
|
|
|
// call the user to inform him of completion
|
|
ReceiveResponse();
|
|
}
|
|
else
|
|
{
|
|
// leave exclusive access
|
|
exclusive_lock.UnLock();
|
|
}
|
|
|
|
CheckOperationDeletion();
|
|
}
|
|
|
|
|
|
// The GetOperation sends the GET PDU
|
|
SnmpEncodeDecode :: PduType SnmpGetOperation::GetPduType(void)
|
|
{
|
|
return SnmpEncodeDecode :: GET;
|
|
}
|
|
|
|
|
|
// The GetOperation sends the GETNEXT PDU
|
|
SnmpEncodeDecode ::PduType SnmpGetNextOperation::GetPduType(void)
|
|
{
|
|
return SnmpEncodeDecode :: GETNEXT;
|
|
}
|
|
|
|
|
|
// The GetOperation sends the SET PDU
|
|
SnmpEncodeDecode :: PduType SnmpSetOperation::GetPduType(void)
|
|
{
|
|
return SnmpEncodeDecode :: SET;
|
|
}
|
|
|
|
|