1 /*_############################################################################
6 _## -----------------------------------------------
7 _## Copyright (c) 2001-2010 Jochen Katz, Frank Fock
9 _## This software is based on SNMP++2.6 from Hewlett Packard:
11 _## Copyright (c) 1996
12 _## Hewlett-Packard Company
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.
26 _## Stuttgart, Germany, Thu Sep 2 00:07:47 CEST 2010
28 _##########################################################################*/
29 /*===================================================================
32 Hewlett-Packard Company
34 ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS.
35 Permission to use, copy, modify, distribute and/or sell this software
36 and/or its documentation is hereby granted without fee. User agrees
37 to display the above copyright notice and this license notice in all
38 copies of the software and any documentation of the software. User
39 agrees to assume all liability for the use of the software; Hewlett-Packard
40 makes no representations about the suitability of this software for any
41 purpose. It is provided "AS-IS" without warranty of any kind,either express
42 or implied. User hereby grants a royalty-free license to any and all
43 derivatives based upon this software code base.
45 M S G Q U E U E . C P P
47 MSG QUEUE CLASS DECLARATION
49 Author: Peter E Mellquist
51 =====================================================================*/
52 char msgqueue_version[]="#(@) SNMP++ $Id: msgqueue.cpp 1826 2010-08-29 20:19:59Z katz $";
54 //-----[ includes ]----------------------------------------------------
56 //----[ snmp++ includes ]----------------------------------------------
58 #include "snmp_pp/msgqueue.h" // queue for holding outstanding messages
59 #include "snmp_pp/snmpmsg.h"
60 #include "snmp_pp/eventlistholder.h"
61 #include "snmp_pp/log.h"
62 #include "snmp_pp/vb.h"
64 #ifdef SNMP_PP_NAMESPACE
71 //--------[ externs ]---------------------------------------------------
72 extern int send_snmp_request(SnmpSocket sock, unsigned char *send_buf,
73 size_t send_len, Address &address);
74 extern int receive_snmp_response(SnmpSocket sock, Snmp &snmp_session,
75 Pdu &pdu, UdpAddress &fromaddress,
76 OctetStr &engine_id, bool process_msg = true);
78 //----[ CSNMPMessage class ]-------------------------------------------
80 CSNMPMessage::CSNMPMessage(unsigned long id,
83 const SnmpTarget &target,
85 unsigned char * rawPdu,
87 const Address & address,
88 snmp_callback callBack,
90 m_uniqueId(id), m_snmp(snmp), m_socket(socket), m_pdu(pdu),
91 m_rawPduLen(rawPduLen), m_callBack(callBack), m_callData(callData),
92 m_reason(0), m_received(0)
95 m_pdu.set_error_index(0);
96 m_pdu.set_error_status(0);
97 m_pdu.set_request_id(m_uniqueId);
99 m_rawPdu = new unsigned char [rawPduLen];
100 memcpy(m_rawPdu, rawPdu, rawPduLen);
101 m_address = (Address *)address.clone();
102 m_target = target.clone();
107 CSNMPMessage::~CSNMPMessage()
114 void CSNMPMessage::SetSendTime()
116 m_sendTime.refresh();
118 // Kludge: When this was first designed the units were millisecs
119 // However, later on the units for the target class were changed
120 // to hundreths of secs. Multiply the hundreths of secs by 10
121 // to create the millisecs which the rest of the objects use.
123 m_sendTime += (m_target->get_timeout() * 10);
126 int CSNMPMessage::SetPdu(const int reason, const Pdu &pdu,
127 const UdpAddress &fromaddress)
129 if (Pdu::match_type(m_pdu.get_type(), pdu.get_type()) == false)
131 LOG_BEGIN(INFO_LOG | 1);
132 LOG("MsgQueue: Response pdu type does not match, pdu is ignored: (id) (type1) (type2)");
134 LOG(m_pdu.get_type());
141 unsigned short orig_type = m_pdu.get_type();
144 LOG_BEGIN(WARNING_LOG | 1);
145 LOG("MsgQueue: Message is already marked as received (id) (reason) (new reason)");
151 // TODO: better check based on error codes
152 if (reason || !m_reason)
154 LOG_BEGIN(WARNING_LOG | 1);
155 LOG("MsgQueue: ignoring the second pdu");
165 LOG_BEGIN(DEBUG_LOG | 10);
166 LOG("MsgQueue: Response received (req id) (status) (msg id)");
167 LOG(pdu.get_request_id());
170 LOG(pdu.get_message_id());
174 if ((orig_type == sNMP_PDU_INFORM) &&
175 (m_pdu.get_type() == sNMP_PDU_RESPONSE))
177 // remove the first two vbs of the pdu if sysUpTime and notify_id
178 if (m_pdu.get_vb_count() < 2)
181 const Vb &vb1 = m_pdu.get_vb(0);
182 if (vb1.get_syntax() != sNMP_SYNTAX_TIMETICKS) return 0;
183 if (vb1.get_oid() != SNMP_MSG_OID_SYSUPTIME) return 0;
185 const Vb &vb2 = m_pdu.get_vb(1);
186 if (vb2.get_syntax() != sNMP_SYNTAX_OID) return 0;
187 if (vb2.get_oid() != SNMP_MSG_OID_TRAPID) return 0;
192 vb1.get_value(timeticks);
193 m_pdu.set_notify_timestamp(timeticks);
196 m_pdu.set_notify_id(oid);
204 int CSNMPMessage::ResendMessage()
208 // Don't bother to resend if we already have the response
210 return SNMP_CLASS_SUCCESS;
213 LOG_BEGIN(DEBUG_LOG | 10);
214 LOG("MsgQueue: Message (msg id) (req id) (info)");
216 LOG(m_pdu.get_message_id());
218 LOG(m_pdu.get_request_id());
219 LOG((m_target->get_retry() <= 0) ? "TIMEOUT" : "RESEND");
222 if (m_target->get_retry() <= 0)
224 // This message has timed out
225 Callback(SNMP_CLASS_TIMEOUT); // perform callback with the error
227 return SNMP_CLASS_TIMEOUT;
230 m_target->set_retry(m_target->get_retry() - 1);
232 int status = send_snmp_request(m_socket, m_rawPdu, m_rawPduLen, *m_address);
234 return SNMP_CLASS_TL_FAILED;
236 return SNMP_CLASS_SUCCESS;
239 int CSNMPMessage::Callback(const int reason)
241 snmp_callback tmp_callBack;
243 // prevent callbacks from using this message
244 tmp_callBack = m_callBack;
247 tmp_callBack(reason, m_snmp, m_pdu, *m_target, m_callData);
253 //----[ CSNMPMessageQueueElt class ]--------------------------------------
255 CSNMPMessageQueue::CSNMPMessageQueueElt::CSNMPMessageQueueElt(
256 CSNMPMessage *message,
257 CSNMPMessageQueueElt *next,
258 CSNMPMessageQueueElt *previous):
259 m_message(message), m_Next(next), m_previous(previous)
261 /* Finish insertion into doubly linked list */
262 if (m_Next) m_Next->m_previous = this;
263 if (m_previous) m_previous->m_Next = this;
266 CSNMPMessageQueue::CSNMPMessageQueueElt::~CSNMPMessageQueueElt()
268 /* Do deletion form doubly linked list */
269 if (m_Next) m_Next->m_previous = m_previous;
270 if (m_previous) m_previous->m_Next = m_Next;
271 if (m_message) delete m_message;
274 CSNMPMessage *CSNMPMessageQueue::CSNMPMessageQueueElt::TestId(const unsigned long uniqueId)
276 if (m_message && (m_message->GetId() == uniqueId))
283 //----[ CSNMPMessageQueue class ]--------------------------------------
285 CSNMPMessageQueue::CSNMPMessageQueue(EventListHolder *holder, Snmp *session)
286 : m_head(0, 0, 0), m_msgCount(0), my_holder(holder), m_snmpSession(session)
290 CSNMPMessageQueue::~CSNMPMessageQueue()
292 CSNMPMessageQueueElt *leftOver;
294 /*--------------------------------------------------------*/
295 /* walk the list deleting any elements still on the queue */
296 /*--------------------------------------------------------*/
297 while ((leftOver = m_head.GetNext()))
303 CSNMPMessage * CSNMPMessageQueue::AddEntry(unsigned long id,
306 const SnmpTarget &target,
308 unsigned char * rawPdu,
310 const Address & address,
311 snmp_callback callBack,
314 if (snmp != m_snmpSession)
316 LOG_BEGIN(ERROR_LOG | 1);
317 LOG("MsgQueue: Adding message for other Snmp object.");
321 CSNMPMessage *newMsg = new CSNMPMessage(id, snmp, socket, target, pdu,
322 rawPdu, rawPduLen, address,
326 /*---------------------------------------------------------*/
327 /* Insert entry at head of list, done automagically by the */
328 /* constructor function, so don't use the return value. */
329 /*---------------------------------------------------------*/
330 (void) new CSNMPMessageQueueElt(newMsg, m_head.GetNext(), &m_head);
333 LOG_BEGIN(DEBUG_LOG | 10);
334 LOG("MsgQueue: Adding entry (req id) (count)");
344 CSNMPMessage *CSNMPMessageQueue::GetEntry(const unsigned long uniqueId)
346 CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
347 CSNMPMessage *returnVal = NULL;
350 if ((returnVal = msgEltPtr->TestId(uniqueId)))
352 msgEltPtr = msgEltPtr->GetNext();
357 int CSNMPMessageQueue::DeleteEntry(const unsigned long uniqueId)
359 CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
362 if (msgEltPtr->TestId(uniqueId)) {
365 LOG_BEGIN(DEBUG_LOG | 10);
366 LOG("MsgQueue: Removed entry (req id)");
369 return SNMP_CLASS_SUCCESS;
371 msgEltPtr = msgEltPtr->GetNext();
373 return SNMP_CLASS_INVALID_REQID;
376 void CSNMPMessageQueue::DeleteSocketEntry(const SnmpSocket socket)
378 CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
379 CSNMPMessageQueueElt *tmp_msgEltPtr;
380 CSNMPMessage *msg = NULL;
383 msg = msgEltPtr->GetMessage();
384 if (socket == msg->GetSocket()) {
385 // Make a callback with an error
386 (void) msg->Callback(SNMP_CLASS_SESSION_DESTROYED);
387 tmp_msgEltPtr = msgEltPtr;
388 msgEltPtr = tmp_msgEltPtr->GetNext();
390 delete tmp_msgEltPtr;
393 msgEltPtr = msgEltPtr->GetNext();
397 CSNMPMessage * CSNMPMessageQueue::GetNextTimeoutEntry()
399 CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
401 msec sendTime(bestTime);
403 CSNMPMessage *bestmsg = NULL;
406 bestmsg = msgEltPtr->GetMessage();
407 bestmsg->GetSendTime(bestTime);
410 // This would be much simpler if the queue was an ordered list!
412 msg = msgEltPtr->GetMessage();
413 msg->GetSendTime(sendTime);
414 if (bestTime > sendTime) {
418 msgEltPtr = msgEltPtr->GetNext();
423 int CSNMPMessageQueue::GetNextTimeout(msec &sendTime)
425 CSNMPMessage *msg = GetNextTimeoutEntry();
427 if (!msg) return 1; // nothing in the queue...
429 msg->GetSendTime(sendTime);
433 #ifdef HAVE_POLL_SYSCALL
435 int CSNMPMessageQueue::GetFdCount()
437 SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
439 CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
441 if (!msgEltPtr) return 0;
444 // we have at least one message in the queue and Snmp class
445 // can only have one socket, so
448 // we can have max 2 sockets
449 SnmpSocket firstSocket = msgEltPtr->GetMessage()->GetSocket();
451 msgEltPtr = msgEltPtr->GetNext();
455 if (msgEltPtr->GetMessage()->GetSocket() != firstSocket)
457 msgEltPtr = msgEltPtr->GetNext();
463 bool CSNMPMessageQueue::GetFdArray(struct pollfd *readfds, int &remaining)
465 SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
467 CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
468 if (!msgEltPtr) return true;
470 if (remaining <= 0) return false;
472 SnmpSocket firstSocket = msgEltPtr->GetMessage()->GetSocket();
473 readfds[0].fd = firstSocket;
474 readfds[0].events = POLLIN;
478 // we have at least one message in the queue and Snmp class
479 // can only have one socket, so we are done
482 // we can have max 2 sockets
483 msgEltPtr = msgEltPtr->GetNext();
487 if (msgEltPtr->GetMessage()->GetSocket() != firstSocket)
489 if (remaining <= 0) return false;
490 readfds[1].fd = msgEltPtr->GetMessage()->GetSocket();
491 readfds[1].events = POLLIN;
496 msgEltPtr = msgEltPtr->GetNext();
502 int CSNMPMessageQueue::HandleEvents(const struct pollfd *readfds,
505 for (int i=0; i < fds ; i++)
507 if (readfds[i].revents & POLLIN)
510 UdpAddress fromaddress;
512 unsigned long temp_req_id;
517 tmppdu.set_request_id(0);
519 // get the response and put it into a Pdu
520 recv_status = receive_snmp_response(readfds[i].fd, *m_snmpSession,
521 tmppdu, fromaddress, engine_id);
524 // find the corresponding msg in the message queue
525 temp_req_id = tmppdu.get_request_id();
526 msg = GetEntry(temp_req_id);
530 LOG_BEGIN(INFO_LOG | 7);
531 LOG("MsgQueue: Ignore received message without outstanding request (req id)");
532 LOG(tmppdu.get_request_id());
534 // the sent message is gone! probably was canceled, ignore it
538 if (tmppdu.get_request_id())
540 // we correctly received the pdu
542 // save it back into the message
543 status = msg->SetPdu(recv_status, tmppdu, fromaddress);
547 // received pdu does not match
548 // @todo if version is SNMPv3 we must return a report
549 // unknown pdu handler!
555 if (engine_id.len() > 0)
557 SnmpTarget *target = msg->GetTarget();
558 if ((target->get_type() == SnmpTarget::type_utarget) &&
559 (target->get_version() == version3))
561 UdpAddress addr = target->get_address();
563 LOG_BEGIN(DEBUG_LOG | 14);
564 LOG("MsgQueue: Adding engine id to table (addr) (id)");
565 LOG(addr.get_printable());
566 LOG(engine_id.get_printable());
569 v3MP::I->add_to_engine_id_table(engine_id,
570 (char*)addr.IpAddress::get_printable(),
578 status = msg->Callback(SNMP_CLASS_ASYNC_RESPONSE);
583 // this is an asynch response and the callback is done.
584 // no need to keep this message around;
585 // Dequeue the message
586 DeleteEntry(temp_req_id);
592 return SNMP_CLASS_SUCCESS;
595 #else // HAVE_POLL_SYSCALL
597 void CSNMPMessageQueue::GetFdSets(int &maxfds, fd_set &readfds,
600 SnmpSynchronize _synchronize(*this); // REENTRANT
601 CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
605 sock = msgEltPtr->GetMessage()->GetSocket();
606 FD_SET(sock, &readfds);
607 if (maxfds < SAFE_INT_CAST(sock+1))
608 maxfds = SAFE_INT_CAST(sock+1);
609 msgEltPtr = msgEltPtr->GetNext();
613 int CSNMPMessageQueue::HandleEvents(const int maxfds,
614 const fd_set &readfds,
619 UdpAddress fromaddress;
621 unsigned long temp_req_id;
624 fd_set snmp_readfds, snmp_writefds, snmp_errfds;
625 int tmp_maxfds = maxfds;
627 // Only read from our own fds
628 FD_ZERO(&snmp_readfds);
629 FD_ZERO(&snmp_writefds);
630 FD_ZERO(&snmp_errfds);
631 GetFdSets(tmp_maxfds, snmp_readfds, snmp_writefds, snmp_errfds);
633 for (int fd = 0; fd < maxfds; fd++)
635 if ((FD_ISSET(fd, &snmp_readfds)) &&
636 (FD_ISSET(fd, (fd_set*)&readfds)))
640 tmppdu.set_request_id(0);
642 // get the response and put it into a Pdu
643 recv_status = receive_snmp_response(fd, *m_snmpSession,
644 tmppdu, fromaddress, engine_id);
647 // find the corresponding msg in the message queue
648 temp_req_id = tmppdu.get_request_id();
649 msg = GetEntry(temp_req_id);
652 LOG_BEGIN(INFO_LOG | 7);
653 LOG("MsgQueue: Ignore received message without outstanding "
655 LOG(tmppdu.get_request_id());
657 // the sent message is gone! probably was canceled, ignore it
661 if (tmppdu.get_request_id()) {
662 // we correctly received the pdu
664 // save it back into the message
665 status = msg->SetPdu(recv_status, tmppdu, fromaddress);
669 // received pdu does not match
670 // @todo if version is SNMPv3 we must return a report
671 // unknown pdu handler!
677 if (engine_id.len() > 0)
679 SnmpTarget *target = msg->GetTarget();
680 if ((target->get_type() == SnmpTarget::type_utarget) &&
681 (target->get_version() == version3))
683 UdpAddress addr = target->get_address();
685 LOG_BEGIN(DEBUG_LOG | 14);
686 LOG("MsgQueue: Adding engine id to table (addr) (id)");
687 LOG(addr.get_printable());
688 LOG(engine_id.get_printable());
691 v3MP::I->add_to_engine_id_table(engine_id,
692 (char*)addr.IpAddress::get_printable(),
700 status = msg->Callback(SNMP_CLASS_ASYNC_RESPONSE);
704 // this is an asynch response and the callback is done.
705 // no need to keep this message around;
706 // Dequeue the message
707 DeleteEntry(temp_req_id);
711 } // if socket has data
713 return SNMP_CLASS_SUCCESS;
716 #endif // HAVE_POLL_SYSCALL
718 int CSNMPMessageQueue::DoRetries(const msec &now)
722 int status = SNMP_CLASS_SUCCESS;
724 while ((msg = GetNextTimeoutEntry())) {
726 msg->GetSendTime(sendTime);
730 // send out the message again
732 status = msg->ResendMessage();
736 if (status == SNMP_CLASS_TIMEOUT)
738 unsigned long req_id = msg->GetId();
740 // Dequeue the message
743 // delete entry in cache
745 v3MP::I->delete_from_cache(req_id);
747 LOG_BEGIN(INFO_LOG | 6);
748 LOG("MsgQueue: Message timed out, removed id from v3MP cache (rid)");
754 // Some other send error, should we dequeue the message?
755 // do we really want to return without processing the rest?
762 break; // the next timeout is still in the future...so we are done
770 int CSNMPMessageQueue::Done()
775 int CSNMPMessageQueue::Done(unsigned long id) REENTRANT ({
776 // FF: This is much more efficient than the above
777 CSNMPMessage *msg = GetEntry(id);
779 if (!msg) return 1; // the message is not in the queue...must have timed out
781 if (msg->GetReceived())
787 #ifdef SNMP_PP_NAMESPACE
788 }; // end of namespace Snmp_pp