Leaked source code of windows server 2003
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

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