]> git.stg.codes - ssmd.git/blob - 3rdparty/snmp++/src/uxsnmp.cpp
Initial adding
[ssmd.git] / 3rdparty / snmp++ / src / uxsnmp.cpp
1 /*_############################################################################
2   _## 
3   _##  uxsnmp.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       U X S N M P . C P P
31
32       UXSNMP CLASS DECLARATION
33
34       Description:    Snmp class
35
36       Author:         Peter E Mellquist
37 =====================================================================*/
38 char snmp_cpp_version[]="#(@) SNMP++ $Id: uxsnmp.cpp 1798 2010-08-14 20:10:48Z katz $";
39
40 /* CK Ng    added support for WIN32 in the whole file */
41
42 //-----[ includes ]----------------------------------------------------
43 #ifdef WIN32
44 #include <sys/types.h>     // system types
45 #include <sys/timeb.h>     // _timeb and _ftime
46 #else
47 #include <unistd.h>        // unix
48 #include <sys/socket.h>    // bsd socket stuff
49 #include <netinet/in.h>    // network types
50 #include <arpa/inet.h>     // arpa types
51 #include <sys/types.h>     // system types
52 #if !(defined CPU && CPU == PPC603)
53 #include <sys/time.h>      // time stuff
54 #endif
55 #endif
56 #ifdef _AIX
57 #define ss_family __ss_family
58 #endif
59
60 #include <stdlib.h>        // need for malloc
61 #include <errno.h>         // ux errs
62
63 #define _INCLUDE_SNMP_ERR_STRINGS
64
65 //----[ snmp++ includes ]----------------------------------------------
66 #include "snmp_pp/config_snmp_pp.h"
67 #include "snmp_pp/uxsnmp.h"        // class def for this module
68 #include "snmp_pp/oid_def.h"       // class def for well known trap oids
69 #include "snmp_pp/v3.h"
70 #include "snmp_pp/msgqueue.h"      // message queue
71 #include "snmp_pp/notifyqueue.h"   // notification queue
72 #include "snmp_pp/snmpmsg.h"       // asn serialization class
73 #include "snmp_pp/eventlistholder.h"
74 #include "snmp_pp/usm_v3.h"
75 #include "snmp_pp/vb.h"
76 #include "snmp_pp/log.h"
77 #include "snmp_pp/IPv6Utility.h"
78
79 #if defined (CPU) && CPU == PPC603
80 #include <sockLib.h> 
81 #include <taskLib.h> 
82 #endif
83
84 #ifdef SNMP_PP_NAMESPACE
85 namespace Snmp_pp {
86 #endif
87
88 //-----[ special includes ]-------------------------------------------
89 extern "C"
90 {
91   //------------[ if using Wind-U, then bring in the ms-windows header ]
92 #ifndef WIN32
93   typedef short WORD;
94   typedef long DWORD;
95 #endif
96 }
97
98 //-----[ macros ]------------------------------------------------------
99 #define DEFAULT_TIMEOUT 1000  // one second default timeout
100 #define DEFAULT_RETRIES 1     // no retry default
101 #define SNMP_PORT 161         // port # for SNMP
102 #define SNMP_TRAP_PORT 162    // port # for SNMP traps
103
104 #ifdef WIN32
105 #ifdef __BCPLUSPLUS__
106 #define _timeb timeb
107 #define _ftime ftime
108 #endif
109 #define close closesocket
110 #endif
111
112 //--------[ globals ]---------------------------------------------------
113
114 //--------------[ well known trap ids ]-----------------------------------
115 const coldStartOid coldStart;
116 const warmStartOid warmStart;
117 const linkDownOid linkDown;
118 const linkUpOid linkUp;
119 const authenticationFailureOid authenticationFailure;
120 const egpNeighborLossOid egpNeighborLoss;
121 const snmpTrapEnterpriseOid snmpTrapEnterprise;
122
123
124 #ifdef _SNMPv3
125
126 void deleteV3Callback(struct Snmp::V3CallBackData *&cbData)
127 {
128   if (cbData->pdu) {
129     delete cbData->pdu;
130     cbData->pdu = 0;
131   }
132   if (cbData->target) {
133     delete cbData->target;
134     cbData->target = 0;
135   }
136   delete cbData;
137   cbData = 0;
138 }
139
140 void v3CallBack(int reason, Snmp *snmp, Pdu &pdu, SnmpTarget &target, void *v3cd)
141 {
142   struct Snmp::V3CallBackData *cbData = (struct Snmp::V3CallBackData*)v3cd;
143
144   Vb tmpvb;
145   pdu.get_vb(tmpvb,0);
146
147   debugprintf(5, "v3CallBack: received oid: %s with value: %s",
148               tmpvb.get_printable_oid(), tmpvb.get_printable_value());
149   debugprintf(5, "v3CallBack: error_msg (%s), pdu_type (%i)",
150               snmp->error_msg(tmpvb.get_oid()), pdu.get_type());
151
152   if ((pdu.get_type() == REPORT_MSG) &&
153       (((tmpvb.get_oid() == oidUsmStatsUnknownEngineIDs) &&
154         (cbData->reports_received == 0)) ||
155        ((tmpvb.get_oid() == oidUsmStatsNotInTimeWindows) &&
156         (cbData->reports_received <= 1)))) {
157     // hide those reports from user
158     int rc;
159     if ((cbData->pdu) && (cbData->target)) {
160       rc = snmp->snmp_engine(*(cbData->pdu), cbData->non_reps,
161                              cbData->max_reps, *(cbData->target),
162                              (snmp_callback)(cbData->oldCallback),
163                              (void *)cbData->cbd, INVALID_SOCKET,
164                              cbData->reports_received + 1);
165       debugprintf(3,"v3CallBack: snmp_engine called, rc (%i)", rc);
166     }
167     else
168       rc = SNMP_CLASS_ERROR;
169
170     if (rc != SNMP_CLASS_SUCCESS) {
171       // call callback if snmp_engine failed or pdu or target was 0
172       debugprintf(3,"v3CallBack: calling user callback");
173       snmp_callback tmp_callBack;
174       tmp_callBack = (snmp_callback)(cbData->oldCallback);
175       tmp_callBack(rc, snmp, pdu, target, (void *)cbData->cbd);
176     }
177   }
178   else {
179     debugprintf(3,"v3CallBack: calling user callback");
180     snmp_callback tmp_callBack;
181     tmp_callBack = (snmp_callback)(cbData->oldCallback);
182     tmp_callBack(reason, snmp, pdu, target, (void *)cbData->cbd);
183   }
184   // save to delete it here, because either snmp_engine created a new
185   // callback entry or the user specified callback has been called
186   deleteV3Callback(cbData);
187   return;
188 }
189 #endif
190
191 //--------[ make the pdu request id ]-----------------------------------
192 // return a unique rid, clock can be too slow , so use current_rid
193 long Snmp::MyMakeReqId()
194 {
195   long rid;
196   eventListHolder->snmpEventList()->lock();
197   do {
198     rid = ++current_rid;
199
200 #ifdef INVALID_REQID
201     debugprintf(-10, "\nWARNING: Using constand RequestID!\n");
202     rid = 0xc0de;
203 #endif
204
205     if ( current_rid > PDU_MAX_RID)
206     {
207       current_rid = rid = PDU_MIN_RID;
208       // let other tasks proceed
209       eventListHolder->snmpEventList()->unlock();
210       struct timeval tv;
211       tv.tv_sec = 0;
212       tv.tv_usec = 100;
213       select(0, 0, 0, 0, &tv);
214       eventListHolder->snmpEventList()->lock();
215     }
216   } while (eventListHolder->snmpEventList()->GetEntry(rid));
217   eventListHolder->snmpEventList()->unlock();
218
219   return rid;
220 }
221
222 //---------[ Send SNMP Request ]---------------------------------------
223 // Send out a snmp request
224 DLLOPT int send_snmp_request(SnmpSocket sock, unsigned char *send_buf,
225                              size_t send_len, Address & address)
226 {
227   // UX only supports UDP type addresses (addr and port) right now
228   if (address.get_type() != Address::type_udp)
229     return -1;// unsupported address type
230
231   debugprintf(1, "++ SNMP++: sending to %s:",
232               ((UdpAddress &)address).UdpAddress::get_printable());
233   debughexprintf(5, send_buf, SAFE_UINT_CAST(send_len));
234
235   int send_result;
236
237   if (((UdpAddress &)address).get_ip_version() == Address::version_ipv4)
238   {
239     // prepare the destination address
240     struct sockaddr_in agent_addr;  // send socket struct
241     memset(&agent_addr, 0, sizeof(agent_addr));
242     agent_addr.sin_family = AF_INET;
243     agent_addr.sin_addr.s_addr
244               = inet_addr(((IpAddress &)address).IpAddress::get_printable());
245     agent_addr.sin_port = htons(((UdpAddress &)address).get_port());
246
247     send_result = sendto(sock, (char*) send_buf, SAFE_INT_CAST(send_len), 0,
248                          (struct sockaddr*) &agent_addr, sizeof(agent_addr));
249   }
250   else
251   {
252 #ifdef SNMP_PP_IPv6
253     struct sockaddr_in6 agent_addr;
254     memset(&agent_addr, 0, sizeof(agent_addr));
255     unsigned int scope = 0;
256     
257     OctetStr addrstr = ((IpAddress &)address).IpAddress::get_printable();
258
259     if (((IpAddress &)address).has_ipv6_scope())
260     {
261         scope = ((IpAddress &)address).get_scope();
262
263         int i = addrstr.len() - 1;
264         while ((i>0) && (addrstr[i] != '%'))
265         {
266             addrstr.set_len(addrstr.len() - 1);
267             i--;
268         }
269         if (addrstr[i] == '%')
270             addrstr.set_len(addrstr.len() - 1);
271     }
272
273     if (inet_pton(AF_INET6, addrstr.get_printable(),
274                   &agent_addr.sin6_addr) < 0)
275     {
276         LOG_BEGIN(ERROR_LOG | 1);
277         LOG("Snmp transport: inet_pton returns (errno) (str)");
278         LOG(errno);
279         LOG(strerror(errno));
280         LOG_END;
281         return -1;
282     }
283     agent_addr.sin6_family = AF_INET6;
284     agent_addr.sin6_port = htons(((UdpAddress &)address).get_port());
285     agent_addr.sin6_scope_id = scope;
286     send_result = sendto( sock, (char*) send_buf, send_len, 0,
287                           (struct sockaddr*) &agent_addr, sizeof(agent_addr));
288 #else
289     debugprintf(0, "User error: Enable IPv6 and recompile snmp++.");
290     return -1;
291 #endif
292   }
293
294   if (send_result < 0)
295   {
296     debugprintf(0, "Error sending packet: %s", strerror(errno));
297     return -1; // send error!
298   }
299
300   return 0;
301 }
302
303 //---------[ receive a snmp response ]---------------------------------
304 // Receive a response from the specified socket.
305 // This function does not set the request id in the pdu if
306 // any error occur in receiving or parsing.  This is important
307 // because the caller initializes this to zero and checks it to
308 // see whether it has been changed to a valid value.  The
309 // return value is the normal PDU status or SNMP_CLASS_SUCCESS.
310 // when we are successful in receiving a pdu.  Otherwise it
311 // is an error status.
312
313 int receive_snmp_response(SnmpSocket sock, Snmp &snmp_session,
314                           Pdu &pdu, UdpAddress &fromaddress,
315                           OctetStr &engine_id, bool process_msg = true)
316 {
317   unsigned char receive_buffer[MAX_SNMP_PACKET + 1];
318   long receive_buffer_len; // len of received data
319 #ifdef SNMP_PP_IPv6
320   struct sockaddr_storage from_addr;
321 #else
322   struct sockaddr_in from_addr;
323 #endif
324 #if !(defined (CPU) && CPU == PPC603) && (defined __GNUC__ || defined __FreeBSD__ || defined _AIX) && ! defined __MINGW32__
325   socklen_t fromlen;
326 #else
327   int fromlen;
328 #endif
329   fromlen = sizeof(from_addr);
330
331   memset(&from_addr, 0, sizeof(from_addr));
332
333   // do the read
334   do {
335     receive_buffer_len = (long) recvfrom(sock, (char *) receive_buffer,
336                                          MAX_SNMP_PACKET + 1, 0,
337                                          (struct sockaddr*) &from_addr,
338                                          &fromlen);
339     debugprintf(2, "++ SNMP++: something received...");
340   } while ((receive_buffer_len < 0) && (EINTR == errno));
341
342   if (receive_buffer_len < 0 )                // error or no data pending
343     return SNMP_CLASS_TL_FAILED;
344   debugprintf(6, "Length received %i from socket %i; fromlen %i",
345               receive_buffer_len, sock, fromlen);
346
347   if (receive_buffer_len == MAX_SNMP_PACKET + 1)
348   {
349     // Message is too long...
350     debugprintf(1, "Received message is ignored (packet too long)");
351     return SNMP_CLASS_ERROR;
352   }
353
354   if (((sockaddr_in&)from_addr).sin_family == AF_INET)
355   {
356     // IPv4
357     fromaddress = inet_ntoa(((sockaddr_in&)from_addr).sin_addr);
358     fromaddress.set_port(ntohs(((sockaddr_in&)from_addr).sin_port));
359   }
360 #ifdef SNMP_PP_IPv6
361   else if (from_addr.ss_family == AF_INET6)
362   {
363     // IPv6
364     char tmp_buffer[INET6_ADDRSTRLEN+1];
365
366     inet_ntop(AF_INET6, &(((sockaddr_in6&)from_addr).sin6_addr),
367               tmp_buffer, INET6_ADDRSTRLEN);
368
369     fromaddress = tmp_buffer;
370     fromaddress.set_port(ntohs(((sockaddr_in6&)from_addr).sin6_port));
371     if (((sockaddr_in6&)from_addr).sin6_scope_id != 0)
372         fromaddress.set_scope(((sockaddr_in6&)from_addr).sin6_scope_id);
373   }
374 #endif // SNMP_PP_IPv6
375   else
376   {
377     debugprintf(0, "Unknown socket address family (%i).",
378                 ((sockaddr_in&)from_addr).sin_family);
379     return SNMP_CLASS_ERROR;
380   }
381
382   debugprintf(1, "++ SNMP++: data received from %s.",
383               fromaddress.get_printable());
384   debughexprintf(5, receive_buffer, receive_buffer_len);
385
386   if (process_msg == false)
387     return SNMP_CLASS_SUCCESS;   // return success
388
389   SnmpMessage snmpmsg;
390   if ( snmpmsg.load( receive_buffer, receive_buffer_len) != SNMP_CLASS_SUCCESS)
391     return SNMP_CLASS_ERROR;
392
393   OctetStr community_name;
394   snmp_version version;
395   OctetStr security_name;
396
397 #ifdef _SNMPv3
398   long int security_model;
399   if (snmpmsg.is_v3_message() == TRUE)
400   {
401     int returncode = snmpmsg.unloadv3(pdu, version, engine_id,
402                                       security_name, security_model,
403                                       fromaddress, snmp_session);
404     if (returncode != SNMP_CLASS_SUCCESS)
405       return returncode;
406   }
407   else
408   {
409 #endif
410     int returncode = snmpmsg.unload( pdu, community_name, version);
411     if (returncode != SNMP_CLASS_SUCCESS)
412       return SNMP_CLASS_ERROR;
413 #ifdef _SNMPv3
414   }
415   if (version == version3)
416   {
417     debugprintf(4,"receive_snmp_response: engine_id (%s), security_name (%s), "
418                 "security_model (%i), security_level (%i)",
419                 engine_id.get_printable(), security_name.get_printable(),
420                 security_model, pdu.get_security_level());
421     debugprintf(5," addtoengineidtable: (%s)",
422                 (unsigned char*)fromaddress.get_printable());
423   }
424 #endif
425
426   //-----[ check for error status stuff..]
427   // an error status is a valid pdu,
428   // the caller needs to know about it
429   if ( pdu.get_error_status() != 0)
430     return pdu.get_error_status();
431
432   debugprintf(5,"receive_snmp_response requestID = %li, "
433               "returning SUCCESS.", pdu.get_request_id());
434
435   return SNMP_CLASS_SUCCESS;   // Success! return
436 }
437
438
439 //---------[ receive a snmp trap ]---------------------------------
440 // Receive a trap from the specified socket
441 // note: caller has to delete target!
442 int receive_snmp_notification(SnmpSocket sock, Snmp &snmp_session,
443                               Pdu &pdu, SnmpTarget **target)
444 {
445   unsigned char receive_buffer[MAX_SNMP_PACKET + 1];
446   long receive_buffer_len; // len of received data
447
448 #ifdef SNMP_PP_IPv6
449   struct sockaddr_storage from_addr;
450 #else
451   struct sockaddr_in from_addr;
452 #endif // SNMP_PP_IPv6
453
454 #if !(defined (CPU) && CPU == PPC603) && (defined __GNUC__ || defined __FreeBSD__ || defined _AIX) && ! defined __MINGW32__
455   socklen_t fromlen;
456 #else
457   int fromlen;
458 #endif
459   fromlen = sizeof(from_addr);
460
461   memset(&from_addr, 0, sizeof(from_addr));
462
463   // do the read
464   do {
465     receive_buffer_len = (long) recvfrom(sock, (char *) receive_buffer,
466                                          MAX_SNMP_PACKET + 1, 0,
467                                          (struct sockaddr*)&from_addr,
468                                          &fromlen);
469   } while (receive_buffer_len < 0 && EINTR == errno);
470
471   if (receive_buffer_len < 0 )                // error or no data pending
472     return SNMP_CLASS_TL_FAILED;
473
474   if (receive_buffer_len == MAX_SNMP_PACKET + 1)
475   {
476     // Message is too long...
477     debugprintf(1, "Received message is ignored (packet too long)");
478     return SNMP_CLASS_ERROR;
479   }
480
481   // copy fromaddress and remote port
482   UdpAddress fromaddress;
483
484   if (((sockaddr_in&)from_addr).sin_family == AF_INET)
485   {
486     // IPv4
487     fromaddress = inet_ntoa(((sockaddr_in&)from_addr).sin_addr);
488     fromaddress.set_port(ntohs(((sockaddr_in&)from_addr).sin_port));
489   }
490 #ifdef SNMP_PP_IPv6
491   else if (from_addr.ss_family == AF_INET6)
492   {
493     // IPv6
494     char tmp_buffer[INET6_ADDRSTRLEN+1];
495
496     inet_ntop(AF_INET6, &(((sockaddr_in6&)from_addr).sin6_addr),
497               tmp_buffer, INET6_ADDRSTRLEN);
498
499     fromaddress = tmp_buffer;
500     fromaddress.set_port(ntohs(((sockaddr_in6&)from_addr).sin6_port));
501     if (((sockaddr_in6&)from_addr).sin6_scope_id != 0)
502         fromaddress.set_scope(((sockaddr_in6&)from_addr).sin6_scope_id);
503   }
504 #endif // SNMP_PP_IPv6
505   else
506   {
507     debugprintf(0, "Unknown socket address family (%i).",
508                 ((sockaddr_in&)from_addr).sin_family);
509     return SNMP_CLASS_TL_FAILED;
510   }
511
512   debugprintf(1, "++ SNMP++: data received from %s.",
513               fromaddress.get_printable());
514   debughexprintf(5, receive_buffer, receive_buffer_len);
515
516   SnmpMessage snmpmsg;
517   if ( snmpmsg.load( receive_buffer, receive_buffer_len) != SNMP_CLASS_SUCCESS)
518     return SNMP_CLASS_ERROR;
519
520   OctetStr community_name;
521   snmp_version version;
522   OctetStr engine_id;
523   OctetStr security_name;
524
525 #ifdef _SNMPv3
526   long int security_model;
527   if (snmpmsg.is_v3_message() == TRUE)
528   {
529     int returncode = snmpmsg.unloadv3(pdu, version, engine_id,
530                                       security_name, security_model,
531                                       fromaddress, snmp_session);
532     if (returncode != SNMP_CLASS_SUCCESS)
533       return returncode;
534   }
535   else
536   {
537 #endif
538     int returncode = snmpmsg.unload( pdu, community_name, version);
539     if (returncode != SNMP_CLASS_SUCCESS)
540       return SNMP_CLASS_ERROR;
541 #ifdef _SNMPv3
542   }
543
544   if (version == version3) {
545     *target = new UTarget();
546     (*target)->set_address(fromaddress);
547     (*target)->set_version(version);
548     ((UTarget*)*target)->set_engine_id(engine_id);
549     ((UTarget*)*target)->set_security_name(security_name);
550     ((UTarget*)*target)->set_security_model(security_model);
551
552     v3MP::I->add_to_engine_id_table(engine_id,
553                          (char*)(fromaddress.IpAddress::get_printable()),
554                          fromaddress.get_port());
555
556     debugprintf(4,"receive_snmp_notification: engine_id (%s), security_name "
557                 "(%s), security_model (%i), security_level (%i)",
558                 engine_id.get_printable(), security_name.get_printable(),
559                 security_model, pdu.get_security_level());
560   }
561   else
562   {
563 #endif
564     *target = new CTarget();
565     (*target)->set_version(version);
566     (*target)->set_address(fromaddress);
567     ((CTarget*)*target)->set_readcommunity( community_name);
568     ((CTarget*)*target)->set_writecommunity( community_name);
569 #ifdef _SNMPv3
570   }
571 #endif
572   return SNMP_CLASS_SUCCESS;   // Success! return
573 }
574
575
576 //--------[ map action ]------------------------------------------------
577 // map the snmp++ action to a SMI pdu type
578 void Snmp::map_action( unsigned short action, unsigned short & pdu_action)
579 {
580   switch( action)
581     {
582     case sNMP_PDU_GET:
583     case sNMP_PDU_GET_ASYNC:
584       pdu_action = sNMP_PDU_GET;
585       break;
586
587     case sNMP_PDU_SET:
588     case sNMP_PDU_SET_ASYNC:
589       pdu_action = sNMP_PDU_SET;
590       break;
591
592     case sNMP_PDU_GETNEXT:
593     case sNMP_PDU_GETNEXT_ASYNC:
594       pdu_action = sNMP_PDU_GETNEXT;
595       break;
596
597     case sNMP_PDU_GETBULK:
598     case sNMP_PDU_GETBULK_ASYNC:
599       pdu_action = sNMP_PDU_GETBULK;
600       break;
601
602     case sNMP_PDU_RESPONSE:
603       pdu_action = sNMP_PDU_RESPONSE;
604       break;
605
606     case sNMP_PDU_INFORM:
607     case sNMP_PDU_INFORM_ASYNC:
608       pdu_action = sNMP_PDU_INFORM;
609       break;
610
611     case sNMP_PDU_REPORT:
612       pdu_action = sNMP_PDU_REPORT;
613       break;
614
615     default:
616       pdu_action = sNMP_PDU_GET;  // TM ?? error ??
617       break;
618
619     };  // end switch
620 }
621
622 //------[ Snmp Class Constructor ]--------------------------------------
623
624 Snmp::Snmp(int &status, const unsigned short port, const bool bind_ipv6)
625     : SnmpSynchronized(),
626       m_bThreadRunning(false), m_iPollTimeOut(DEFAULT_TIMEOUT)
627 {
628   IpAddress *addresses[2];
629
630   if (bind_ipv6)
631   {
632     listen_address = "::";
633
634     addresses[0] = NULL;
635     addresses[1] = &listen_address;
636
637     init(status, addresses, 0, port);
638   }
639   else
640   {
641     listen_address = "0.0.0.0";
642
643     addresses[0] = &listen_address;
644     addresses[1] = NULL;
645
646     init(status, addresses, port, 0);
647   }
648
649 }
650
651 Snmp::Snmp( int &status, const UdpAddress& addr)
652     : SnmpSynchronized(),
653       m_bThreadRunning(false), m_iPollTimeOut(DEFAULT_TIMEOUT)
654 {
655   IpAddress *addresses[2];
656
657   listen_address = addr;
658
659   if (listen_address.get_ip_version() == Address::version_ipv4)
660   {
661     addresses[0] = &listen_address;
662     addresses[1] = NULL;
663     init(status, addresses, addr.get_port(), 0);
664   }
665   else
666   {
667     addresses[0] = NULL;
668     addresses[1] = &listen_address;
669     init(status, addresses, 0, addr.get_port());
670   }
671 }
672
673 Snmp::Snmp( int &status,  const UdpAddress& addr_v4,
674             const UdpAddress& addr_v6)
675     : SnmpSynchronized(),
676       m_bThreadRunning(false), m_iPollTimeOut(DEFAULT_TIMEOUT)
677 {
678   IpAddress *addresses[2];
679
680   listen_address = addr_v4;
681   IpAddress address_v6((IpAddress)addr_v6);
682   addresses[0] = &listen_address;
683   addresses[1] = &address_v6;
684
685   init(status, addresses, addr_v4.get_port(), addr_v6.get_port());
686 }
687
688 void Snmp::socket_startup()
689 {
690 #ifdef WIN32
691   WSADATA WSAData;
692   (void)WSAStartup(0x0101, &WSAData);
693 #endif
694 }
695
696 void Snmp::socket_cleanup()
697 {
698 #ifdef WIN32
699   int iRetValue = WSACleanup();
700   debugprintf(4, "WSACleanup: ReturnValue (%i)", iRetValue);
701 #endif
702 }
703
704 void Snmp::init(int& status, IpAddress *addresses[2],
705                 const unsigned short port_v4,
706                 const unsigned short port_v6)
707 {
708 #ifdef _THREADS
709 #ifdef WIN32
710   m_hThread = INVALID_HANDLE_VALUE;
711   m_hThreadEndEvent = ::CreateEvent(NULL, true, false, NULL);
712 #endif
713 #endif
714
715   eventListHolder = new EventListHolder(this);
716   // initialize the request_id
717   eventListHolder->snmpEventList()->lock();
718 //  srand(time(0)); // better than nothing
719   current_rid = (rand() % (PDU_MAX_RID - PDU_MIN_RID +1)) + PDU_MIN_RID;
720   debugprintf(4, "Initialized request_id to %i.", current_rid);
721   eventListHolder->snmpEventList()->unlock();
722
723   // intialize all the trap receiving member variables
724   notifycallback = 0;
725   notifycallback_data = 0;
726 #ifdef HPUX
727   int errno = 0;
728 #endif
729
730   status = SNMP_CLASS_ERROR;
731   iv_snmp_session = INVALID_SOCKET;
732 #ifdef SNMP_PP_IPv6
733   iv_snmp_session_ipv6 = INVALID_SOCKET;
734 #endif
735
736   /* Open IPv4 socket */
737   if (addresses[0])
738   {
739     // open a socket to be used for the session
740     if (( iv_snmp_session = socket( AF_INET, SOCK_DGRAM,0)) == INVALID_SOCKET)
741     {
742 #ifdef WIN32
743       int werr = WSAGetLastError();
744       debugprintf(1, "Call to socket throws error %d", werr);
745       if (EMFILE == werr ||WSAENOBUFS == werr || ENFILE == werr)
746         status = SNMP_CLASS_RESOURCE_UNAVAIL;
747       else if (WSAEHOSTDOWN == werr)
748         status = SNMP_CLASS_TL_FAILED;
749       else
750         status = SNMP_CLASS_TL_UNSUPPORTED;
751 #else
752       if (EMFILE == errno || ENOBUFS == errno || ENFILE == errno)
753         status = SNMP_CLASS_RESOURCE_UNAVAIL;
754       else if (EHOSTDOWN == errno)
755         status = SNMP_CLASS_TL_FAILED;
756       else
757         status = SNMP_CLASS_TL_UNSUPPORTED;
758 #endif
759     }
760     else
761     {
762       // set up the manager socket attributes
763       unsigned long inaddr = inet_addr(addresses[0]->get_printable());
764       struct sockaddr_in mgr_addr;
765       memset(&mgr_addr, 0, sizeof(mgr_addr));
766       mgr_addr.sin_family = AF_INET;
767       mgr_addr.sin_addr.s_addr = inaddr;
768       mgr_addr.sin_port = htons( port_v4);
769 #ifdef CYGPKG_NET_OPENBSD_STACK
770       mgr_addr.sin_len = sizeof(mgr_addr);
771 #endif
772
773       // bind the socket
774       if (bind(iv_snmp_session, (struct sockaddr*)&mgr_addr,
775                sizeof(mgr_addr)) < 0)
776       {
777 #ifdef WIN32
778         int werr = WSAGetLastError();
779         debugprintf(1, "Call to bind throws error %d", werr);
780         if (WSAEADDRINUSE  == werr)
781           status = SNMP_CLASS_TL_IN_USE;
782         else if (WSAENOBUFS == werr)
783           status = SNMP_CLASS_RESOURCE_UNAVAIL;
784         else if (werr == WSAEAFNOSUPPORT)
785           status = SNMP_CLASS_TL_UNSUPPORTED;
786         else if (werr == WSAENETUNREACH)
787           status = SNMP_CLASS_TL_FAILED;
788         else if (werr == EACCES)
789           status = SNMP_CLASS_TL_ACCESS_DENIED;
790         else
791           status = SNMP_CLASS_INTERNAL_ERROR;
792 #else
793         if (EADDRINUSE  == errno)
794           status = SNMP_CLASS_TL_IN_USE;
795         else if (ENOBUFS == errno)
796           status = SNMP_CLASS_RESOURCE_UNAVAIL;
797         else if (errno == EAFNOSUPPORT)
798           status = SNMP_CLASS_TL_UNSUPPORTED;
799         else if (errno == ENETUNREACH)
800           status = SNMP_CLASS_TL_FAILED;
801         else if (errno == EACCES)
802           status = SNMP_CLASS_TL_ACCESS_DENIED;
803         else
804         {
805           debugprintf(0, "Uncatched errno value %d, returning internal error.",
806                       errno);
807           status = SNMP_CLASS_INTERNAL_ERROR;
808         }
809 #endif
810         close(iv_snmp_session);    // close the dynamic socket
811         iv_snmp_session = INVALID_SOCKET;
812       }
813       else
814       {
815         status = SNMP_CLASS_SUCCESS;
816 #ifdef SNMP_BROADCAST
817         int enable_broadcast = 1;
818         setsockopt(iv_snmp_session, SOL_SOCKET, SO_BROADCAST,
819                    (char*)&enable_broadcast, sizeof(enable_broadcast));
820 #endif
821       }
822     }
823     if (status != SNMP_CLASS_SUCCESS)
824       return;
825   }
826
827   /* Open IPv6 socket */
828   if (addresses[1])
829   {
830 #ifdef SNMP_PP_IPv6
831     // open a socket to be used for the session
832     if (( iv_snmp_session_ipv6 = socket( AF_INET6, SOCK_DGRAM,0)) 
833         == INVALID_SOCKET)
834     {
835 #ifdef WIN32
836       int werr = WSAGetLastError();
837       if (EMFILE == werr ||WSAENOBUFS == werr || ENFILE == werr)
838         status = SNMP_CLASS_RESOURCE_UNAVAIL;
839       else if (WSAEHOSTDOWN == werr)
840         status = SNMP_CLASS_TL_FAILED;
841       else
842         status = SNMP_CLASS_TL_UNSUPPORTED;
843 #else
844       if (EMFILE == errno || ENOBUFS == errno || ENFILE == errno)
845         status = SNMP_CLASS_RESOURCE_UNAVAIL;
846       else if (EHOSTDOWN == errno)
847         status = SNMP_CLASS_TL_FAILED;
848       else
849         status = SNMP_CLASS_TL_UNSUPPORTED;
850 #endif
851     }
852     else
853     {
854       // set up the manager socket attributes
855       struct sockaddr_in6 mgr_addr;
856       memset(&mgr_addr, 0, sizeof(mgr_addr));
857       unsigned int scope = 0;
858     
859       OctetStr addrstr = addresses[1]->get_printable();
860
861       if (addresses[1]->has_ipv6_scope())
862       {
863         scope = addresses[1]->get_scope();
864
865         int i = addrstr.len() - 1;
866         while ((i>0) && (addrstr[i] != '%'))
867         {
868             addrstr.set_len(addrstr.len() - 1);
869             i--;
870         }
871         if (addrstr[i] == '%')
872             addrstr.set_len(addrstr.len() - 1);
873       }
874       if (inet_pton(AF_INET6, addrstr.get_printable(),
875                     &mgr_addr.sin6_addr) < 0)
876       {
877         LOG_BEGIN(ERROR_LOG | 1);
878         LOG("Snmp transport: inet_pton returns (errno) (str)");
879         LOG(errno);
880         LOG(strerror(errno));
881         LOG_END;
882         status = SNMP_CLASS_INVALID_ADDRESS;
883         return;
884       }
885
886       mgr_addr.sin6_family = AF_INET6;
887       mgr_addr.sin6_port = htons( port_v6);
888       mgr_addr.sin6_scope_id = scope;
889       // bind the socket
890       if (bind(iv_snmp_session_ipv6, (struct sockaddr*) &mgr_addr,
891                sizeof(mgr_addr)) < 0)
892       {
893 #ifdef WIN32
894         int werr = WSAGetLastError();
895         if (WSAEADDRINUSE  == werr)
896           status = SNMP_CLASS_TL_IN_USE;
897         else if (WSAENOBUFS == werr)
898           status = SNMP_CLASS_RESOURCE_UNAVAIL;
899         else if (werr == WSAEAFNOSUPPORT)
900           status = SNMP_CLASS_TL_UNSUPPORTED;
901         else if (werr == WSAENETUNREACH)
902           status = SNMP_CLASS_TL_FAILED;
903         else if (werr == EACCES)
904           status = SNMP_CLASS_TL_ACCESS_DENIED;
905         else
906           status = SNMP_CLASS_INTERNAL_ERROR;
907 #else
908         if (EADDRINUSE  == errno)
909           status = SNMP_CLASS_TL_IN_USE;
910         else if (ENOBUFS == errno)
911           status = SNMP_CLASS_RESOURCE_UNAVAIL;
912         else if (errno == EAFNOSUPPORT)
913           status = SNMP_CLASS_TL_UNSUPPORTED;
914         else if (errno == ENETUNREACH)
915           status = SNMP_CLASS_TL_FAILED;
916         else if (errno == EACCES)
917           status = SNMP_CLASS_TL_ACCESS_DENIED;
918         else
919           status = SNMP_CLASS_INTERNAL_ERROR;
920 #endif
921         close(iv_snmp_session_ipv6);    // close the dynamic socket
922         iv_snmp_session_ipv6 = INVALID_SOCKET;
923       }
924       else
925       {
926         status = SNMP_CLASS_SUCCESS;
927 #ifdef SNMP_BROADCAST
928         int enable_broadcast = 1;
929         setsockopt(iv_snmp_session_ipv6, SOL_SOCKET, SO_BROADCAST,
930                    (char*)&enable_broadcast, sizeof(enable_broadcast));
931 #endif
932       }
933     }
934 #else
935     debugprintf(0, "User error: Enable IPv6 and recompile snmp++.");
936 #endif // SNMP_PP_IPv6
937   }
938   return;
939 }
940
941
942 //---------[ Snmp Class Destructor ]----------------------------------
943 Snmp::~Snmp()
944 {
945   stop_poll_thread();
946
947 #ifdef _THREADS
948 #ifdef WIN32
949   ::CloseHandle(m_hThreadEndEvent);
950 #endif
951 #endif
952
953   // if we failed during construction then don't try
954   // to free stuff up that was not allocated
955   if (iv_snmp_session != INVALID_SOCKET)
956   {
957     // go through the snmpEventList and delete any outstanding
958     // events on this socket
959     eventListHolder->snmpEventList()->DeleteSocketEntry(iv_snmp_session);
960
961     close(iv_snmp_session);    // close the dynamic socket
962   }
963   // if we failed during construction then don't try
964   // to free stuff up that was not allocated
965
966 #ifdef SNMP_PP_IPv6
967   if (iv_snmp_session_ipv6 != INVALID_SOCKET)
968   {
969     // go through the snmpEventList and delete any outstanding
970     // events on this socket
971     eventListHolder->snmpEventList()->DeleteSocketEntry(iv_snmp_session_ipv6);
972
973     close(iv_snmp_session_ipv6);    // close the dynamic socket
974   }
975 #endif
976
977   // shut down trap reception if used
978   notify_unregister();
979
980   delete eventListHolder;
981 }
982
983 // Get the version of the snmp++ library at runtime
984 // This function MUST stay in the cpp file!
985 const char *Snmp::get_version()
986 {
987   return SNMP_PP_VERSION_STRING;
988 }
989
990 //-------------------[ returns error string ]--------------------------
991 const char *Snmp::error_msg(const int c)
992 {
993 #ifdef _SNMPv3
994   if (c>=SNMPv3_USM_MIN_ERROR)
995     return ((c>SNMPv3_USM_MAX_ERROR)?pv3Errs[SNMPv3_USM_ERRORCOUNT]:pv3Errs[c-SNMPv3_USM_MIN_ERROR]);
996   if (c<=SNMPv3_MP_MAX_ERROR)
997     return ((c<SNMPv3_MP_MIN_ERROR)?nv3Errs[SNMPv3_MP_ERRORCOUNT]:nv3Errs[SNMPv3_MP_MAX_ERROR - c]);
998 #endif
999   return ((c<0)?
1000           ((c<MAX_NEG_ERROR)?nErrs[-(MAX_NEG_ERROR)+1]:nErrs[-c]):
1001           ((c>MAX_POS_ERROR)?pErrs[MAX_POS_ERROR+1]:pErrs[c]));
1002 }
1003
1004 #ifdef _SNMPv3
1005 const char* Snmp::error_msg(const Oid& v3Oid)
1006 {
1007   // UsmStats
1008   if (v3Oid == oidUsmStatsUnsupportedSecLevels)
1009     return error_msg(SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL);
1010
1011   if (v3Oid == oidUsmStatsNotInTimeWindows)
1012     return error_msg(SNMPv3_USM_NOT_IN_TIME_WINDOW);
1013
1014   if (v3Oid == oidUsmStatsUnknownUserNames )
1015     return error_msg(SNMPv3_USM_UNKNOWN_SECURITY_NAME);
1016
1017   if (v3Oid == oidUsmStatsUnknownEngineIDs)
1018     return error_msg(SNMPv3_USM_UNKNOWN_ENGINEID);
1019
1020   if (v3Oid == oidUsmStatsWrongDigests)
1021     return error_msg(SNMPv3_USM_AUTHENTICATION_FAILURE);
1022
1023   if (v3Oid == oidUsmStatsDecryptionErrors)
1024     return error_msg(SNMPv3_USM_DECRYPTION_ERROR);
1025
1026   // MPDstats
1027   if (v3Oid == oidSnmpUnknownSecurityModels)
1028     return error_msg(SNMPv3_MP_UNSUPPORTED_SECURITY_MODEL);
1029
1030   if (v3Oid == oidSnmpInvalidMsgs)
1031     return error_msg(SNMPv3_MP_INVALID_MESSAGE);
1032
1033   if (v3Oid == oidSnmpUnknownPDUHandlers)
1034     return error_msg(SNMPv3_MP_UNKNOWN_PDU_HANDLERS);
1035
1036   if (v3Oid == oidSnmpUnavailableContexts)
1037     return error_msg(SNMPv3_MP_UNAVAILABLE_CONTEXT);
1038
1039   if (v3Oid == oidSnmpUnknownContexts)
1040     return error_msg(SNMPv3_MP_UNKNOWN_CONTEXT);
1041
1042   return error_msg(MAX_POS_ERROR + 1);
1043 }
1044 #endif
1045
1046 //------------------------[ get ]---------------------------------------
1047 int Snmp::get(Pdu &pdu, const SnmpTarget &target)
1048 {
1049   pdu.set_type( sNMP_PDU_GET);
1050   return snmp_engine( pdu, 0, 0, target, NULL, 0);
1051 }
1052
1053 //------------------------[ get async ]----------------------------------
1054 int Snmp::get(Pdu &pdu, const SnmpTarget &target,
1055               const snmp_callback callback,
1056               const void * callback_data)
1057 {
1058   pdu.set_type( sNMP_PDU_GET_ASYNC);
1059   return snmp_engine( pdu, 0, 0, target, callback, callback_data);
1060 }
1061
1062 //------------------------[ get next ]-----------------------------------
1063 int Snmp::get_next(Pdu &pdu, const SnmpTarget &target)
1064 {
1065   pdu.set_type( sNMP_PDU_GETNEXT);
1066   return snmp_engine( pdu, 0, 0, target, NULL, 0);
1067 }
1068
1069 //------------------------[ get next async ]-----------------------------
1070 int Snmp::get_next(Pdu &pdu, const SnmpTarget &target,
1071                    const snmp_callback callback,
1072                    const void * callback_data)
1073 {
1074   pdu.set_type( sNMP_PDU_GETNEXT_ASYNC);
1075   return snmp_engine( pdu, 0, 0, target, callback, callback_data);
1076 }
1077
1078 //-------------------------[ set ]---------------------------------------
1079 int Snmp::set(Pdu &pdu, const SnmpTarget &target)
1080 {
1081   pdu.set_type( sNMP_PDU_SET);
1082   return snmp_engine( pdu, 0, 0, target, NULL, 0);
1083 }
1084
1085 //------------------------[ set async ]----------------------------------
1086 int Snmp::set(Pdu &pdu, const SnmpTarget &target,
1087               const snmp_callback callback,
1088               const void * callback_data)
1089 {
1090   pdu.set_type( sNMP_PDU_SET_ASYNC);
1091   return snmp_engine( pdu, 0, 0, target, callback, callback_data);
1092 }
1093
1094 //-----------------------[ get bulk ]------------------------------------
1095 int Snmp::get_bulk(Pdu &pdu,                // pdu to use
1096                    const SnmpTarget &target,// destination target
1097                    const int non_repeaters, // number of non repeaters
1098                    const int max_reps)      // maximum number of repetitions
1099 {
1100   pdu.set_type( sNMP_PDU_GETBULK);
1101   return snmp_engine( pdu, non_repeaters, max_reps, target, NULL, 0);
1102 }
1103
1104 //-----------------------[ get bulk async ]------------------------------
1105 int Snmp::get_bulk(Pdu &pdu,                 // pdu to use
1106                    const SnmpTarget &target, // destination target
1107                    const int non_repeaters,  // number of non repeaters
1108                    const int max_reps,       // maximum number of repetitions
1109                    const snmp_callback callback,// callback to use
1110                    const void * callback_data)  // callback data
1111 {
1112   pdu.set_type( sNMP_PDU_GETBULK_ASYNC);
1113   return snmp_engine( pdu, non_repeaters, max_reps, target,
1114                       callback, callback_data);
1115 }
1116
1117 //------------------------[ inform_response ]----------------------------
1118 int Snmp::response(Pdu &pdu,                 // pdu to use
1119                    const SnmpTarget &target, // response target
1120                    const SnmpSocket fd)
1121 {
1122   pdu.set_type( sNMP_PDU_RESPONSE);
1123   return snmp_engine(pdu, 0, 0, target, NULL, 0, fd);
1124 }
1125
1126 int Snmp::send_raw_data(unsigned char *send_buf,
1127                         size_t send_len, UdpAddress &address, SnmpSocket fd)
1128 {
1129   // REENTRANT() removed because of #ifdef
1130   SnmpSynchronize _synchronize(*this);
1131
1132   if (fd != INVALID_SOCKET)
1133     return send_snmp_request(fd, send_buf, send_len, address);
1134   else
1135   {
1136 #ifdef SNMP_PP_IPv6
1137     if (address.get_ip_version() == Address::version_ipv4)
1138     {
1139       if (iv_snmp_session != INVALID_SOCKET)
1140         return send_snmp_request(iv_snmp_session, send_buf,
1141                                  send_len, address);
1142       else
1143         address.map_to_ipv6();
1144     }
1145     return send_snmp_request(iv_snmp_session_ipv6, send_buf,
1146                              send_len, address);
1147 #else
1148     return send_snmp_request(iv_snmp_session, send_buf,
1149                              send_len, address);
1150 #endif
1151
1152   }
1153 }
1154
1155 //-----------------------[ cancel ]--------------------------------------
1156 int Snmp::cancel(const unsigned long request_id)
1157 {
1158   eventListHolder->snmpEventList()->lock();
1159   int status = eventListHolder->snmpEventList()->DeleteEntry(request_id);
1160   eventListHolder->snmpEventList()->unlock();
1161
1162   return status;
1163 }
1164
1165
1166 //----------------------[ sending report, V3 only]-----------------------
1167 int Snmp::report(Pdu &pdu,                // pdu to send
1168                  const SnmpTarget &target)// destination target
1169 {
1170   pdu.set_type( sNMP_PDU_REPORT);
1171   return snmp_engine( pdu, 0, 0, target, NULL, 0);
1172 }
1173
1174 //----------------------[ blocking inform, V2 only]------------------------
1175 int Snmp::inform(Pdu &pdu,                // pdu to send
1176                  const SnmpTarget &target)// destination target
1177 {
1178   if (target.get_version() == version1)
1179   {
1180     LOG_BEGIN(ERROR_LOG | 1);
1181     LOG("Snmp: Invalid Operation: Inform not defined for SNMPv1");
1182     LOG_END;
1183
1184     return SNMP_CLASS_INVALID_OPERATION;
1185   }
1186
1187   pdu.set_type( sNMP_PDU_INFORM);
1188   check_notify_timestamp(pdu);
1189   return snmp_engine( pdu, 0, 0, target, NULL, 0);
1190 }
1191
1192 //----------------------[ asynch inform, V2 only]------------------------
1193 int Snmp::inform(Pdu &pdu,                // pdu to send
1194                  const SnmpTarget &target,      // destination target
1195                  const snmp_callback callback,  // callback function
1196                  const void * callback_data)    // callback data
1197 {
1198   if (target.get_version() == version1)
1199   {
1200     LOG_BEGIN(ERROR_LOG | 1);
1201     LOG("Snmp: Invalid Operation: Inform not defined for SNMPv1");
1202     LOG_END;
1203
1204     return SNMP_CLASS_INVALID_OPERATION;
1205   }
1206
1207   pdu.set_type(sNMP_PDU_INFORM_ASYNC);
1208   check_notify_timestamp(pdu);
1209   return snmp_engine( pdu, 0, 0, target, callback, callback_data);
1210 }
1211
1212
1213 //---------------------[ send a trap ]-----------------------------------
1214 int Snmp::trap(Pdu &pdu,                        // pdu to send
1215                const SnmpTarget &target)        // destination target
1216 {
1217   OctetStr my_get_community;
1218   OctetStr my_set_community;
1219   GenAddress address;
1220   unsigned long my_timeout;
1221   int my_retry;
1222   unsigned char version;
1223   int status;
1224
1225   debugprintf(1, "++ SNMP++, Send a Trap");
1226   //---------[ make sure pdu is valid ]---------------------------------
1227   if (!pdu.valid())
1228   {
1229     debugprintf(0, "-- SNMP++, PDU Object Invalid");
1230     return  SNMP_CLASS_INVALID_PDU;
1231   }
1232
1233   //---------[ make sure target is valid ]------------------------------
1234   if (!target.valid())
1235   {
1236     debugprintf(0, "-- SNMP++, Target Object Invalid");
1237     return SNMP_CLASS_INVALID_TARGET;
1238   }
1239
1240   CTarget* ctarget = NULL;
1241   UTarget* utarget = NULL;
1242   OctetStr security_name;
1243   int security_model;
1244
1245   switch (target.get_type()) {
1246     case SnmpTarget::type_ctarget:
1247       ctarget = (CTarget*)(&target);
1248       break;
1249     case SnmpTarget::type_utarget:
1250       utarget = (UTarget*)(&target);
1251       break;
1252     case SnmpTarget::type_base:
1253       debugprintf(0, "-- SNMP++, do not use SnmpTarget, use a  CTarget or UTarget");
1254       return SNMP_CLASS_INVALID_TARGET;
1255     default:
1256       // target is not known
1257       debugprintf(0, "-- SNMP++, type of target is unknown!");
1258       return SNMP_CLASS_UNSUPPORTED;
1259   }
1260
1261   if (ctarget) {
1262     debugprintf(3, "snmp::trap called with CTarget");
1263     if (!ctarget->resolve_to_C( my_get_community, my_set_community, address,
1264                                 my_timeout, my_retry, version))
1265     {
1266       debugprintf(0, "-- SNMP++, Resolve Fail (CTarget)");
1267       return SNMP_CLASS_UNSUPPORTED;
1268     }
1269 #ifdef _SNMPv3
1270     if (version == version3)
1271     {
1272       debugprintf(0, "-- SNMP++, use UTarget for SNMPv3");
1273       return SNMP_CLASS_INVALID_TARGET;
1274     }
1275 #endif
1276   }
1277   else { // target is not a CTarget:
1278     if (utarget) {
1279       debugprintf(3, "trap called with UTarget");
1280       if (!utarget->resolve_to_U( security_name, security_model, address,
1281                                   my_timeout, my_retry, version))
1282       {
1283         debugprintf(0, "-- SNMP++, Resolve Fail (UTarget)");
1284         return SNMP_CLASS_UNSUPPORTED;
1285       }
1286 #ifdef _SNMPv3
1287       if (version != version3) {
1288 #endif
1289         my_get_community = security_name;
1290         if ((security_model != SNMP_SECURITY_MODEL_V1) &&
1291             (security_model != SNMP_SECURITY_MODEL_V2)) {
1292           debugprintf(0, "-- SNMP++, Target contains invalid security_model/version combination");
1293           return SNMP_CLASS_INVALID_TARGET;
1294         }
1295 #ifdef _SNMPv3
1296       } // end if (version != version3)
1297 #endif
1298     }
1299     else { // target is neither CTarget nor UTarget:
1300       debugprintf(0, "-- SNMP++, Resolve Fail");
1301       return SNMP_CLASS_INVALID_TARGET;
1302     }
1303   }
1304
1305   //--------[ determine request id to use ]------------------------------
1306   pdu.set_request_id( MyMakeReqId());
1307
1308   //--------[ check timestamp, if null use system time ]-----------------
1309   check_notify_timestamp(pdu);
1310
1311   //------[ validate address to use ]-------------------------------------
1312   if (!address.valid()) {
1313     debugprintf(0, "-- SNMP++, Bad address");
1314     return SNMP_CLASS_INVALID_TARGET;
1315   }
1316
1317   if ((address.get_type() != Address::type_ip) &&
1318       (address.get_type() != Address::type_udp) )
1319     {
1320       debugprintf(0, "-- SNMP++, Bad address type");
1321       return SNMP_CLASS_TL_UNSUPPORTED;
1322     }
1323
1324   UdpAddress udp_address(address);
1325   if (!udp_address.valid()) {
1326     debugprintf(0, "-- SNMP++, copy address failed");
1327     return SNMP_CLASS_RESOURCE_UNAVAIL;
1328   }
1329
1330   //----------[ choose the target address port ]-----------------------
1331   if ((address.get_type() == Address::type_ip) || !udp_address.get_port())
1332     udp_address.set_port(SNMP_TRAP_PORT);
1333
1334   //----------[ based on the target type, choose v1 or v1 trap type ]-----
1335   if ( version == version1)
1336     pdu.set_type( sNMP_PDU_V1TRAP);
1337   else // v2 and v3 use v2TRAP
1338     pdu.set_type( sNMP_PDU_TRAP);
1339
1340   SnmpMessage snmpmsg;
1341
1342 #ifdef _SNMPv3
1343   if ( version == version3) {
1344
1345     OctetStr engine_id = v3MP::I->get_local_engine_id();
1346     if (!utarget) {
1347       debugprintf(0, "-- SNMP++, dont know how to handle SNMPv3 without UTarget!");
1348       return SNMP_CLASS_INVALID_TARGET;
1349     }
1350
1351     // set context_engine_id of pdu, if it is not set
1352     if (pdu.get_context_engine_id().len() == 0)
1353     {
1354       debugprintf(8, "Setting contextEngineID of Pdu to (%s)",
1355                   engine_id.get_printable());
1356       pdu.set_context_engine_id(engine_id);
1357     }
1358
1359     debugprintf(4,"Snmp::trap:");
1360     debugprintf(4," engineID (%s), securityName (%s)\n securityModel (%i) security_level (%i)",
1361                 engine_id.get_printable(), security_name.get_printable(),
1362                 security_model, pdu.get_security_level());
1363     debugprintf(4," Addr/Port (%s)",udp_address.get_printable());
1364
1365     status = snmpmsg.loadv3( pdu, engine_id, security_name,
1366                              security_model, (snmp_version)version);
1367   }
1368   else
1369 #endif
1370     status = snmpmsg.load( pdu, my_get_community, (snmp_version) version);
1371
1372   if ( status != SNMP_CLASS_SUCCESS) {
1373     debugprintf(0, "snmp message load error!");
1374     return status;
1375   }
1376
1377   lock();
1378   //------[ send the trap ]
1379 #ifdef SNMP_PP_IPv6
1380   if (udp_address.get_ip_version() == Address::version_ipv4)
1381   {
1382     if (iv_snmp_session != INVALID_SOCKET)
1383       status = send_snmp_request(iv_snmp_session,
1384                                  snmpmsg.data(), (size_t)snmpmsg.len(),
1385                                  udp_address);
1386     else
1387     {
1388       udp_address.map_to_ipv6();
1389       status = send_snmp_request(iv_snmp_session_ipv6,
1390                                  snmpmsg.data(), (size_t)snmpmsg.len(),
1391                                  udp_address);
1392     }
1393   }
1394   else
1395     status = send_snmp_request(iv_snmp_session_ipv6,
1396                                snmpmsg.data(), (size_t)snmpmsg.len(),
1397                                udp_address);
1398 #else
1399   status = send_snmp_request(iv_snmp_session, snmpmsg.data(),
1400                              (size_t)snmpmsg.len(), udp_address);
1401 #endif
1402
1403   unlock();
1404   if (status != 0)
1405     return SNMP_CLASS_TL_FAILED;
1406
1407   return SNMP_CLASS_SUCCESS;
1408 }
1409
1410 //----------------[ set notify_timestamp if it is null ]-------------
1411 #if defined (CPU) && CPU == PPC603
1412
1413   struct SCommTimer
1414   {
1415         unsigned long NumMS;
1416         unsigned long FractMS;
1417   };
1418
1419   extern "C"
1420   {
1421   void GetTime (struct SCommTimer *  Time);
1422   }
1423 #endif
1424
1425 void Snmp::check_notify_timestamp(Pdu &pdu)
1426 {
1427   // As we don't know, when the application was started,
1428   // use a continuously increasing notify_timestamp
1429   TimeTicks timestamp;
1430   pdu.get_notify_timestamp( timestamp);
1431   if (timestamp <= 0)
1432   {
1433 #ifdef WIN32
1434     struct _timeb timebuffer;
1435     _ftime( &timebuffer );
1436     timebuffer.time -= 1103760000;   // knock off 35 years worth of seconds
1437     timestamp = SAFE_ULONG_CAST((timebuffer.time * 100) +
1438                                 (timebuffer.millitm / 10));
1439 #elif defined (CPU) && CPU == PPC603
1440     SCommTimer theTime;
1441
1442     GetTime(&theTime); // This function must be defined by the application
1443
1444     timestamp = theTime.NumMS/10;
1445 #else
1446     struct timeval tp;
1447     gettimeofday(&tp, NULL);
1448     tp.tv_sec -= 1103760000;   // knock off 35 years worth of seconds
1449     timestamp = (tp.tv_sec * 100) + (tp.tv_usec / 10000);
1450 #endif
1451
1452     pdu.set_notify_timestamp( timestamp);
1453   }
1454 }
1455
1456 //-----------------------[ read the notification filters ]----------------
1457 int Snmp::get_notify_filter(OidCollection &trapids,
1458                             TargetCollection &targets)
1459 {
1460   CNotifyEvent *e = eventListHolder->notifyEventList()->GetEntry(this);
1461
1462   if (!e)  return SNMP_CLASS_INVALID;
1463
1464   e->get_filter(trapids, targets);
1465
1466   return SNMP_CLASS_SUCCESS;
1467 }
1468
1469 // Set the port for listening to traps and informs.
1470 void Snmp::notify_set_listen_port(const int port)
1471 {
1472   eventListHolder->notifyEventList()->set_listen_port(port);
1473 }
1474
1475 // Get the port that is used for listening to traps and informs.
1476 int Snmp::notify_get_listen_port()
1477 {
1478   return eventListHolder->notifyEventList()->get_listen_port();
1479 }
1480
1481 //-----------------------[ register to get traps]-------------------------
1482 int Snmp::notify_register(const OidCollection     &trapids,
1483                           const TargetCollection  &targets,
1484                           const snmp_callback      callback,
1485                           const void              *callback_data)
1486 {
1487   // remove any previous filters for this session
1488   notify_unregister();
1489
1490   // assign callback and callback data info
1491   notifycallback = callback;
1492   notifycallback_data = (void *)callback_data;
1493
1494   // add to the notify queue
1495   return eventListHolder->notifyEventList()->AddEntry(this, trapids, targets);
1496 }
1497
1498 //-----------------------[ un-register to get traps]----------------------
1499 int Snmp::notify_unregister()
1500 {
1501   // remove from the notify queue
1502   eventListHolder->notifyEventList()->DeleteEntry(this);
1503
1504   // null out callback information
1505   notifycallback = 0;
1506   notifycallback_data = 0;
1507
1508   return SNMP_CLASS_SUCCESS;
1509 }
1510
1511 //---------[ get / set engine ]-----------------------------------------
1512 // The main snmp engine used for all requests
1513 // async requests return out early and don't wait in here for
1514 // the response
1515 int Snmp::snmp_engine( Pdu &pdu,              // pdu to use
1516                        long int non_reps,     // # of non repititions
1517                        long int max_reps,     // # of max repititions
1518                        const SnmpTarget &target,    // from this target
1519                        const snmp_callback cb,// callback for async calls
1520                        const void *cbd,      // callback data
1521                        SnmpSocket fd,
1522                        int reports_received)
1523
1524 {
1525   long req_id = 0;                   // pdu request id
1526   int status;                        // send status
1527
1528 #ifdef _SNMPv3
1529   // save original PDU for later reference
1530   Pdu backupPdu = pdu;
1531
1532   for (int maxloops=0; maxloops<3; maxloops++)
1533   {
1534 #endif
1535
1536     unsigned short pdu_action;        // type of pdu to build
1537     unsigned short action;        // type of pdu to build
1538     unsigned long my_timeout;        // target specific timeout
1539     int my_retry;                // target specific retry
1540
1541     OctetStr my_get_community;
1542     OctetStr my_set_community;
1543     GenAddress address;
1544     unsigned char version;
1545
1546     //---------[ make sure pdu is valid ]--------------------------
1547     if ( !pdu.valid())
1548       return  SNMP_CLASS_INVALID_PDU;
1549
1550     //---------[ depending on user action, map the correct pdu action]
1551     action = pdu.get_type();
1552     map_action(action, pdu_action);
1553
1554     //---------[ check for correct mode ]---------------------------
1555     // if the class was constructed as a blocked model, callback=0
1556     // and async calls are attempted, an error is returned
1557     if (( cb == 0) &&
1558         ((action == sNMP_PDU_GET_ASYNC) ||
1559          (action == sNMP_PDU_SET_ASYNC) ||
1560          (action == sNMP_PDU_GETNEXT_ASYNC) ||
1561          (action == sNMP_PDU_GETBULK_ASYNC) ||
1562          (action == sNMP_PDU_INFORM_ASYNC)))
1563       return SNMP_CLASS_INVALID_CALLBACK;
1564
1565     //---------[ more mode checking ]--------------------------------
1566     // if the class was constructed as an async model, callback = something
1567     // and blocked calls are attempted, an error is returned
1568     if (( cb != 0) &&
1569         ((action == sNMP_PDU_GET) ||
1570          (action == sNMP_PDU_SET) ||
1571          (action == sNMP_PDU_GETNEXT) ||
1572          (action == sNMP_PDU_GETBULK) ||
1573          (action == sNMP_PDU_INFORM)))
1574       return SNMP_CLASS_INVALID_CALLBACK;
1575
1576     //---------[ make sure target is valid ]-------------------------
1577     // make sure that the target is valid
1578     if ( ! target.valid())
1579       return SNMP_CLASS_INVALID_TARGET;
1580
1581     OctetStr community_string;
1582     OctetStr security_name;
1583     int security_model;
1584     const CTarget* ctarget = NULL;
1585     const UTarget* utarget = NULL;
1586
1587     switch (target.get_type())
1588     {
1589       case SnmpTarget::type_ctarget:
1590         ctarget = (CTarget*)(&target);
1591         break;
1592       case SnmpTarget::type_utarget:
1593         utarget = (UTarget*)(&target);
1594         break;
1595       case SnmpTarget::type_base:
1596         debugprintf(0, "-- SNMP++, do not use SnmpTarget,"
1597                     "use a  CTarget or UTarget");
1598         return SNMP_CLASS_INVALID_TARGET;
1599       default:
1600         /* target is not known */
1601         debugprintf(0, "-- SNMP++, type of target is unknown!");
1602         return SNMP_CLASS_UNSUPPORTED;
1603     }
1604
1605     if (ctarget) /* Is is a CTarget? */
1606     {
1607       debugprintf(3, "snmp_engine called with CTarget");
1608       if (!ctarget->resolve_to_C( my_get_community, my_set_community,
1609                                   address, my_timeout, my_retry, version))
1610       {
1611         debugprintf(0, "-- SNMP++, Resolve Fail (CTarget)");
1612         return SNMP_CLASS_UNSUPPORTED;
1613       }
1614 #ifdef _SNMPv3
1615       if ((version == version3) ||
1616           (action == sNMP_PDU_REPORT))
1617       {
1618         debugprintf(0, "-- SNMP++, use UTarget for SNMPv3");
1619         return SNMP_CLASS_INVALID_TARGET;
1620       }
1621 #endif
1622       //----------[ use the appropriate community string ]-----------------
1623       if (( action == sNMP_PDU_GET) ||
1624           ( action == sNMP_PDU_GET_ASYNC) ||
1625           ( action == sNMP_PDU_GETNEXT) ||
1626           ( action == sNMP_PDU_GETNEXT_ASYNC) ||
1627           ( action == sNMP_PDU_GETBULK) ||
1628           ( action == sNMP_PDU_GETBULK_ASYNC) ||
1629           ( action == sNMP_PDU_INFORM) ||
1630           ( action == sNMP_PDU_INFORM_ASYNC) ||
1631           ( action == sNMP_PDU_RESPONSE))
1632         community_string = my_get_community;
1633       else /* got to be a set */
1634         community_string = my_set_community;
1635     }
1636     else if (utarget)  /* Is is a UTarget? */
1637     {
1638       debugprintf(3, "snmp_engine called with UTarget");
1639       if (!utarget->resolve_to_U( security_name, security_model,
1640                                   address, my_timeout,
1641                                   my_retry, version))
1642       {
1643         debugprintf(0, "-- SNMP++, Resolve Fail (UTarget)");
1644         return SNMP_CLASS_UNSUPPORTED;
1645       }
1646 #ifdef _SNMPv3
1647       if (version != version3)
1648       {
1649 #endif
1650         community_string = security_name;
1651         if (((version == version1) && (security_model != SNMP_SECURITY_MODEL_V1)) ||
1652             ((version == version2c) && (security_model != SNMP_SECURITY_MODEL_V2)))
1653         {
1654           LOG_BEGIN(ERROR_LOG | 1);
1655           LOG("Snmp: Target does not match SNMP version: (security model) (version)");
1656           LOG(security_model);
1657           LOG(version);
1658           LOG_END;
1659
1660           return SNMP_CLASS_INVALID_TARGET;
1661         }
1662 #ifdef _SNMPv3
1663       } // end if (version != version3)
1664 #endif
1665     }
1666     else
1667     { // target is neither CTarget nor UTarget (should not happen)
1668       debugprintf(0, "-- SNMP++, Resolve Fail");
1669       return SNMP_CLASS_INVALID_TARGET;
1670     }
1671
1672     if (!address.valid())
1673     {
1674       debugprintf(0, "-- SNMP++, Target contains invalid address");
1675       return SNMP_CLASS_INVALID_TARGET;
1676     }
1677
1678     //----------[ validate the target address ]--------------------------
1679     if ((address.get_type() != Address::type_ip) &&
1680         (address.get_type() != Address::type_udp) )
1681     {
1682       debugprintf(0, "-- SNMP++, Bad address type");
1683       return SNMP_CLASS_TL_UNSUPPORTED;
1684     }
1685
1686     UdpAddress udp_address(address);
1687     if (!udp_address.valid())
1688     {
1689       debugprintf(0, "-- SNMP++, Bad address");
1690       return SNMP_CLASS_RESOURCE_UNAVAIL;
1691     }
1692
1693     //----------[ choose the target address port ]-----------------------
1694     if ((address.get_type() == Address::type_ip) || !udp_address.get_port())
1695     {
1696       if (pdu_action == sNMP_PDU_INFORM)
1697         udp_address.set_port(SNMP_TRAP_PORT);
1698       else
1699         udp_address.set_port(SNMP_PORT);
1700     }
1701     // otherwise port was already set
1702
1703     // check socket to use
1704     SnmpSocket iv_session_used = fd;
1705
1706     if (fd == INVALID_SOCKET)
1707     {
1708 #ifdef SNMP_PP_IPv6
1709       if (udp_address.get_ip_version() == Address::version_ipv4)
1710       {
1711         if (iv_snmp_session != INVALID_SOCKET)
1712           iv_session_used = iv_snmp_session;
1713         else
1714         {
1715           udp_address.map_to_ipv6();
1716           iv_session_used = iv_snmp_session_ipv6;
1717         }
1718       }
1719       else
1720         iv_session_used = iv_snmp_session_ipv6;
1721 #else
1722       iv_session_used = iv_snmp_session;
1723 #endif
1724     }
1725
1726     if ((pdu_action != sNMP_PDU_RESPONSE) &&
1727         (pdu_action != sNMP_PDU_REPORT))
1728     {
1729       // set error index to none
1730       pdu.set_error_index(0);
1731
1732       // determine request id to use
1733       req_id = MyMakeReqId();
1734       pdu.set_request_id(req_id);
1735     }
1736
1737     //---------[ map GetBulk over v1 to GetNext ]-------------------------
1738     if (( pdu_action == sNMP_PDU_GETBULK)&&( (snmp_version)version== version1))
1739       pdu_action = sNMP_PDU_GETNEXT;
1740     if ( pdu_action == sNMP_PDU_GETBULK) {
1741       pdu.set_error_status((int) non_reps);
1742       pdu.set_error_index((int) max_reps);
1743     }
1744
1745     pdu.set_type( pdu_action);
1746     SnmpMessage snmpmsg;
1747
1748 #ifdef _SNMPv3
1749     struct V3CallBackData *v3CallBackData = 0;
1750
1751     if (version == version3)
1752     {
1753       if (!utarget)
1754       {
1755         debugprintf(0, "-- SNMP++, need UTarget to send SNMPv3 message!");
1756         return SNMP_CLASS_INVALID_TARGET;
1757       }
1758       OctetStr engine_id;
1759       utarget->get_engine_id(engine_id);
1760       if (engine_id.len() == 0)
1761       {
1762         if (v3MP::I->get_from_engine_id_table(engine_id,
1763                                             (char*)udp_address.get_printable())
1764             == SNMPv3_MP_OK )
1765         {
1766           // Override const here
1767           ((UTarget*)utarget)->set_engine_id(engine_id);
1768         }
1769         else
1770         {
1771           // check if engine id discovery is enabled
1772           if ((!v3MP::I->get_usm()->is_discovery_enabled()) &&
1773               ((pdu_action == sNMP_PDU_GET) ||
1774                (pdu_action == sNMP_PDU_SET) ||
1775                (pdu_action == sNMP_PDU_GETNEXT) ||
1776                (pdu_action == sNMP_PDU_GETBULK) ||
1777                (pdu_action == sNMP_PDU_INFORM)))
1778           {
1779             // no engine id, discovery disabled and not authoritytive
1780             LOG_BEGIN(ERROR_LOG | 1);
1781             LOG("Not authoritative and discovery disabled. Target without engine id is invalid");
1782             LOG_END;
1783             return SNMP_CLASS_INVALID_TARGET;
1784           }
1785         }
1786       }
1787       // set context_engine_id of pdu, if it is not set
1788       if (pdu.get_context_engine_id().len() == 0)
1789       {
1790         debugprintf(8, "Setting contextEngineID of Pdu to (%s)",
1791                     engine_id.get_printable());
1792         pdu.set_context_engine_id(engine_id);
1793         backupPdu.set_context_engine_id(engine_id);
1794       }
1795
1796       debugprintf(4,"Snmp::snmp_engine: engineID (%s), securityName (%s)"
1797                   "securityModel (%i) security_level (%i)",
1798                   engine_id.get_printable(), security_name.get_printable(),
1799                   security_model, pdu.get_security_level());
1800       debugprintf(4," Addr/Port (%s)",udp_address.get_printable());
1801
1802       status = snmpmsg.loadv3( pdu, engine_id, security_name,
1803                                security_model, (snmp_version)version);
1804     }
1805     else
1806 #endif
1807       status = snmpmsg.load( pdu, community_string,(snmp_version) version);
1808
1809     if ( status != SNMP_CLASS_SUCCESS)
1810     {
1811       debugprintf(0, "snmp message load error!");
1812       return status;
1813     }
1814
1815     // first add the message to the queue
1816     if ((pdu_action != sNMP_PDU_RESPONSE) &&
1817         (pdu_action != sNMP_PDU_REPORT))
1818     {
1819 #ifdef _SNMPv3
1820         if ((version == version3) && ((action == sNMP_PDU_GET_ASYNC) ||
1821                                       (action == sNMP_PDU_SET_ASYNC) ||
1822                                       (action == sNMP_PDU_GETNEXT_ASYNC) ||
1823                                       (action == sNMP_PDU_GETBULK_ASYNC) ||
1824                                       (action == sNMP_PDU_INFORM_ASYNC))) {
1825             // add callback for v3
1826             v3CallBackData = new struct V3CallBackData;
1827
1828             v3CallBackData->pdu = new Pdu(pdu);
1829             v3CallBackData->pdu->set_type(backupPdu.get_type());
1830             v3CallBackData->non_reps = non_reps;
1831             v3CallBackData->max_reps = max_reps;
1832
1833             v3CallBackData->target = new UTarget(*utarget);
1834             v3CallBackData->oldCallback = cb;
1835             v3CallBackData->cbd = cbd;
1836             v3CallBackData->reports_received = reports_received;
1837
1838             // Add the message to the message queue
1839             eventListHolder->snmpEventList()->AddEntry(req_id, this, iv_session_used,
1840                      target, pdu, snmpmsg.data(), (size_t) snmpmsg.len(),
1841                      udp_address, v3CallBack, (void *)v3CallBackData);
1842         }
1843         else
1844 #endif
1845         {
1846             eventListHolder->snmpEventList()->AddEntry(req_id, this, iv_session_used,
1847                      target, pdu, snmpmsg.data(), (size_t) snmpmsg.len(),
1848                      udp_address, cb, (void *)cbd);
1849         }
1850     }
1851
1852     //------[ send the request ]
1853     lock();
1854     status = send_snmp_request(iv_session_used,
1855                                snmpmsg.data(), (size_t) snmpmsg.len(),
1856                                udp_address);
1857     unlock();
1858
1859     if (status != 0)
1860     {
1861         if ((pdu_action != sNMP_PDU_RESPONSE) &&
1862             (pdu_action != sNMP_PDU_REPORT))
1863         {
1864             // remove the id from message queue
1865             eventListHolder->snmpEventList()->lock();
1866             eventListHolder->snmpEventList()->DeleteEntry(req_id);
1867             eventListHolder->snmpEventList()->unlock();
1868
1869 #ifdef _SNMPv3
1870             // dont forget to delete this
1871             if (v3CallBackData) deleteV3Callback(v3CallBackData);
1872 #endif
1873         }
1874         return SNMP_CLASS_TL_FAILED;
1875     }
1876
1877     if ((pdu_action == sNMP_PDU_RESPONSE) ||
1878         (pdu_action == sNMP_PDU_REPORT))
1879       return SNMP_CLASS_SUCCESS; // don't wait for an answer
1880
1881     //----[ if an async mode request then return success ]-----
1882     if (( action == sNMP_PDU_GET_ASYNC) ||
1883         ( action == sNMP_PDU_SET_ASYNC) ||
1884         ( action == sNMP_PDU_GETNEXT_ASYNC) ||
1885         ( action == sNMP_PDU_GETBULK_ASYNC) ||
1886         ( action == sNMP_PDU_INFORM_ASYNC))
1887       return SNMP_CLASS_SUCCESS;
1888
1889     // Now wait for the response (or timeout) for our message.
1890     // This handles any necessary retries.
1891     status = eventListHolder->SNMPBlockForResponse(req_id, pdu);
1892
1893     if (pdu.get_type() != REPORT_MSG) {
1894 #ifdef _SNMPv3
1895       if (status == SNMPv3_MP_OK)
1896         return SNMP_CLASS_SUCCESS;
1897       else
1898 #endif
1899         return status;
1900     }
1901 #ifdef _SNMPv3
1902     else
1903       if (status == SNMPv3_USM_DECRYPTION_ERROR)
1904         return status;
1905
1906     // We received a REPORT-MSG, check if we should try another time
1907     Vb first_vb;
1908     Oid first_oid;
1909     pdu.get_vb(first_vb,0);
1910     first_vb.get_oid(first_oid);
1911
1912     debugprintf(1,"received oid: %s with value: %s",
1913                 first_vb.get_printable_oid(), first_vb.get_printable_value());
1914     debugprintf(1, "%s", error_msg(first_oid));
1915
1916     switch (maxloops)
1917     {
1918       case 0:
1919       {
1920         // This was our first try, so we may receive a unknown engine id 
1921         // report or a not in time window report
1922         if (first_oid == oidUsmStatsUnknownEngineIDs)
1923         {
1924           pdu = backupPdu; // restore pdu and try again
1925           break;
1926         }
1927         else if (first_oid == oidUsmStatsNotInTimeWindows)
1928         {
1929           ++maxloops; // increase it, as the next request must succeed
1930           pdu = backupPdu; // restore pdu and try again
1931           break;
1932         }
1933         return (status == SNMPv3_MP_OK) ? SNMP_CLASS_SUCCESS : status;
1934       }
1935       case 1:
1936       {
1937         // This was the second try, engine id discovery should be ok
1938         // so test only for not in time report
1939         if (first_oid == oidUsmStatsNotInTimeWindows)
1940         {
1941           pdu = backupPdu; // restore pdu and try again
1942           break;
1943         }
1944         return (status == SNMPv3_MP_OK) ? SNMP_CLASS_SUCCESS : status;
1945       }
1946       case 2:
1947       {
1948         // We tried three times: one for engine id discovery, one for
1949         // time sync and we still get a report --> somethings wrong!
1950         return (status == SNMPv3_MP_OK) ? SNMP_CLASS_SUCCESS : status;
1951       }
1952     }
1953   }
1954 #endif
1955   return status;
1956 }
1957
1958 #ifdef _SNMPv3
1959 int Snmp::engine_id_discovery(OctetStr &engine_id,
1960                               const int timeout_sec,
1961                               const UdpAddress &addr)
1962 {
1963   unsigned char *message;
1964   int message_length;
1965   SnmpSocket sock;
1966   SnmpMessage snmpmsg;
1967
1968   unsigned char snmpv3_message[60] = {
1969     0x30, 0x3a,
1970           0x02, 0x01, 0x03,                   // Version: 3
1971           0x30, 0x0f,                         // global header length 15
1972                 0x02, 0x03, 0x01, 0x00, 0x00, // message id
1973                 0x02, 0x02, 0x10, 0x00,       // message max size
1974                 0x04, 0x01, 0x04,             // flags (reportable set)
1975                 0x02, 0x01, 0x03,             // security model USM
1976           0x04, 0x10,                         // security params
1977                 0x30, 0x0e,
1978                       0x04, 0x00,             // no engine id
1979                       0x02, 0x01, 0x00,       // boots 0
1980                       0x02, 0x01, 0x00,       // time 0
1981                       0x04, 0x00,             // no user name
1982                       0x04, 0x00,             // no auth par
1983                       0x04, 0x00,             // no priv par
1984           0x30, 0x12,
1985                 0x04, 0x00,                   // no context engine id
1986                 0x04, 0x00,                   // no context name
1987           0xa0, 0x0c,                         // GET PDU
1988                 0x02, 0x02, 0x34, 0x26,       // request id
1989                 0x02, 0x01, 0x00,             // error status no error
1990                 0x02, 0x01, 0x00,             // error index 0
1991                 0x30, 0x00                    // no data
1992   };
1993
1994   message = (unsigned char *)snmpv3_message;
1995   message_length = 60;
1996
1997   engine_id.clear();
1998
1999   UdpAddress uaddr(addr);
2000
2001 #ifdef SNMP_PP_IPv6
2002   if (uaddr.get_ip_version() == Address::version_ipv4)
2003   {
2004     if (iv_snmp_session != INVALID_SOCKET)
2005       sock = iv_snmp_session;
2006     else
2007     {
2008       uaddr.map_to_ipv6();
2009       sock = iv_snmp_session_ipv6;
2010     }
2011   }
2012   else
2013     sock = iv_snmp_session_ipv6;
2014 #else
2015   sock = iv_snmp_session;
2016 #endif
2017
2018   lock();
2019   if (send_snmp_request(sock, message, message_length, uaddr) < 0)
2020   {
2021     debugprintf(0, "Error sending message.");
2022     unlock();
2023     return SNMP_CLASS_TL_FAILED;
2024   }
2025
2026   // now wait for the responses
2027   Pdu dummy_pdu;
2028   int nfound = 0;
2029   msec end_time;
2030   struct timeval fd_timeout;
2031
2032   end_time += timeout_sec * 1000;
2033
2034 #ifdef HAVE_POLL_SYSCALL
2035   struct pollfd readfds;
2036   int timeout;
2037 #else
2038   fd_set readfds;
2039 #endif
2040
2041   do
2042   {
2043     bool something_to_receive = false;
2044     end_time.GetDeltaFromNow(fd_timeout);
2045
2046 #ifdef HAVE_POLL_SYSCALL
2047     memset(&readfds, 0, sizeof(struct pollfd));
2048     readfds.fd = sock;
2049     readfds.events = POLLIN;
2050     timeout = fd_timeout.tv_sec * 1000 + fd_timeout.tv_usec / 1000;
2051     nfound = poll(&readfds, 1, timeout);
2052     if ((nfound > 0) && (readfds.revents & POLLIN))
2053         something_to_receive = true;
2054 #else
2055     FD_ZERO(&readfds);
2056     FD_SET(sock, &readfds);
2057
2058     nfound = select((int)(sock + 1), &readfds, NULL, NULL, &fd_timeout);
2059     if ((nfound > 0) && (FD_ISSET(sock, &readfds)))
2060         something_to_receive = true;
2061 #endif
2062
2063     if (something_to_receive)
2064     {
2065       // receive message
2066       UdpAddress from;
2067       int res = receive_snmp_response(sock, *this, dummy_pdu,
2068                                       from, engine_id, true /* process_msg */);
2069       if ((res == SNMP_CLASS_SUCCESS) ||
2070           (res == SNMPv3_MP_UNKNOWN_PDU_HANDLERS))
2071       {
2072           //dummy_pdu.get_context_engine_id(engine_id);
2073         debugprintf(3, "Response received from (%s) id %s.",
2074                     from.get_printable(), engine_id.get_printable());
2075         unlock();
2076         return SNMP_CLASS_SUCCESS;
2077       }
2078       else
2079       {
2080         debugprintf(0, "Error receiving discovery response.");
2081       }
2082     }
2083   } while ((nfound > 0) ||
2084            (fd_timeout.tv_sec > 0) || (fd_timeout.tv_usec > 0));
2085   unlock();
2086
2087   return SNMP_CLASS_TIMEOUT;
2088 }
2089 #endif
2090
2091 // Send a SNMP Broadcast message.
2092 int Snmp::broadcast_discovery(UdpAddressCollection &addresses,
2093                               const int timeout_sec,
2094                               const UdpAddress &addr,
2095                               const snmp_version version,
2096                               const OctetStr *community)
2097 {
2098   unsigned char *message;
2099   int message_length;
2100   SnmpSocket sock;
2101   SnmpMessage snmpmsg;
2102
2103 #ifdef _SNMPv3
2104   unsigned char snmpv3_broadcast_message[60] = {
2105     0x30, 0x3a,
2106           0x02, 0x01, 0x03,                   // Version: 3
2107           0x30, 0x0f,                         // global header length 15
2108                 0x02, 0x03, 0x01, 0x00, 0x00, // message id
2109                 0x02, 0x02, 0x10, 0x00,       // message max size
2110                 0x04, 0x01, 0x04,             // flags (reportable set)
2111                 0x02, 0x01, 0x03,             // security model USM
2112           0x04, 0x10,                         // security params
2113                 0x30, 0x0e,
2114                       0x04, 0x00,             // no engine id
2115                       0x02, 0x01, 0x00,       // boots 0
2116                       0x02, 0x01, 0x00,       // time 0
2117                       0x04, 0x00,             // no user name
2118                       0x04, 0x00,             // no auth par
2119                       0x04, 0x00,             // no priv par
2120           0x30, 0x12,
2121                 0x04, 0x00,                   // no context engine id
2122                 0x04, 0x00,                   // no context name
2123           0xa0, 0x0c,                         // GET PDU
2124                 0x02, 0x02, 0x34, 0x26,       // request id
2125                 0x02, 0x01, 0x00,             // error status no error
2126                 0x02, 0x01, 0x00,             // error index 0
2127                 0x30, 0x00                    // no data
2128   };
2129
2130   if (version == version3)
2131   {
2132     message = (unsigned char *)snmpv3_broadcast_message;
2133     message_length = 60;
2134   }
2135   else
2136 #endif
2137   {
2138     Pdu pdu;
2139     Vb vb;
2140     OctetStr get_community;
2141
2142     vb.set_oid("1.3.6.1.2.1.1.1.0");
2143     pdu +=vb;
2144     pdu.set_error_index(0);            // set error index to none
2145     pdu.set_request_id(MyMakeReqId()); // determine request id to use
2146     pdu.set_type(sNMP_PDU_GET);        // set pdu type
2147
2148     if (community)
2149       get_community = *community;
2150     else
2151       get_community = "public";
2152
2153     int status = snmpmsg.load(pdu, get_community, version);
2154     if (status != SNMP_CLASS_SUCCESS)
2155     {
2156       debugprintf(0, "Error encoding broadcast pdu (%i).", status);
2157       return status;
2158     }
2159     message        = snmpmsg.data();
2160     message_length = snmpmsg.len();
2161   }
2162
2163   UdpAddress uaddr(addr);
2164
2165 #ifdef SNMP_PP_IPv6
2166   if (uaddr.get_ip_version() == Address::version_ipv4)
2167   {
2168     if (iv_snmp_session != INVALID_SOCKET)
2169       sock = iv_snmp_session;
2170     else
2171     {
2172       uaddr.map_to_ipv6();
2173       sock = iv_snmp_session_ipv6;
2174     }
2175   }
2176   else
2177     sock = iv_snmp_session_ipv6;
2178 #else
2179   sock = iv_snmp_session;
2180 #endif
2181
2182   lock();
2183   if (send_snmp_request(sock, message, message_length, uaddr) < 0)
2184   {
2185     debugprintf(0, "Error sending broadast.");
2186     unlock();
2187     return SNMP_CLASS_TL_FAILED;
2188   }
2189
2190   // now wait for the responses
2191   Pdu dummy_pdu;
2192   OctetStr engine_id;
2193   int nfound = 0;
2194   msec end_time;
2195   struct timeval fd_timeout;
2196
2197   end_time += timeout_sec * 1000;
2198
2199 #ifdef HAVE_POLL_SYSCALL
2200   struct pollfd readfds;
2201   int timeout;
2202 #else
2203   fd_set readfds;
2204 #endif
2205
2206   do
2207   {
2208     bool something_to_receive = false;
2209     end_time.GetDeltaFromNow(fd_timeout);
2210
2211 #ifdef HAVE_POLL_SYSCALL
2212     memset(&readfds, 0, sizeof(struct pollfd));
2213     readfds.fd = sock;
2214     readfds.events = POLLIN;
2215     timeout = fd_timeout.tv_sec * 1000 + fd_timeout.tv_usec / 1000;
2216     nfound = poll(&readfds, 1, timeout);
2217     if ((nfound > 0) && (readfds.revents & POLLIN))
2218         something_to_receive = true;
2219 #else
2220     FD_ZERO(&readfds);
2221     FD_SET(sock, &readfds);
2222
2223     nfound = select((int)(sock + 1), &readfds, NULL, NULL, &fd_timeout);
2224     if ((nfound > 0) && (FD_ISSET(sock, &readfds)))
2225         something_to_receive = true;
2226 #endif
2227
2228     if (something_to_receive)
2229     {
2230       // receive message
2231       UdpAddress from;
2232       if (receive_snmp_response(sock, *this, dummy_pdu,
2233                                 from, engine_id, false /* process_msg */)
2234           == SNMP_CLASS_SUCCESS)
2235       {
2236         addresses += from;
2237       }
2238       else
2239       {
2240         debugprintf(0, "Error receiving broadcast response.");
2241       }
2242     }
2243   } while ((nfound > 0) ||
2244            (fd_timeout.tv_sec > 0) || (fd_timeout.tv_usec > 0));
2245   unlock();
2246
2247 #ifdef __DEBUG
2248   for (int i=0; i < addresses.size(); ++i)
2249   {
2250     debugprintf(3, "Broadcast response received from (%s).",
2251                 addresses[i].get_printable());
2252   }
2253 #endif
2254   return 0;
2255 }
2256
2257 //     Starts the working thread for the recovery of the pending events
2258 bool Snmp::start_poll_thread(const int timeout)
2259 {
2260 #ifdef _THREADS 
2261     // store the timeout value for later
2262     m_iPollTimeOut = timeout;
2263
2264     // if we are already running return ok
2265     if (m_bThreadRunning == true) return true;
2266
2267     // since we are here, things must be fine so far...
2268     m_bThreadRunning = true;
2269
2270     // start the ProcessThread function....
2271 #ifdef WIN32
2272     DWORD id;
2273     m_hThread = CreateThread(NULL, 0,
2274                        (LPTHREAD_START_ROUTINE)&Snmp::process_thread,
2275                        this, 0, &id);
2276     if (m_hThread == NULL)
2277     {
2278         debugprintf(0, "Could not create ProcessThread");
2279         m_bThreadRunning = false;
2280     }
2281 #elif defined (CPU) && CPU == PPC603
2282         m_hThread = taskSpawn("Snmp::process_thread",  0, 0, 10000, (int (*)(...))Snmp::process_thread,  (int)this, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2283     if (m_hThread == ERROR)
2284     {
2285         // Could not create thread.
2286         debugprintf(0, "Could not create ProcessThread");
2287         m_bThreadRunning = false;
2288     }
2289 #else
2290     int rc = pthread_create(&m_hThread, NULL, Snmp::process_thread,
2291                             (void*) this);
2292     if (rc)
2293     {
2294         // Could not create thread.
2295         debugprintf(0, "Could not create ProcessThread");
2296         m_bThreadRunning = false;
2297     }
2298 #endif
2299 #endif
2300     return m_bThreadRunning;
2301 }
2302
2303 ///////////////////////////////////////////////////////////////////////////////
2304 //      stop_poll_thread
2305 //     Stops the recovery of the pending events
2306 //
2307 ///////////////////////////////////////////////////////////////////////////////
2308 void Snmp::stop_poll_thread()
2309 {
2310     if (m_bThreadRunning == false) return;
2311
2312 #ifdef _THREADS
2313     // stop the thread
2314     m_bThreadRunning = false;
2315
2316     // Wait for the working thread to stop....
2317 #ifdef WIN32
2318     ::WaitForSingleObject(m_hThreadEndEvent, INFINITE);
2319     CloseHandle(m_hThread);
2320 #elif defined (CPU) && CPU == PPC603
2321     while (taskIdVerify(m_hThread) == OK)
2322         taskDelay(10);
2323 #else
2324     //int *status; // not used
2325     pthread_join(m_hThread, NULL /*(void**) &status */); 
2326 #endif
2327 #endif
2328 }
2329
2330 #ifdef WIN32
2331 int Snmp::process_thread(Snmp *pSnmp)
2332 {
2333 #else
2334 void* Snmp::process_thread(void *arg)
2335 {
2336     Snmp* pSnmp = (Snmp*) arg;
2337 #endif // !WIN32
2338
2339     // Loop as long as we haven't stopped
2340     while (pSnmp->is_running())
2341     {
2342         pSnmp->eventListHolder
2343              ->SNMPProcessEvents(pSnmp->m_iPollTimeOut);
2344     }
2345
2346 #ifdef _THREADS
2347 #ifdef WIN32
2348     ::SetEvent(pSnmp->m_hThreadEndEvent);
2349 #else
2350 #if defined (CPU) && CPU == PPC603
2351         exit(0);
2352 #else
2353     pthread_exit(0);
2354 #endif
2355 #endif
2356 #endif
2357     return 0;
2358 }
2359
2360 #ifdef SNMP_PP_NAMESPACE
2361 }; // end of namespace Snmp_pp
2362 #endif