|
|
//***************************************************************************
//
// 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; }
|