//*************************************************************************** // // File: // // Module: MS SNMP Provider // // Purpose: // // Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved // //*************************************************************************** /*--------------------------------------------------------- Filename: message.cpp Written By: B.Rajeev ----------------------------------------------------------*/ #include "precomp.h" #include "common.h" #include "sync.h" #include "message.h" #include "idmap.h" #include "dummy.h" #include "flow.h" #include "vblist.h" #include "sec.h" #include "pdu.h" #include "frame.h" #include "opreg.h" #include "ssent.h" #include "session.h" #define ILLEGAL_PDU_HANDLE 100000 #define ILLEGAL_VBL_HANDLE 100000 Message::Message(IN const SessionFrameId session_frame_id, IN SnmpPdu &snmp_pdu, SnmpOperation &snmp_operation ) : snmp_pdu(&snmp_pdu), operation(snmp_operation) { Message::session_frame_id = session_frame_id; } SessionFrameId Message::GetSessionFrameId(void) const { return session_frame_id; } SnmpOperation &Message::GetOperation(void) const { return operation; } SnmpPdu &Message::GetSnmpPdu(void) const { return *snmp_pdu; } void Message::SetSnmpPdu(IN SnmpPdu &new_snmp_pdu) { delete snmp_pdu; snmp_pdu = &new_snmp_pdu; } Message::~Message(void) { delete snmp_pdu; } // deregisters the waiting message from the message registry // for each request id stored in the RequestIdList void WaitingMessage::DeregisterRequestIds() { for( UINT request_ids_left = request_id_list.GetCount(); request_ids_left > 0; request_id_list.RemoveHead(), request_ids_left--) { RequestId request_id = request_id_list.GetHead(); session->message_registry.RemoveMessage(request_id); } } // an exit fn - prepares an error report and calls // ReceiveReply to signal a non-receipt void WaitingMessage::WrapUp(IN SnmpErrorReport &error_report) { DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: WrapUp () session_id(%d), frame_id(%d)\n" ,message->GetSessionFrameId(), last_transport_frame_id ) ; ) try // ignore any exceptions arising during the ReceiveReply { // no reply to receive ReceiveReply(NULL, error_report); } catch ( Heap_Exception e_He ) {} catch(GeneralException exception) {} } // initializes the private variables WaitingMessage::WaitingMessage(IN SnmpImpSession &session, IN Message &message) : session ( NULL ) , message ( NULL ), reply_snmp_pdu ( NULL ) { WaitingMessage::session = &session; // the message ptr must be deleted by the waiting message WaitingMessage::message = &message; // sent message has not been processed yet sent_message_processed = FALSE; // set illegal values for last_transport_frame_id last_transport_frame_id = ILLEGAL_TRANSPORT_FRAME_ID; // these values are currently obtained from the // session, but may be specified per message later max_rexns = SnmpImpSession :: RetryCount ( session.GetRetryCount() ) ; rexns_left = max_rexns; strobes = 0 ; active = FALSE; } // sends the message. involves request_id generation, // registering with the message_registry, decoding the // message and updating the pdu and registering a timer // event void WaitingMessage::Transmit() { try { // generate request_id and register with the registry RequestId request_id = session->message_registry.GenerateRequestId(*this); // insert the request id into the message // if unsuccessful, the exception handler gets called session->m_EncodeDecode.SetRequestId( message->GetSnmpPdu(), request_id ); last_transport_frame_id = request_id ; // append the request id to the request id list request_id_list.AddTail(request_id); DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: Transmit - About to transmit (session (%d), frame_id(%d))\n", message->GetSessionFrameId(), request_id ) ; ) // save the previous value of active and set the active // flag. This is needed to check on returning whether the // waiting message needs to be destroyed BOOL prev_active_state = active; active = TRUE; strobes = GetTickCount () ; // send message session->transport.TransportSendFrame(last_transport_frame_id, message->GetSnmpPdu()); session->id_mapping.Associate(last_transport_frame_id, message->GetSessionFrameId()); // if asked to destroy self, well, do it (and return) if ( !active ) { delete this; return; } // restore the previous value of "active" active = prev_active_state; // generate timer_event_id and register with the timer session->timer.SetMessageTimerEvent(*this); DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: Transmit - Transmitted session_id(%d),frame_id(%d))\n",message->GetSessionFrameId(), request_id ) ; ) } catch ( Heap_Exception e_He ) { WrapUp(GeneralException(Snmp_Error, Snmp_Local_Error,__FILE__,__LINE__)); throw; } catch(GeneralException exception) { WrapUp(exception); throw; } } // used by the timer to notify the waiting message of // a timer event. if need, the message is retransmitted. // when all rexns are exhausted, ReceiveReply is called void WaitingMessage::TimerNotification() { DWORD t_Ticks = GetTickCount () ; if ( strobes > t_Ticks ) { strobes = t_Ticks ; // Take hit on clock overflow return ; } if ( ( t_Ticks - strobes ) >= SnmpImpSession :: RetryTimeout ( session->GetRetryTimeout () ) ) { DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: TimerNotification - timed out after (%ld)" , ( t_Ticks - strobes ) ) ; ) // if any rexns left, update rexns_left, send message if ( rexns_left > 0 ) { // generate request_id and register with the registry RequestId request_id = session->message_registry.GenerateRequestId(*this); // insert the request id into the message // if unsuccessful, the exception handler gets called try { session->m_EncodeDecode.SetRequestId( message->GetSnmpPdu() , request_id ); } catch ( Heap_Exception e_He ) { WrapUp(GeneralException(Snmp_Error, Snmp_Local_Error,__FILE__,__LINE__)); return ; } catch(GeneralException exception) { WrapUp(exception); return; } last_transport_frame_id = request_id ; // append the request id to the request id list request_id_list.AddTail(request_id); BOOL prev_active_state = active; active = TRUE; strobes = GetTickCount () ; DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: TimerNotification - Resend %d.%d, req_id(%d), this(%d) at time (%d)\n",message->GetSessionFrameId(), rexns_left, request_id, this, strobes ) ; ) session->id_mapping.DisassociateTransportFrameId(last_transport_frame_id); // send message session->transport.TransportSendFrame(last_transport_frame_id, message->GetSnmpPdu()); // associate the last transport frame id with the session frame id session->id_mapping.Associate(last_transport_frame_id, message->GetSessionFrameId()); // if asked to destroy self, well, do it (and return) if ( !active ) { delete this; return; } // restore the previous value of "active" active = prev_active_state; rexns_left--; DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: TimerNotification - Retransmitted session_id(%d),frame_id(%d))\n",message->GetSessionFrameId(), last_transport_frame_id ) ; ) } else { DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: TimerNotification - No response session_id(%d),frame_id(%d))\n",message->GetSessionFrameId(), last_transport_frame_id ) ; ) // else wrap up as no response has been received WrapUp(SnmpErrorReport(Snmp_Error, Snmp_No_Response)); return; // since the waiting_message would have been destroyed } } else { } } // A call to this function signifies that state corresponding to the // waiting_message need not be kept any further // if required, it cancels the timer event and // deregisters with the message registry // it notifies the flow control mechanism of the termination // which destroys the waiting_message void WaitingMessage::ReceiveReply(IN const SnmpPdu *snmp_pdu, IN SnmpErrorReport &error_report) { DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: ReceiveReply (this(%d), session_id(%d),frame_id(%d),error(%d), status(%d))\n",this, message->GetSessionFrameId(), last_transport_frame_id,error_report.GetError(), error_report.GetStatus() ) ; ) // cancels registrations with message registry DeregisterRequestIds(); // cancels timer event session->timer.CancelMessageTimer(*this,session->timer_event_id); // if required (the corresponding SENT event has not been signaled // yet), cancel the association with the last transport frame id if ( last_transport_frame_id != ILLEGAL_TRANSPORT_FRAME_ID ) { session->id_mapping.DisassociateTransportFrameId(last_transport_frame_id); last_transport_frame_id = ILLEGAL_TRANSPORT_FRAME_ID; } // call fc_mech.NotifyReceipt(this,pdu,error_report) // which should destroy the waiting message session->flow_control.NotifyReceipt(*this, snmp_pdu, error_report); } // buffers the snmp pdu received as a reply void WaitingMessage::BufferReply(IN const SnmpPdu &reply_snmp_pdu) { if ( WaitingMessage::reply_snmp_pdu == NULL ) { DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"WaitingMessage :: Buffering reply %d, %d, this(%d)\n",message->GetSessionFrameId(), rexns_left, this ) ; ) WaitingMessage::reply_snmp_pdu = new SnmpPdu((SnmpPdu&)reply_snmp_pdu); } } // returns TRUE if a reply has been buffered BOOL WaitingMessage::ReplyBuffered() { return (reply_snmp_pdu != NULL); } // returns a ptr to the buffered reply pdu, if buffered // otherwise a null ptr is returned // IMPORTANT: it sets the reply_snmp_pdu to NULL, so that it may not // be deleted when the waiting message is destroyed SnmpPdu *WaitingMessage::GetBufferedReply() { SnmpPdu *to_return = reply_snmp_pdu; reply_snmp_pdu = NULL; return to_return; } // informs the waiting message that a sent message has been // processed void WaitingMessage::SetSentMessageProcessed() { sent_message_processed = TRUE; } // if a sent message has been processed, it returns TRUE, else FALSE BOOL WaitingMessage::GetSentMessageProcessed() { return sent_message_processed; } void WaitingMessage::SelfDestruct(void) { if ( !active ) { delete this; return; } else // else, set the active flag to FALSE // when this is detected, it'll self destruct active = FALSE; } TimerEventId WaitingMessage::GetTimerEventId () { return m_TimerEventId ; } void WaitingMessage::SetTimerEventId ( TimerEventId a_TimerEventId ) { m_TimerEventId = a_TimerEventId ; } // if required, it cancels registration with the message_registry and // the timer event with the timer. WaitingMessage::~WaitingMessage(void) { // if required, cancel registrations with message registry if ( !request_id_list.IsEmpty() ) DeregisterRequestIds(); session->timer.CancelMessageTimer(*this,session->timer_event_id); // if required (the corresponding SENT event has not been signaled // yet), cancel the association with the last transport frame id if ( last_transport_frame_id != ILLEGAL_TRANSPORT_FRAME_ID ) session->id_mapping.DisassociateTransportFrameId(last_transport_frame_id); // if a reply pdu has been buffered, destroy it if ( reply_snmp_pdu != NULL ) { delete &reply_snmp_pdu->GetVarbindList () ; delete reply_snmp_pdu; } // deletes the message ptr delete message; }