1 /*_############################################################################
6 _## -----------------------------------------------
7 _## Copyright (c) 2001-2010 Jochen Katz, Frank Fock
9 _## This software is based on SNMP++2.6 from Hewlett Packard:
11 _## Copyright (c) 1996
12 _## Hewlett-Packard Company
14 _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS.
15 _## Permission to use, copy, modify, distribute and/or sell this software
16 _## and/or its documentation is hereby granted without fee. User agrees
17 _## to display the above copyright notice and this license notice in all
18 _## copies of the software and any documentation of the software. User
19 _## agrees to assume all liability for the use of the software;
20 _## Hewlett-Packard and Jochen Katz make no representations about the
21 _## suitability of this software for any purpose. It is provided
22 _## "AS-IS" without warranty of any kind, either express or implied. User
23 _## hereby grants a royalty-free license to any and all derivatives based
24 _## upon this software code base.
26 _## Stuttgart, Germany, Thu Sep 2 00:07:47 CEST 2010
28 _##########################################################################*/
29 /*===================================================================
32 Hewlett-Packard Company
34 ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS.
35 Permission to use, copy, modify, distribute and/or sell this software
36 and/or its documentation is hereby granted without fee. User agrees
37 to display the above copyright notice and this license notice in all
38 copies of the software and any documentation of the software. User
39 agrees to assume all liability for the use of the software; Hewlett-Packard
40 makes no representations about the suitability of this software for any
41 purpose. It is provided "AS-IS" without warranty of any kind,either express
42 or implied. User hereby grants a royalty-free license to any and all
43 derivatives based upon this software code base.
45 N O T I F Y Q U E U E . C P P
47 CNotifyEventQueue CLASS DEFINITION
49 COPYRIGHT HEWLETT PACKARD COMPANY 1999
51 INFORMATION NETWORKS DIVISION
53 NETWORK MANAGEMENT SECTION
55 DESIGN + AUTHOR: Tom Murray
58 Queue for holding callback associated with user defined
61 =====================================================================*/
62 char notifyqueue_version[]="#(@) SNMP++ $Id: notifyqueue.cpp 1826 2010-08-29 20:19:59Z katz $";
64 //-----[ includes ]----------------------------------------------------
67 #if defined (CPU) && CPU == PPC603
71 #if defined(__APPLE__)
72 #include <sys/types.h>
73 #include <sys/socket.h>
74 #include <netinet/in.h>
75 #include <arpa/inet.h>
79 //----[ snmp++ includes ]----------------------------------------------
81 #include "snmp_pp/config_snmp_pp.h"
82 #include "snmp_pp/v3.h"
83 #include "snmp_pp/notifyqueue.h" // queue for holding sessions waiting for async notifications
84 #include "snmp_pp/eventlistholder.h"
85 #include "snmp_pp/uxsnmp.h"
86 #include "snmp_pp/snmperrs.h"
87 #include "snmp_pp/pdu.h"
88 #include "snmp_pp/log.h"
90 #ifdef SNMP_PP_NAMESPACE
94 //--------[ externs ]---------------------------------------------------
95 extern int receive_snmp_notification(SnmpSocket sock, Snmp &snmp_session,
96 Pdu &pdu, SnmpTarget **target);
98 //-----[ macros ]------------------------------------------------------
99 // should be in snmp.h...
100 #define SNMP_PORT 161 // standard port # for SNMP
101 #define SNMP_TRAP_PORT 162 // standard port # for SNMP traps
104 #define close closesocket
109 //----[ CNotifyEvent class ]------------------------------------------------
111 CNotifyEvent::CNotifyEvent(Snmp *snmp,
112 const OidCollection &trapids,
113 const TargetCollection &targets)
116 // create new collections using parms passed in
117 notify_ids = new OidCollection(trapids);
118 notify_targets = new TargetCollection(targets);
121 CNotifyEvent::~CNotifyEvent()
123 // free up local collections
124 if (notify_ids) { delete notify_ids; notify_ids = 0; }
125 if (notify_targets) { delete notify_targets; notify_targets = 0; }
128 int CNotifyEvent::notify_filter(const Oid &trapid, SnmpTarget &target) const
130 int target_count, has_target = FALSE, target_matches = FALSE;
131 int trapid_count, has_trapid = FALSE, trapid_matches = FALSE;
132 GenAddress targetaddr, tmpaddr;
134 // figure out how many targets, handle empty case as all targets
135 if ((notify_targets) && ((target_count = notify_targets->size())))
137 SnmpTarget *tmptarget = 0;
140 target.get_address(targetaddr);
142 if (targetaddr.valid()) {
143 // loop through all targets in the collection
144 SnmpTarget::target_type target_type = target.get_type();
145 SnmpTarget::target_type tmptarget_type;
147 for ( int x = 0; x < target_count; x++) // for all targets
149 if (notify_targets->get_element(tmptarget, x))
152 tmptarget->get_address(tmpaddr);
153 if ((tmpaddr.valid())) {
156 /* check for types of Address */
157 if ((tmpaddr.get_type() == Address::type_ip) &&
158 (targetaddr.get_type() == Address::type_udp))
160 /* special case that works for UdpAddress == IpAddress */
161 IpAddress ip1(targetaddr);
162 IpAddress ip2(tmpaddr);
164 addr_equal = (ip1.valid() && ip2.valid() && (ip1 == ip2));
168 addr_equal = (targetaddr == tmpaddr);
172 tmptarget_type = tmptarget->get_type();
173 if (target_type == SnmpTarget::type_utarget) {
174 // target is a UTarget
175 if (tmptarget_type == SnmpTarget::type_utarget) {
177 if ((((UTarget*)(&target))->get_security_name() ==
178 ((UTarget*)tmptarget)->get_security_name()) &&
179 (((UTarget*)(&target))->get_security_model() ==
180 ((UTarget*)tmptarget)->get_security_model())) {
181 target_matches = TRUE;
186 if (tmptarget_type == SnmpTarget::type_ctarget)
187 // in case utarget is used with v1 or v2:
188 if ((tmptarget->get_version() == target.get_version()) &&
189 (((UTarget*)(&target))->get_security_name() ==
190 OctetStr(((CTarget*)tmptarget)->
191 get_readcommunity()))) {
192 target_matches = TRUE;
197 if (target_type == SnmpTarget::type_ctarget) {
198 // target is a CTarget
199 if (tmptarget_type == SnmpTarget::type_ctarget) {
201 if (!strcmp(((CTarget*)(&target))->get_readcommunity(),
202 ((CTarget*)tmptarget)->get_readcommunity())) {
203 target_matches = TRUE;
208 if (tmptarget_type == SnmpTarget::type_utarget) {
209 if ((tmptarget->get_version() == target.get_version()) &&
210 (OctetStr(((CTarget*)(&target))->get_readcommunity()) ==
211 ((UTarget*)tmptarget)->get_security_name())) {
212 target_matches = TRUE;
218 } // end if (add_equal)
219 } // end if tmpaddr.valid()...
223 // else no targets means all targets
225 // figure out how many trapids, handle empty case as all trapids
226 if ((notify_ids) && ((trapid_count = notify_ids->size()))) {
229 // loop through all trapids in the collection
230 for (int y=0; y < trapid_count; y++) // for all trapids
232 if (notify_ids->get_element(tmpoid, y))
234 if (trapid == tmpoid) {
235 trapid_matches = TRUE;
240 // else no trapids means all traps
242 // Make the callback if the trap passed the filters
243 if ((has_target && !target_matches) || (has_trapid && !trapid_matches))
249 int CNotifyEvent::Callback(SnmpTarget &target, Pdu &pdu, SnmpSocket fd, int status)
252 pdu.get_notify_id(trapid);
254 // Make the callback if the trap passed the filters
255 if ((m_snmp) && (notify_filter(trapid, target)))
259 if (SNMP_CLASS_TL_FAILED == status)
260 reason = SNMP_CLASS_TL_FAILED;
262 reason = SNMP_CLASS_NOTIFICATION;
264 //------[ call into the callback function ]-------------------------
265 if (m_snmp->get_notify_callback())
266 (m_snmp->get_notify_callback())(
268 m_snmp, // snmp++ session who owns the req
271 m_snmp->get_notify_callback_data()); // callback data
273 return SNMP_CLASS_SUCCESS;
277 //----[ CNotifyEventQueueElt class ]--------------------------------------
279 CNotifyEventQueue::CNotifyEventQueueElt::CNotifyEventQueueElt(
280 CNotifyEvent *notifyevent,
281 CNotifyEventQueueElt *next,
282 CNotifyEventQueueElt *previous)
283 : m_notifyevent(notifyevent), m_Next(next), m_previous(previous)
285 /* Finish insertion into doubly linked list */
286 if (m_Next) m_Next->m_previous = this;
287 if (m_previous) m_previous->m_Next = this;
290 CNotifyEventQueue::CNotifyEventQueueElt::~CNotifyEventQueueElt()
292 /* Do deletion form doubly linked list */
293 if (m_Next) m_Next->m_previous = m_previous;
294 if (m_previous) m_previous->m_Next = m_Next;
295 if (m_notifyevent) delete m_notifyevent;
298 CNotifyEvent *CNotifyEventQueue::CNotifyEventQueueElt::TestId(Snmp *snmp)
300 if (m_notifyevent && (m_notifyevent->GetId() == snmp))
301 return m_notifyevent;
306 //----[ CNotifyEventQueue class ]--------------------------------------
307 CNotifyEventQueue::CNotifyEventQueue(EventListHolder *holder, Snmp *session)
308 : m_head(NULL,NULL,NULL), m_msgCount(0), m_notify_fd(INVALID_SOCKET),
309 m_listen_port(SNMP_TRAP_PORT),
310 my_holder(holder), m_snmpSession(session)
312 //TM: could do the trap registration setup here but seems better to
313 //wait until the app actually requests trap receives by calling
317 CNotifyEventQueue::~CNotifyEventQueue()
319 CNotifyEventQueueElt *leftOver;
321 /* walk the list deleting any elements still on the queue */
323 while ((leftOver = m_head.GetNext()))
328 SnmpSocket CNotifyEventQueue::get_notify_fd() const
333 int CNotifyEventQueue::AddEntry(Snmp *snmp,
334 const OidCollection &trapids,
335 const TargetCollection &targets)
337 SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
339 if (snmp != m_snmpSession)
341 debugprintf(0, "WARNING: Adding notification event for other Snmp object");
346 m_notify_addr = snmp->get_listen_address();
347 m_notify_addr.set_port(m_listen_port);
349 int status = SNMP_CLASS_SUCCESS;
351 // This is the first request to receive notifications
352 // Set up the socket for the snmp trap port (162) or the
353 // specified port through set_listen_port()
354 bool is_v4_address = (m_notify_addr.get_ip_version() == Address::version_ipv4);
357 struct sockaddr_in mgr_addr;
359 // open a socket to be used for the session
360 if ((m_notify_fd = socket(AF_INET, SOCK_DGRAM,0)) < 0)
363 int werr = WSAGetLastError();
364 if (EMFILE == werr ||WSAENOBUFS == werr || ENFILE == werr)
365 status = SNMP_CLASS_RESOURCE_UNAVAIL;
366 else if (WSAEHOSTDOWN == werr)
367 status = SNMP_CLASS_TL_FAILED;
369 status = SNMP_CLASS_TL_UNSUPPORTED;
371 if (EMFILE == errno || ENOBUFS == errno || ENFILE == errno)
372 status = SNMP_CLASS_RESOURCE_UNAVAIL;
373 else if (EHOSTDOWN == errno)
374 status = SNMP_CLASS_TL_FAILED;
376 status = SNMP_CLASS_TL_UNSUPPORTED;
382 // set up the manager socket attributes
383 unsigned long inaddr = inet_addr(IpAddress(m_notify_addr).get_printable());
384 memset(&mgr_addr, 0, sizeof(mgr_addr));
385 mgr_addr.sin_family = AF_INET;
386 mgr_addr.sin_addr.s_addr = inaddr; // was htonl( INADDR_ANY);
387 mgr_addr.sin_port = htons(m_notify_addr.get_port());
388 #ifdef CYGPKG_NET_OPENBSD_STACK
389 mgr_addr.sin_len = sizeof(mgr_addr);
393 if (bind(m_notify_fd, (struct sockaddr *) &mgr_addr,
394 sizeof(mgr_addr)) < 0)
397 int werr = WSAGetLastError();
398 if (WSAEADDRINUSE == werr)
399 status = SNMP_CLASS_TL_IN_USE;
400 else if (WSAENOBUFS == werr)
401 status = SNMP_CLASS_RESOURCE_UNAVAIL;
402 else if (werr == WSAEAFNOSUPPORT)
403 status = SNMP_CLASS_TL_UNSUPPORTED;
404 else if (werr == WSAENETUNREACH)
405 status = SNMP_CLASS_TL_FAILED;
406 else if (werr == EACCES)
407 status = SNMP_CLASS_TL_ACCESS_DENIED;
409 status = SNMP_CLASS_INTERNAL_ERROR;
411 if (EADDRINUSE == errno)
412 status = SNMP_CLASS_TL_IN_USE;
413 else if (ENOBUFS == errno)
414 status = SNMP_CLASS_RESOURCE_UNAVAIL;
415 else if (errno == EAFNOSUPPORT)
416 status = SNMP_CLASS_TL_UNSUPPORTED;
417 else if (errno == ENETUNREACH)
418 status = SNMP_CLASS_TL_FAILED;
419 else if (errno == EACCES)
420 status = SNMP_CLASS_TL_ACCESS_DENIED;
423 debugprintf(0, "Uncatched errno value %d, returning internal error.",
425 status = SNMP_CLASS_INTERNAL_ERROR;
428 debugprintf(0, "Fatal: could not bind to %s",
429 m_notify_addr.get_printable());
434 debugprintf(3, "Bind to %s for notifications, fd %d.",
435 m_notify_addr.get_printable(), m_notify_fd);
441 // open a socket to be used for the session
442 if ((m_notify_fd = socket(AF_INET6, SOCK_DGRAM,0)) < 0)
445 int werr = WSAGetLastError();
446 if (EMFILE == werr ||WSAENOBUFS == werr || ENFILE == werr)
447 status = SNMP_CLASS_RESOURCE_UNAVAIL;
448 else if (WSAEHOSTDOWN == werr)
449 status = SNMP_CLASS_TL_FAILED;
451 status = SNMP_CLASS_TL_UNSUPPORTED;
453 if (EMFILE == errno || ENOBUFS == errno || ENFILE == errno)
454 status = SNMP_CLASS_RESOURCE_UNAVAIL;
455 else if (EHOSTDOWN == errno)
456 status = SNMP_CLASS_TL_FAILED;
458 status = SNMP_CLASS_TL_UNSUPPORTED;
464 // set up the manager socket attributes
465 struct sockaddr_in6 mgr_addr;
466 memset(&mgr_addr, 0, sizeof(mgr_addr));
468 unsigned int scope = 0;
470 OctetStr addrstr = ((IpAddress &)m_notify_addr).IpAddress::get_printable();
472 if (m_notify_addr.has_ipv6_scope())
474 scope = m_notify_addr.get_scope();
476 int y = addrstr.len() - 1;
477 while ((y>0) && (addrstr[y] != '%'))
479 addrstr.set_len(addrstr.len() - 1);
482 if (addrstr[y] == '%')
483 addrstr.set_len(addrstr.len() - 1);
486 if (inet_pton(AF_INET6, addrstr.get_printable(),
487 &mgr_addr.sin6_addr) < 0)
489 LOG_BEGIN(ERROR_LOG | 1);
490 LOG("Notify transport: inet_pton returns (errno) (str)");
492 LOG(strerror(errno));
495 return SNMP_CLASS_INVALID_ADDRESS;
498 mgr_addr.sin6_family = AF_INET6;
499 mgr_addr.sin6_port = htons(m_notify_addr.get_port());
500 mgr_addr.sin6_scope_id = scope;
503 if (bind(m_notify_fd, (struct sockaddr *) &mgr_addr,
504 sizeof(mgr_addr)) < 0)
507 int werr = WSAGetLastError();
508 if (WSAEADDRINUSE == werr)
509 status = SNMP_CLASS_TL_IN_USE;
510 else if (WSAENOBUFS == werr)
511 status = SNMP_CLASS_RESOURCE_UNAVAIL;
512 else if (werr == WSAEAFNOSUPPORT)
513 status = SNMP_CLASS_TL_UNSUPPORTED;
514 else if (werr == WSAENETUNREACH)
515 status = SNMP_CLASS_TL_FAILED;
516 else if (werr == EACCES)
517 status = SNMP_CLASS_TL_ACCESS_DENIED;
519 status = SNMP_CLASS_INTERNAL_ERROR;
521 if (EADDRINUSE == errno)
522 status = SNMP_CLASS_TL_IN_USE;
523 else if (ENOBUFS == errno)
524 status = SNMP_CLASS_RESOURCE_UNAVAIL;
525 else if (errno == EAFNOSUPPORT)
526 status = SNMP_CLASS_TL_UNSUPPORTED;
527 else if (errno == ENETUNREACH)
528 status = SNMP_CLASS_TL_FAILED;
529 else if (errno == EACCES)
530 status = SNMP_CLASS_TL_ACCESS_DENIED;
533 debugprintf(0, "Uncatched errno value %d, returning internal error.",
535 status = SNMP_CLASS_INTERNAL_ERROR;
539 debugprintf(0, "Fatal: could not bind to %s",
540 m_notify_addr.get_printable());
544 debugprintf(3, "Bind to %s for notifications, fd %d.",
545 m_notify_addr.get_printable(), m_notify_fd);
547 debugprintf(0, "User error: Enable IPv6 and recompile snmp++.");
549 return SNMP_CLASS_TL_UNSUPPORTED;
551 } // not is_v4_address
554 CNotifyEvent *newEvent = new CNotifyEvent(snmp, trapids, targets);
556 /*---------------------------------------------------------*/
557 /* Insert entry at head of list, done automagically by the */
558 /* constructor function, so don't use the return value. */
559 /*---------------------------------------------------------*/
560 (void) new CNotifyEventQueueElt(newEvent, m_head.GetNext(), &m_head);
563 return SNMP_CLASS_SUCCESS;
566 void CNotifyEventQueue::cleanup()
568 if (m_notify_fd != INVALID_SOCKET)
571 m_notify_fd = INVALID_SOCKET;
573 m_notify_addr.clear();
576 CNotifyEvent *CNotifyEventQueue::GetEntry(Snmp * snmp) REENTRANT ({
577 CNotifyEventQueueElt *msgEltPtr = m_head.GetNext();
578 CNotifyEvent *returnVal = NULL;
581 if ((returnVal = msgEltPtr->TestId(snmp)))
583 msgEltPtr = msgEltPtr->GetNext();
588 void CNotifyEventQueue::DeleteEntry(Snmp *snmp)
591 CNotifyEventQueueElt *msgEltPtr = m_head.GetNext();
594 if (msgEltPtr->TestId(snmp)){
599 msgEltPtr = msgEltPtr->GetNext();
604 // shut down the trap socket (if valid) if not using it.
605 if (m_notify_fd != INVALID_SOCKET)
607 debugprintf(3, "Closing notifications port %s, fd %d.",
608 m_notify_addr.get_printable(), m_notify_fd);
610 m_notify_fd = INVALID_SOCKET;
612 m_notify_addr.clear();
617 #ifdef HAVE_POLL_SYSCALL
618 int CNotifyEventQueue::GetFdCount()
620 SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
621 if (m_notify_fd == INVALID_SOCKET)
626 bool CNotifyEventQueue::GetFdArray(struct pollfd *readfds,
629 SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
631 if (m_notify_fd != INVALID_SOCKET)
635 readfds[0].fd = m_notify_fd;
636 readfds[0].events = POLLIN;
642 int CNotifyEventQueue::HandleEvents(const struct pollfd *readfds,
645 SnmpSynchronize _synchronize(*this); // instead of REENTRANT()
647 int status = SNMP_CLASS_SUCCESS;
649 if (m_notify_fd == INVALID_SOCKET)
652 for (int i=0; i < fds; i++)
655 SnmpTarget *target = NULL;
657 if ((readfds[i].revents & POLLIN) == 0)
658 continue; // nothing to receive
660 if (readfds[i].fd != m_notify_fd)
661 continue; // not our socket
663 status = receive_snmp_notification(m_notify_fd, *m_snmpSession,
666 if ((SNMP_CLASS_SUCCESS == status) ||
667 (SNMP_CLASS_TL_FAILED == status))
669 // If we have transport layer failure, the app will want to
671 // Go through each snmp object and check the filters, making
672 // callbacks as necessary
673 if (!target) target = new SnmpTarget();
675 CNotifyEventQueueElt *notifyEltPtr = m_head.GetNext();
678 notifyEltPtr->GetNotifyEvent()->Callback(*target, pdu,
679 m_notify_fd, status);
680 notifyEltPtr = notifyEltPtr->GetNext();
681 } // for each snmp object
683 if (target) // receive_snmp_notification calls new
692 void CNotifyEventQueue::GetFdSets(int &maxfds, fd_set &readfds,
693 fd_set &/*writefds*/,
694 fd_set &/*exceptfds*/)
696 SnmpSynchronize _synchronize(*this); // REENTRANT
697 if (m_notify_fd != INVALID_SOCKET)
699 FD_SET(m_notify_fd, &readfds);
700 if (maxfds < SAFE_INT_CAST(m_notify_fd + 1))
701 maxfds = SAFE_INT_CAST(m_notify_fd + 1);
706 int CNotifyEventQueue::HandleEvents(const int /*maxfds*/,
707 const fd_set &readfds,
708 const fd_set &/*writefds*/,
709 const fd_set &/*exceptfds*/)
711 SnmpSynchronize _synchronize(*this); // REENTRANT
712 int status = SNMP_CLASS_SUCCESS;
714 if (m_notify_fd == INVALID_SOCKET)
718 SnmpTarget *target = NULL;
720 // pull the notifiaction off the socket
721 if (FD_ISSET(m_notify_fd, (fd_set*)&readfds)) {
722 status = receive_snmp_notification(m_notify_fd, *m_snmpSession,
725 if ((SNMP_CLASS_SUCCESS == status) ||
726 (SNMP_CLASS_TL_FAILED == status))
728 // If we have transport layer failure, the app will want to
730 // Go through each snmp object and check the filters, making
731 // callbacks as necessary
733 // On failure target will be NULL
735 target = new SnmpTarget();
737 CNotifyEventQueueElt *notifyEltPtr = m_head.GetNext();
740 notifyEltPtr->GetNotifyEvent()->Callback(*target, pdu,
741 m_notify_fd, status);
742 notifyEltPtr = notifyEltPtr->GetNext();
743 } // for each snmp object
745 if (target) // receive_snmp_notification calls new
751 #endif // HAVE_POLL_SYSCALL
753 #ifdef SNMP_PP_NAMESPACE
754 }; // end of namespace Snmp_pp