msgqueue.cpp
上传用户:ets1996
上传日期:2014-09-30
资源大小:353k
文件大小:17k
源码类别:

SNMP编程

开发平台:

Visual C++

  1. /*_############################################################################
  2.   _## 
  3.   _##  msgqueue.cpp  
  4.   _##
  5.   _##  SNMP++v3.2.22
  6.   _##  -----------------------------------------------
  7.   _##  Copyright (c) 2001-2007 Jochen Katz, Frank Fock
  8.   _##
  9.   _##  This software is based on SNMP++2.6 from Hewlett Packard:
  10.   _##  
  11.   _##    Copyright (c) 1996
  12.   _##    Hewlett-Packard Company
  13.   _##  
  14.   _##  ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS.
  15.   _##  Permission to use, copy, modify, distribute and/or sell this software 
  16.   _##  and/or its documentation is hereby granted without fee. User agrees 
  17.   _##  to display the above copyright notice and this license notice in all 
  18.   _##  copies of the software and any documentation of the software. User 
  19.   _##  agrees to assume all liability for the use of the software; 
  20.   _##  Hewlett-Packard and Jochen Katz make no representations about the 
  21.   _##  suitability of this software for any purpose. It is provided 
  22.   _##  "AS-IS" without warranty of any kind, either express or implied. User 
  23.   _##  hereby grants a royalty-free license to any and all derivatives based
  24.   _##  upon this software code base. 
  25.   _##  
  26.   _##  Stuttgart, Germany, Wed May  2 23:22:30 CEST 2007 
  27.   _##  
  28.   _##########################################################################*/
  29. /*===================================================================
  30.   Copyright (c) 1999
  31.   Hewlett-Packard Company
  32.   ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS.
  33.   Permission to use, copy, modify, distribute and/or sell this software
  34.   and/or its documentation is hereby granted without fee. User agrees
  35.   to display the above copyright notice and this license notice in all
  36.   copies of the software and any documentation of the software. User
  37.   agrees to assume all liability for the use of the software; Hewlett-Packard
  38.   makes no representations about the suitability of this software for any
  39.   purpose. It is provided "AS-IS" without warranty of any kind,either express
  40.   or implied. User hereby grants a royalty-free license to any and all
  41.   derivatives based upon this software code base.
  42.       M S G Q U E U E . C P P
  43.       MSG QUEUE CLASS DECLARATION
  44.       Description: HPUX version of Snmp class
  45.       Language:         ANSI C++
  46.       Author:           Peter E Mellquist
  47. =====================================================================*/
  48. char msgqueue_version[]="#(@) SNMP++ $Id: msgqueue.cpp 267 2006-10-02 13:49:26Z katz $";
  49. //-----[ includes ]----------------------------------------------------
  50. //----[ snmp++ includes ]----------------------------------------------
  51. #include "snmp_pp/msgqueue.h" // queue for holding outstanding messages
  52. #include "snmp_pp/snmpmsg.h"
  53. #include "snmp_pp/eventlistholder.h"
  54. #include "snmp_pp/log.h"
  55. #include "snmp_pp/vb.h"
  56. #ifdef SNMP_PP_NAMESPACE
  57. namespace Snmp_pp {
  58. #endif
  59. #define SNMP_PORT 161
  60. //--------[ externs ]---------------------------------------------------
  61. extern int send_snmp_request(SnmpSocket sock, unsigned char *send_buf,
  62.       size_t send_len, Address &address);
  63. extern int receive_snmp_response(SnmpSocket sock, Snmp &snmp_session,
  64.                                  Pdu &pdu, UdpAddress &fromaddress,
  65.  OctetStr &engine_id, bool process_msg = true);
  66. //----[ CSNMPMessage class ]-------------------------------------------
  67. CSNMPMessage::CSNMPMessage(unsigned long id,
  68.    Snmp * snmp,
  69.    SnmpSocket socket,
  70.    const SnmpTarget &target,
  71.    Pdu &pdu,
  72.    unsigned char * rawPdu,
  73.    size_t rawPduLen,
  74.    const Address & address,
  75.    snmp_callback callBack,
  76.    void * callData):
  77.   m_uniqueId(id), m_snmp(snmp), m_socket(socket), m_pdu(pdu),
  78.   m_rawPduLen(rawPduLen), m_callBack(callBack), m_callData(callData),
  79.   m_reason(0), m_received(0)
  80. {
  81.   // reset pdu mvs
  82.   m_pdu.set_error_index(0);
  83.   m_pdu.set_error_status(0);
  84.   m_pdu.set_request_id(m_uniqueId);
  85.   m_rawPdu = new unsigned char [rawPduLen];
  86.   memcpy(m_rawPdu, rawPdu, rawPduLen);
  87.   m_address = (Address *)address.clone();
  88.   m_target = target.clone();
  89.   SetSendTime();
  90. }
  91. CSNMPMessage::~CSNMPMessage()
  92. {
  93.   delete [] m_rawPdu;
  94.   delete m_address;
  95.   delete m_target;
  96. }
  97. void CSNMPMessage::SetSendTime()
  98. {
  99.   m_sendTime.refresh();
  100.   // Kludge: When this was first designed the units were millisecs
  101.   // However, later on the units for the target class were changed
  102.   // to hundreths of secs.  Multiply the hundreths of secs by 10
  103.   // to create the millisecs which the rest of the objects use.
  104.   // 11-Dec-95 TM
  105.   m_sendTime += (m_target->get_timeout() * 10);
  106. }
  107. int CSNMPMessage::SetPdu(const int reason, const Pdu &pdu,
  108.  const UdpAddress &fromaddress)
  109. {
  110.   if (Pdu::match_type(m_pdu.get_type(), pdu.get_type()) == false)
  111.   {
  112.     LOG_BEGIN(INFO_LOG | 1);
  113.     LOG("MsgQueue: Response pdu type does not match, pdu is ignored: (id) (type1) (type2)");
  114.     LOG(m_uniqueId);
  115.     LOG(m_pdu.get_type());
  116.     LOG(pdu.get_type());
  117.     LOG_END;
  118.     return -1;
  119.   }
  120.   unsigned short orig_type = m_pdu.get_type();
  121.   if (m_received)
  122.   {
  123.     LOG_BEGIN(WARNING_LOG | 1);
  124.     LOG("MsgQueue: Message is already marked as received (id) (reason) (new reason)");
  125.     LOG(m_uniqueId);
  126.     LOG(reason);
  127.     LOG(m_reason);
  128.     LOG_END;
  129.     // TODO: better check based on error codes
  130.     if (reason || !m_reason)
  131.     {
  132.       LOG_BEGIN(WARNING_LOG | 1);
  133.       LOG("MsgQueue: ignoring the second pdu");
  134.       LOG_END;
  135.       return 0;
  136.     }
  137.   }
  138.   m_received = 1;
  139.   m_pdu = pdu;
  140.   m_reason = reason;
  141.   if ((orig_type == sNMP_PDU_INFORM) &&
  142.       (m_pdu.get_type() == sNMP_PDU_RESPONSE))
  143.   {
  144.     // remove the first two vbs of the pdu if sysUpTime and notify_id
  145.     if (m_pdu.get_vb_count() < 2)
  146.       return 0;
  147.     const Vb &vb1 = m_pdu.get_vb(0);
  148.     if (vb1.get_syntax() != sNMP_SYNTAX_TIMETICKS)   return 0;
  149.     if (vb1.get_oid()    != SNMP_MSG_OID_SYSUPTIME)  return 0;
  150.     const Vb &vb2 = m_pdu.get_vb(1);
  151.     if (vb2.get_syntax() != sNMP_SYNTAX_OID)         return 0;
  152.     if (vb2.get_oid()    != SNMP_MSG_OID_TRAPID)     return 0;
  153.     TimeTicks timeticks;
  154.     Oid oid;
  155.     vb1.get_value(timeticks);
  156.     m_pdu.set_notify_timestamp(timeticks);
  157.     vb2.get_value(oid);
  158.     m_pdu.set_notify_id(oid);
  159.     m_pdu.delete_vb(1);
  160.     m_pdu.delete_vb(0);
  161.   }
  162.   return 0;
  163. }
  164. int CSNMPMessage::ResendMessage()
  165. {
  166.   if (m_received)
  167.   {
  168.     // Don't bother to resend if we already have the response
  169.     SetSendTime();
  170.     return SNMP_CLASS_SUCCESS;
  171.   }
  172.   if (m_target->get_retry() <= 0)
  173.   {
  174.     // This message has timed out
  175.     Callback(SNMP_CLASS_TIMEOUT);   // perform callback with the error
  176.     return SNMP_CLASS_TIMEOUT;
  177.   }
  178.   m_target->set_retry(m_target->get_retry() - 1);
  179.   SetSendTime();
  180.   int status = send_snmp_request(m_socket, m_rawPdu, m_rawPduLen, *m_address);
  181.   if (status != 0)
  182.     return SNMP_CLASS_TL_FAILED;
  183.   return SNMP_CLASS_SUCCESS;
  184. }
  185. int CSNMPMessage::Callback(const int reason)
  186. {
  187.   snmp_callback   tmp_callBack;
  188.   if (m_callBack) {
  189.     // prevent callbacks from using this message
  190.     tmp_callBack = m_callBack;
  191.     m_callBack = NULL;
  192.     tmp_callBack(reason, m_snmp, m_pdu, *m_target, m_callData);
  193.     return 0;
  194.   }
  195.   return 1;
  196. }
  197. //----[ CSNMPMessageQueueElt class ]--------------------------------------
  198. CSNMPMessageQueue::CSNMPMessageQueueElt::CSNMPMessageQueueElt(
  199.                                            CSNMPMessage *message,
  200.    CSNMPMessageQueueElt *next,
  201.    CSNMPMessageQueueElt *previous):
  202.   m_message(message), m_Next(next), m_previous(previous)
  203. {
  204.   /* Finish insertion into doubly linked list */
  205.   if (m_Next)     m_Next->m_previous = this;
  206.   if (m_previous) m_previous->m_Next = this;
  207. }
  208. CSNMPMessageQueue::CSNMPMessageQueueElt::~CSNMPMessageQueueElt()
  209. {
  210.   /* Do deletion form doubly linked list */
  211.   if (m_Next)     m_Next->m_previous = m_previous;
  212.   if (m_previous) m_previous->m_Next = m_Next;
  213.   if (m_message)  delete m_message;
  214. }
  215. CSNMPMessage *CSNMPMessageQueue::CSNMPMessageQueueElt::TestId(const unsigned long uniqueId)
  216. {
  217.   if (m_message && (m_message->GetId() == uniqueId))
  218.     return m_message;
  219.   return 0;
  220. }
  221. //----[ CSNMPMessageQueue class ]--------------------------------------
  222. CSNMPMessageQueue::CSNMPMessageQueue(EventListHolder *holder, Snmp *session)
  223.   : m_head(0, 0, 0), m_msgCount(0), m_idStack(0), m_stackTop(0),
  224.     m_stackSize(0), my_holder(holder), m_snmpSession(session)
  225. {
  226.   PushId(0);
  227. }
  228. CSNMPMessageQueue::~CSNMPMessageQueue()
  229. {
  230.   CSNMPMessageQueueElt *leftOver;
  231.   lock();
  232.     /*--------------------------------------------------------*/
  233.     /* walk the list deleting any elements still on the queue */
  234.     /*--------------------------------------------------------*/
  235.   while ((leftOver = m_head.GetNext()))
  236.     delete leftOver;
  237.   if (m_idStack)
  238.     delete [] m_idStack;
  239.   unlock();
  240. }
  241. CSNMPMessage * CSNMPMessageQueue::AddEntry(unsigned long id,
  242.    Snmp * snmp,
  243.    SnmpSocket socket,
  244.    const SnmpTarget &target,
  245.    Pdu &pdu,
  246.    unsigned char * rawPdu,
  247.    size_t rawPduLen,
  248.    const Address & address,
  249.    snmp_callback callBack,
  250.    void * callData)
  251. {
  252.   if (snmp != m_snmpSession)
  253.   {
  254.     LOG_BEGIN(WARNING_LOG | 1);
  255.     LOG("MsgQueue: WARNING: Adding message for other Snmp object.");
  256.     LOG_END;
  257.   }
  258.   CSNMPMessage *newMsg = new CSNMPMessage(id, snmp, socket, target, pdu,
  259.   rawPdu, rawPduLen, address,
  260.   callBack, callData);
  261.   lock();
  262.     /*---------------------------------------------------------*/
  263.     /* Insert entry at head of list, done automagically by the */
  264.     /* constructor function, so don't use the return value.    */
  265.     /*---------------------------------------------------------*/
  266.   (void) new CSNMPMessageQueueElt(newMsg, m_head.GetNext(), &m_head);
  267.   ++m_msgCount;
  268.   unlock();
  269.   return newMsg;
  270. }
  271. CSNMPMessage *CSNMPMessageQueue::GetEntry(const unsigned long uniqueId)
  272. {
  273.   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
  274.   CSNMPMessage *returnVal = NULL;
  275.   while (msgEltPtr){
  276.     if ((returnVal = msgEltPtr->TestId(uniqueId)))
  277.       return returnVal;
  278.     msgEltPtr = msgEltPtr->GetNext();
  279.   }
  280.   return 0;
  281. }
  282. int CSNMPMessageQueue::DeleteEntry(const unsigned long uniqueId)
  283. {
  284.   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
  285.   while (msgEltPtr){
  286.     if (msgEltPtr->TestId(uniqueId)) {
  287.       delete msgEltPtr;
  288.       m_msgCount--;
  289.       return SNMP_CLASS_SUCCESS;
  290.     }
  291.     msgEltPtr = msgEltPtr->GetNext();
  292.   }
  293.   return SNMP_CLASS_INVALID_REQID;
  294. }
  295. void CSNMPMessageQueue::DeleteSocketEntry(const SnmpSocket socket)
  296. REENTRANT({
  297.   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
  298.   CSNMPMessageQueueElt *tmp_msgEltPtr;
  299.   CSNMPMessage *msg = NULL;
  300.   while (msgEltPtr){
  301.     msg = msgEltPtr->GetMessage();
  302.     if (socket == msg->GetSocket()) {
  303.       // Make a callback with an error
  304.       (void) msg->Callback(SNMP_CLASS_SESSION_DESTROYED);
  305.       tmp_msgEltPtr = msgEltPtr;
  306.       msgEltPtr = tmp_msgEltPtr->GetNext();
  307.       // delete the entry
  308.       delete tmp_msgEltPtr;
  309.     }
  310.     else
  311.       msgEltPtr = msgEltPtr->GetNext();
  312.   }
  313. })
  314. CSNMPMessage * CSNMPMessageQueue::GetNextTimeoutEntry()
  315. {
  316.   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
  317.   msec bestTime;
  318.   msec sendTime(bestTime);
  319.   CSNMPMessage *msg;
  320.   CSNMPMessage *bestmsg = NULL;
  321.   if (msgEltPtr) {
  322.     bestmsg = msgEltPtr->GetMessage();
  323.     bestmsg->GetSendTime(bestTime);
  324.   }
  325.   // This would be much simpler if the queue was an ordered list!
  326.   while (msgEltPtr){
  327.     msg = msgEltPtr->GetMessage();
  328.     msg->GetSendTime(sendTime);
  329.     if (bestTime  > sendTime) {
  330.       bestTime = sendTime;
  331.       bestmsg = msg;
  332.     }
  333.     msgEltPtr = msgEltPtr->GetNext();
  334.   }
  335.   return bestmsg;
  336. }
  337. int CSNMPMessageQueue::GetNextTimeout(msec &sendTime)
  338. {
  339.   CSNMPMessage *msg = GetNextTimeoutEntry();
  340.   if (!msg)  return 1;    // nothing in the queue...
  341.   msg->GetSendTime(sendTime);
  342.   return 0;
  343. }
  344. void CSNMPMessageQueue::GetFdSets(int &maxfds, fd_set &readfds,
  345.   fd_set &, fd_set &) REENTRANT ({
  346.   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
  347.   SnmpSocket sock;
  348.   while (msgEltPtr){
  349.     sock = msgEltPtr->GetMessage()->GetSocket();
  350.     FD_SET(sock, &readfds);
  351.     if (maxfds < sock+1)
  352.       maxfds = SAFE_INT_CAST(sock+1);
  353.     msgEltPtr = msgEltPtr->GetNext();
  354.   }
  355. })
  356. void CSNMPMessageQueue::PushId(const unsigned long id) REENTRANT ({
  357.   unsigned long *newStack = 0;
  358.   // check whether stack is too small or much too big
  359.   if ((!m_idStack) ||
  360.       (m_stackSize < m_stackTop+1) || (m_stackSize - m_stackTop > 50)) {
  361.     newStack = new unsigned long [m_stackTop+10];
  362.     m_stackSize = m_stackTop+10;
  363.   }
  364.   if (newStack) {
  365.     newStack[m_stackTop] = id;
  366.     if (m_idStack) {
  367.       for (int i=0; i< m_stackTop; i++)
  368. newStack[i] = m_idStack[i];
  369.       delete [] m_idStack;
  370.     }
  371.     m_idStack = newStack;
  372.     m_stackTop++;
  373.   }
  374.   else
  375.     m_idStack[m_stackTop++] = id;
  376. })
  377. unsigned long CSNMPMessageQueue::PeekId() REENTRANT ({
  378.   return m_idStack[m_stackTop - 1];
  379. })
  380. int CSNMPMessageQueue::HandleEvents(const int maxfds,
  381.     const fd_set &readfds,
  382.     const fd_set &,
  383.     const fd_set &)
  384. {
  385.   CSNMPMessage *msg;
  386.   UdpAddress fromaddress;
  387.   Pdu tmppdu;
  388.   unsigned long temp_req_id;
  389.   int status;
  390.   int recv_status;
  391.   fd_set snmp_readfds, snmp_writefds, snmp_errfds;
  392.   int tmp_maxfds = maxfds;
  393.   // Only read from our own fds
  394.   FD_ZERO(&snmp_readfds);
  395.   FD_ZERO(&snmp_writefds);
  396.   FD_ZERO(&snmp_errfds);
  397.   GetFdSets(tmp_maxfds, snmp_readfds, snmp_writefds, snmp_errfds);
  398.   for (int fd = 0; fd < maxfds; fd++)
  399.   {
  400.     if ((FD_ISSET(fd, &snmp_readfds)) &&
  401. (FD_ISSET(fd, &readfds)))
  402.     {
  403.       OctetStr engine_id;
  404.       tmppdu.set_request_id(0);
  405.       // get the response and put it into a Pdu
  406.       recv_status = receive_snmp_response(fd, *m_snmpSession,
  407.                                           tmppdu, fromaddress, engine_id);
  408.       lock();
  409.       // find the corresponding msg in the message queue
  410.       temp_req_id = tmppdu.get_request_id();
  411.       msg = GetEntry(temp_req_id);
  412.       if (!msg) {
  413.      unlock();
  414.      // the sent message is gone! probably was canceled, ignore it
  415.      continue;
  416.       }
  417.       if (tmppdu.get_request_id()) {
  418.      // we correctly received the pdu
  419.      // save it back into the message
  420.      status = msg->SetPdu(recv_status, tmppdu, fromaddress);
  421.              if (status != 0)
  422.              {
  423.                // received pdu does not match
  424.                // @todo if version is SNMPv3 we must return a report
  425.                //       unknown pdu handler!
  426.                unlock();
  427.                continue;
  428.              }
  429. #ifdef _SNMPv3
  430.      if (engine_id.len() > 0)
  431.      {
  432.  SnmpTarget *target = msg->GetTarget();
  433.  if ((target->get_type() == SnmpTarget::type_utarget) &&
  434.      (target->get_version() == version3))
  435.  {
  436.    UdpAddress addr = target->get_address();
  437.    LOG_BEGIN(DEBUG_LOG | 14);
  438.    LOG("MsgQueue: Adding engine id to table (addr) (id)");
  439.    LOG(addr.get_printable());
  440.    LOG(engine_id.get_printable());
  441.    LOG_END;
  442.    v3MP::I->add_to_engine_id_table(engine_id,
  443.        (char*)addr.IpAddress::get_printable(),
  444.        addr.get_port());
  445.  }
  446.      }
  447. #endif
  448.      // Do the callback
  449.              unlock();
  450.      status = msg->Callback(SNMP_CLASS_ASYNC_RESPONSE);
  451.              lock();
  452.      if (!status) {
  453.        // this is an asynch response and the callback is done.
  454.        // no need to keep this message around;
  455.        // Dequeue the message
  456.        DeleteEntry(temp_req_id);
  457.      }
  458.       }
  459.       unlock();
  460.     } // if socket has data
  461.   } // for all sockets
  462.   return SNMP_CLASS_SUCCESS;
  463. }
  464. int CSNMPMessageQueue::DoRetries(const msec &now)
  465. {
  466.   CSNMPMessage *msg;
  467.   msec sendTime(0, 0);
  468.   int status = SNMP_CLASS_SUCCESS;
  469.   lock();
  470.   while ((msg = GetNextTimeoutEntry())) {
  471.     msg->GetSendTime(sendTime);
  472.     if (sendTime <= now)
  473.     {
  474.       // send out the message again
  475.       unlock();
  476.       status = msg->ResendMessage();
  477.       lock();
  478.       if (status != 0)
  479.       {
  480. if (status == SNMP_CLASS_TIMEOUT)
  481. {
  482.   unsigned long req_id = msg->GetId();
  483.   // Dequeue the message
  484.   DeleteEntry(req_id);
  485. #ifdef _SNMPv3
  486.   // delete entry in cache
  487.   if (v3MP::I)
  488.     v3MP::I->delete_from_cache(req_id);
  489.           LOG_BEGIN(INFO_LOG | 6);
  490.           LOG("MsgQueue: Message timed out, removed id from v3MP cache (rid)");
  491.           LOG(req_id);
  492.           LOG_END;
  493. #endif
  494. }
  495. else {
  496.   // Some other send error, should we dequeue the message?
  497.   // do we really want to return without processing the rest?
  498.           unlock();
  499.   return status;
  500. }
  501.       }
  502.     }
  503.     else {
  504.       break;  // the next timeout is still in the future...so we are done
  505.     }
  506.   }
  507.   unlock();
  508.   return status;
  509. }
  510. int CSNMPMessageQueue::Done()
  511. {
  512.   unsigned long id;
  513.   if ((id = PeekId()))
  514.   {
  515.     // we were looking for a req_id.  Did we find it?
  516.     lock();
  517.     CSNMPMessage *msg = GetEntry(id);
  518.     unlock();
  519.     if (msg)
  520.       return msg->GetReceived();
  521.     else
  522.       return 1; // the message is not in the queue...must have timed out
  523.   }
  524.   return 0;
  525. }
  526. int CSNMPMessageQueue::Done(unsigned long id) REENTRANT ({
  527.   // FF: This is much more efficient than the above
  528.   CSNMPMessage *msg = GetEntry(id);
  529.   if (!msg) return 1; // the message is not in the queue...must have timed out
  530.   if (msg->GetReceived())
  531.       return 1;
  532.   return 0;
  533. })
  534. #ifdef SNMP_PP_NAMESPACE
  535. }; // end of namespace Snmp_pp
  536. #endif