]> git.stg.codes - ssmd.git/blob - 3rdparty/snmp++/src/msgqueue.cpp
Initial adding
[ssmd.git] / 3rdparty / snmp++ / src / msgqueue.cpp
1 /*_############################################################################
2   _## 
3   _##  msgqueue.cpp  
4   _##
5   _##  SNMP++v3.2.25
6   _##  -----------------------------------------------
7   _##  Copyright (c) 2001-2010 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, Thu Sep  2 00:07:47 CEST 2010 
27   _##  
28   _##########################################################################*/
29 /*===================================================================
30
31   Copyright (c) 1999
32   Hewlett-Packard Company
33
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.
44
45       M S G Q U E U E . C P P
46
47       MSG QUEUE CLASS DECLARATION
48
49       Author:           Peter E Mellquist
50
51 =====================================================================*/
52 char msgqueue_version[]="#(@) SNMP++ $Id: msgqueue.cpp 1826 2010-08-29 20:19:59Z katz $";
53
54 //-----[ includes ]----------------------------------------------------
55
56 //----[ snmp++ includes ]----------------------------------------------
57
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"
63
64 #ifdef SNMP_PP_NAMESPACE
65 namespace Snmp_pp {
66 #endif
67
68
69 #define SNMP_PORT 161
70
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);
77
78 //----[ CSNMPMessage class ]-------------------------------------------
79
80 CSNMPMessage::CSNMPMessage(unsigned long id,
81                            Snmp * snmp,
82                            SnmpSocket socket,
83                            const SnmpTarget &target,
84                            Pdu &pdu,
85                            unsigned char * rawPdu,
86                            size_t rawPduLen,
87                            const Address & address,
88                            snmp_callback callBack,
89                            void * callData):
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)
93 {
94   // reset pdu mvs
95   m_pdu.set_error_index(0);
96   m_pdu.set_error_status(0);
97   m_pdu.set_request_id(m_uniqueId);
98
99   m_rawPdu = new unsigned char [rawPduLen];
100   memcpy(m_rawPdu, rawPdu, rawPduLen);
101   m_address = (Address *)address.clone();
102   m_target = target.clone();
103
104   SetSendTime();
105 }
106
107 CSNMPMessage::~CSNMPMessage()
108 {
109   delete [] m_rawPdu;
110   delete m_address;
111   delete m_target;
112 }
113
114 void CSNMPMessage::SetSendTime()
115 {
116   m_sendTime.refresh();
117
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.
122   // 11-Dec-95 TM
123   m_sendTime += (m_target->get_timeout() * 10);
124 }
125
126 int CSNMPMessage::SetPdu(const int reason, const Pdu &pdu,
127                          const UdpAddress &fromaddress)
128 {
129   if (Pdu::match_type(m_pdu.get_type(), pdu.get_type()) == false)
130   {
131     LOG_BEGIN(INFO_LOG | 1);
132     LOG("MsgQueue: Response pdu type does not match, pdu is ignored: (id) (type1) (type2)");
133     LOG(m_uniqueId);
134     LOG(m_pdu.get_type());
135     LOG(pdu.get_type());
136     LOG_END;
137
138     return -1;
139   }
140
141   unsigned short orig_type = m_pdu.get_type();
142   if (m_received)
143   {
144     LOG_BEGIN(WARNING_LOG | 1);
145     LOG("MsgQueue: Message is already marked as received (id) (reason) (new reason)");
146     LOG(m_uniqueId);
147     LOG(reason);
148     LOG(m_reason);
149     LOG_END;
150
151     // TODO: better check based on error codes
152     if (reason || !m_reason)
153     {
154       LOG_BEGIN(WARNING_LOG | 1);
155       LOG("MsgQueue: ignoring the second pdu");
156       LOG_END;
157
158       return 0;
159     }
160   }
161   m_received = 1;
162   m_pdu = pdu;
163   m_reason = reason;
164
165   LOG_BEGIN(DEBUG_LOG | 10);
166   LOG("MsgQueue: Response received (req id) (status) (msg id)");
167   LOG(pdu.get_request_id());
168   LOG(reason);
169 #ifdef _SNMPv3
170   LOG(pdu.get_message_id());
171 #endif
172   LOG_END;
173
174   if ((orig_type == sNMP_PDU_INFORM) &&
175       (m_pdu.get_type() == sNMP_PDU_RESPONSE))
176   {
177     // remove the first two vbs of the pdu if sysUpTime and notify_id
178     if (m_pdu.get_vb_count() < 2)
179       return 0;
180
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;
184
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;
188
189     TimeTicks timeticks;
190     Oid oid;
191
192     vb1.get_value(timeticks);
193     m_pdu.set_notify_timestamp(timeticks);
194
195     vb2.get_value(oid);
196     m_pdu.set_notify_id(oid);
197
198     m_pdu.delete_vb(1);
199     m_pdu.delete_vb(0);
200   }
201   return 0;
202 }
203
204 int CSNMPMessage::ResendMessage()
205 {
206   if (m_received)
207   {
208     // Don't bother to resend if we already have the response
209     SetSendTime();
210     return SNMP_CLASS_SUCCESS;
211   }
212
213   LOG_BEGIN(DEBUG_LOG | 10);
214   LOG("MsgQueue: Message (msg id) (req id) (info)");
215 #ifdef _SNMPv3
216   LOG(m_pdu.get_message_id());
217 #endif
218   LOG(m_pdu.get_request_id());
219   LOG((m_target->get_retry() <= 0) ? "TIMEOUT" : "RESEND");
220   LOG_END;
221
222   if (m_target->get_retry() <= 0)
223   {
224     // This message has timed out
225     Callback(SNMP_CLASS_TIMEOUT);   // perform callback with the error
226
227     return SNMP_CLASS_TIMEOUT;
228   }
229
230   m_target->set_retry(m_target->get_retry() - 1);
231   SetSendTime();
232   int status = send_snmp_request(m_socket, m_rawPdu, m_rawPduLen, *m_address);
233   if (status != 0)
234     return SNMP_CLASS_TL_FAILED;
235
236   return SNMP_CLASS_SUCCESS;
237 }
238
239 int CSNMPMessage::Callback(const int reason)
240 {
241   snmp_callback   tmp_callBack;
242   if (m_callBack) {
243     // prevent callbacks from using this message
244     tmp_callBack = m_callBack;
245     m_callBack = NULL;
246
247     tmp_callBack(reason, m_snmp, m_pdu, *m_target, m_callData);
248     return 0;
249   }
250   return 1;
251 }
252
253 //----[ CSNMPMessageQueueElt class ]--------------------------------------
254
255 CSNMPMessageQueue::CSNMPMessageQueueElt::CSNMPMessageQueueElt(
256                                            CSNMPMessage *message,
257                                            CSNMPMessageQueueElt *next,
258                                            CSNMPMessageQueueElt *previous):
259   m_message(message), m_Next(next), m_previous(previous)
260 {
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;
264 }
265
266 CSNMPMessageQueue::CSNMPMessageQueueElt::~CSNMPMessageQueueElt()
267 {
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;
272 }
273
274 CSNMPMessage *CSNMPMessageQueue::CSNMPMessageQueueElt::TestId(const unsigned long uniqueId)
275 {
276   if (m_message && (m_message->GetId() == uniqueId))
277     return m_message;
278   return 0;
279 }
280
281
282
283 //----[ CSNMPMessageQueue class ]--------------------------------------
284
285 CSNMPMessageQueue::CSNMPMessageQueue(EventListHolder *holder, Snmp *session)
286   : m_head(0, 0, 0), m_msgCount(0), my_holder(holder), m_snmpSession(session)
287 {
288 }
289
290 CSNMPMessageQueue::~CSNMPMessageQueue()
291 {
292   CSNMPMessageQueueElt *leftOver;
293   lock();
294     /*--------------------------------------------------------*/
295     /* walk the list deleting any elements still on the queue */
296     /*--------------------------------------------------------*/
297   while ((leftOver = m_head.GetNext()))
298     delete leftOver;
299
300   unlock();
301 }
302
303 CSNMPMessage * CSNMPMessageQueue::AddEntry(unsigned long id,
304                                            Snmp * snmp,
305                                            SnmpSocket socket,
306                                            const SnmpTarget &target,
307                                            Pdu &pdu,
308                                            unsigned char * rawPdu,
309                                            size_t rawPduLen,
310                                            const Address & address,
311                                            snmp_callback callBack,
312                                            void * callData)
313 {
314   if (snmp != m_snmpSession)
315   {
316     LOG_BEGIN(ERROR_LOG | 1);
317     LOG("MsgQueue: Adding message for other Snmp object.");
318     LOG_END;
319   }
320
321   CSNMPMessage *newMsg = new CSNMPMessage(id, snmp, socket, target, pdu,
322                                           rawPdu, rawPduLen, address,
323                                           callBack, callData);
324
325   lock();
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);
331   ++m_msgCount;
332
333   LOG_BEGIN(DEBUG_LOG | 10);
334   LOG("MsgQueue: Adding entry (req id) (count)");
335   LOG(id);
336   LOG(m_msgCount);
337   LOG_END;
338
339   unlock();
340   return newMsg;
341 }
342
343
344 CSNMPMessage *CSNMPMessageQueue::GetEntry(const unsigned long uniqueId)
345 {
346   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
347   CSNMPMessage *returnVal = NULL;
348
349   while (msgEltPtr){
350     if ((returnVal = msgEltPtr->TestId(uniqueId)))
351       return returnVal;
352     msgEltPtr = msgEltPtr->GetNext();
353   }
354   return 0;
355 }
356
357 int CSNMPMessageQueue::DeleteEntry(const unsigned long uniqueId)
358 {
359   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
360
361   while (msgEltPtr){
362     if (msgEltPtr->TestId(uniqueId)) {
363       delete msgEltPtr;
364       m_msgCount--;
365       LOG_BEGIN(DEBUG_LOG | 10);
366       LOG("MsgQueue: Removed entry (req id)");
367       LOG(uniqueId);
368       LOG_END;
369       return SNMP_CLASS_SUCCESS;
370     }
371     msgEltPtr = msgEltPtr->GetNext();
372   }
373   return SNMP_CLASS_INVALID_REQID;
374 }
375
376 void CSNMPMessageQueue::DeleteSocketEntry(const SnmpSocket socket)
377 REENTRANT({
378   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
379   CSNMPMessageQueueElt *tmp_msgEltPtr;
380   CSNMPMessage *msg = NULL;
381
382   while (msgEltPtr){
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();
389       // delete the entry
390       delete tmp_msgEltPtr;
391     }
392     else
393       msgEltPtr = msgEltPtr->GetNext();
394   }
395 })
396
397 CSNMPMessage * CSNMPMessageQueue::GetNextTimeoutEntry()
398 {
399   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
400   msec bestTime;
401   msec sendTime(bestTime);
402   CSNMPMessage *msg;
403   CSNMPMessage *bestmsg = NULL;
404
405   if (msgEltPtr) {
406     bestmsg = msgEltPtr->GetMessage();
407     bestmsg->GetSendTime(bestTime);
408   }
409
410   // This would be much simpler if the queue was an ordered list!
411   while (msgEltPtr){
412     msg = msgEltPtr->GetMessage();
413     msg->GetSendTime(sendTime);
414     if (bestTime  > sendTime) {
415       bestTime = sendTime;
416       bestmsg = msg;
417     }
418     msgEltPtr = msgEltPtr->GetNext();
419   }
420   return bestmsg;
421 }
422
423 int CSNMPMessageQueue::GetNextTimeout(msec &sendTime)
424 {
425   CSNMPMessage *msg = GetNextTimeoutEntry();
426
427   if (!msg)  return 1;    // nothing in the queue...
428
429   msg->GetSendTime(sendTime);
430   return 0;
431 }
432
433 #ifdef HAVE_POLL_SYSCALL
434
435 int CSNMPMessageQueue::GetFdCount()
436 {
437   SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
438
439   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
440
441   if (!msgEltPtr) return 0;
442
443 #ifndef SNMP_PP_IPv6
444   // we have at least one message in the queue and Snmp class
445   // can only have one socket, so
446   return 1;
447 #else
448   // we can have max 2 sockets
449   SnmpSocket firstSocket = msgEltPtr->GetMessage()->GetSocket();
450
451   msgEltPtr = msgEltPtr->GetNext();
452
453   while (msgEltPtr)
454   {
455       if (msgEltPtr->GetMessage()->GetSocket() != firstSocket)
456           return 2;
457       msgEltPtr = msgEltPtr->GetNext();
458   }
459   return 1;
460 #endif
461 }
462
463 bool CSNMPMessageQueue::GetFdArray(struct pollfd *readfds, int &remaining)
464 {
465   SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
466
467   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
468   if (!msgEltPtr) return true;
469
470   if (remaining <= 0) return false;
471
472   SnmpSocket firstSocket = msgEltPtr->GetMessage()->GetSocket();
473   readfds[0].fd = firstSocket;
474   readfds[0].events = POLLIN;
475   remaining--;
476
477 #ifndef SNMP_PP_IPv6
478   // we have at least one message in the queue and Snmp class
479   // can only have one socket, so we are done
480   return true;
481 #else
482   // we can have max 2 sockets
483   msgEltPtr = msgEltPtr->GetNext();
484
485   while (msgEltPtr)
486   {
487       if (msgEltPtr->GetMessage()->GetSocket() != firstSocket)
488       {
489           if (remaining <= 0) return false;
490           readfds[1].fd = msgEltPtr->GetMessage()->GetSocket();
491           readfds[1].events = POLLIN;
492           remaining--;
493
494           return true;
495       }
496       msgEltPtr = msgEltPtr->GetNext();
497   }
498   return true;
499 #endif
500 }
501
502 int CSNMPMessageQueue::HandleEvents(const struct pollfd *readfds,
503                                     const int fds)
504 {
505   for (int i=0; i < fds ; i++)
506   {
507     if (readfds[i].revents & POLLIN)
508     {
509       CSNMPMessage *msg;
510       UdpAddress fromaddress;
511       Pdu tmppdu;
512       unsigned long temp_req_id;
513       int status;
514       int recv_status;
515       OctetStr engine_id;
516
517       tmppdu.set_request_id(0);
518
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);
522
523       lock();
524       // find the corresponding msg in the message queue
525       temp_req_id = tmppdu.get_request_id();
526       msg = GetEntry(temp_req_id);
527       if (!msg)
528       {
529         unlock();
530         LOG_BEGIN(INFO_LOG | 7);
531         LOG("MsgQueue: Ignore received message without outstanding request (req id)");
532         LOG(tmppdu.get_request_id());
533         LOG_END;
534         // the sent message is gone! probably was canceled, ignore it
535         continue;
536       }
537
538       if (tmppdu.get_request_id())
539       {
540         // we correctly received the pdu
541
542         // save it back into the message
543         status = msg->SetPdu(recv_status, tmppdu, fromaddress);
544
545         if (status != 0)
546         {
547           // received pdu does not match
548           // @todo if version is SNMPv3 we must return a report
549           //       unknown pdu handler!
550           unlock();
551           continue;
552         }
553
554 #ifdef _SNMPv3
555         if (engine_id.len() > 0)
556         {
557           SnmpTarget *target = msg->GetTarget();
558           if ((target->get_type() == SnmpTarget::type_utarget) &&
559               (target->get_version() == version3))
560           {
561             UdpAddress addr = target->get_address();
562
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());
567             LOG_END;
568
569             v3MP::I->add_to_engine_id_table(engine_id,
570                                             (char*)addr.IpAddress::get_printable(),
571                                             addr.get_port());
572           }
573         }
574 #endif
575
576         // Do the callback
577         unlock();
578         status = msg->Callback(SNMP_CLASS_ASYNC_RESPONSE);
579         lock();
580
581         if (!status)
582         {
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);
587         }
588       }
589       unlock();
590     }
591   }
592   return SNMP_CLASS_SUCCESS;
593 }
594
595 #else // HAVE_POLL_SYSCALL
596
597 void CSNMPMessageQueue::GetFdSets(int &maxfds, fd_set &readfds,
598                                   fd_set &, fd_set &)
599 {
600   SnmpSynchronize _synchronize(*this); // REENTRANT
601   CSNMPMessageQueueElt *msgEltPtr = m_head.GetNext();
602   SnmpSocket sock;
603
604   while (msgEltPtr){
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();
610   }
611 }
612
613 int CSNMPMessageQueue::HandleEvents(const int maxfds,
614                                     const fd_set &readfds,
615                                     const fd_set &,
616                                     const fd_set &)
617 {
618   CSNMPMessage *msg;
619   UdpAddress fromaddress;
620   Pdu tmppdu;
621   unsigned long temp_req_id;
622   int status;
623   int recv_status;
624   fd_set snmp_readfds, snmp_writefds, snmp_errfds;
625   int tmp_maxfds = maxfds;
626
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);
632
633   for (int fd = 0; fd < maxfds; fd++)
634   {
635     if ((FD_ISSET(fd, &snmp_readfds)) &&
636         (FD_ISSET(fd, (fd_set*)&readfds)))
637     {
638       OctetStr engine_id;
639
640       tmppdu.set_request_id(0);
641
642       // get the response and put it into a Pdu
643       recv_status = receive_snmp_response(fd, *m_snmpSession,
644                                           tmppdu, fromaddress, engine_id);
645
646       lock();
647       // find the corresponding msg in the message queue
648       temp_req_id = tmppdu.get_request_id();
649       msg = GetEntry(temp_req_id);
650       if (!msg) {
651              unlock();
652              LOG_BEGIN(INFO_LOG | 7);
653              LOG("MsgQueue: Ignore received message without outstanding "
654                  "request (req id)");
655              LOG(tmppdu.get_request_id());
656              LOG_END;
657              // the sent message is gone! probably was canceled, ignore it
658              continue;
659       }
660
661       if (tmppdu.get_request_id()) {
662              // we correctly received the pdu
663
664              // save it back into the message
665              status = msg->SetPdu(recv_status, tmppdu, fromaddress);
666
667              if (status != 0)
668              {
669                // received pdu does not match
670                // @todo if version is SNMPv3 we must return a report
671                //       unknown pdu handler!
672                unlock();
673                continue;
674              }
675
676 #ifdef _SNMPv3
677              if (engine_id.len() > 0)
678              {
679                  SnmpTarget *target = msg->GetTarget();
680                  if ((target->get_type() == SnmpTarget::type_utarget) &&
681                      (target->get_version() == version3))
682                  {
683                    UdpAddress addr = target->get_address();
684
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());
689                    LOG_END;
690
691                    v3MP::I->add_to_engine_id_table(engine_id,
692                                (char*)addr.IpAddress::get_printable(),
693                                addr.get_port());
694                  }
695              }
696 #endif
697
698              // Do the callback
699              unlock();
700              status = msg->Callback(SNMP_CLASS_ASYNC_RESPONSE);
701              lock();
702
703              if (!status) {
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);
708              }
709       }
710       unlock();
711     } // if socket has data
712   } // for all sockets
713   return SNMP_CLASS_SUCCESS;
714 }
715
716 #endif // HAVE_POLL_SYSCALL
717
718 int CSNMPMessageQueue::DoRetries(const msec &now)
719 {
720   CSNMPMessage *msg;
721   msec sendTime(0, 0);
722   int status = SNMP_CLASS_SUCCESS;
723   lock();
724   while ((msg = GetNextTimeoutEntry())) {
725
726     msg->GetSendTime(sendTime);
727
728     if (sendTime <= now)
729     {
730       // send out the message again
731       unlock();
732       status = msg->ResendMessage();
733       lock();
734       if (status != 0)
735       {
736         if (status == SNMP_CLASS_TIMEOUT)
737         {
738           unsigned long req_id = msg->GetId();
739
740           // Dequeue the message
741           DeleteEntry(req_id);
742 #ifdef _SNMPv3
743           // delete entry in cache
744           if (v3MP::I)
745             v3MP::I->delete_from_cache(req_id);
746
747           LOG_BEGIN(INFO_LOG | 6);
748           LOG("MsgQueue: Message timed out, removed id from v3MP cache (rid)");
749           LOG(req_id);
750           LOG_END;
751 #endif
752         }
753         else {
754           // Some other send error, should we dequeue the message?
755           // do we really want to return without processing the rest?
756           unlock();
757           return status;
758         }
759       }
760     }
761     else {
762       break;  // the next timeout is still in the future...so we are done
763     }
764   }
765   unlock();
766   return status;
767 }
768
769
770 int CSNMPMessageQueue::Done()
771 {
772     return 0;
773 }
774
775 int CSNMPMessageQueue::Done(unsigned long id) REENTRANT ({
776   // FF: This is much more efficient than the above
777   CSNMPMessage *msg = GetEntry(id);
778
779   if (!msg) return 1; // the message is not in the queue...must have timed out
780
781   if (msg->GetReceived())
782       return 1;
783
784   return 0;
785 })
786
787 #ifdef SNMP_PP_NAMESPACE
788 }; // end of namespace Snmp_pp
789 #endif