Ejemplo n.º 1
0
int CNotifyEventQueue::AddEntry(Snmp *snmp,
                                const OidCollection &trapids,
                                const TargetCollection &targets)
{
  SnmpSynchronize _synchronize(*this); // instead of REENTRANT()

  if (snmp != m_snmpSession)
  {
    debugprintf(0, "WARNING: Adding notification event for other Snmp object");
  }

  if (!m_msgCount)
  {
    m_notify_addr = snmp->get_listen_address();
    m_notify_addr.set_port(m_listen_port);

    int status = SNMP_CLASS_SUCCESS;

    // This is the first request to receive notifications
    // Set up the socket for the snmp trap port (162) or the
    // specified port through set_listen_port()
    bool is_v4_address = (m_notify_addr.get_ip_version() == Address::version_ipv4);
    if (is_v4_address)
    {
      struct sockaddr_in mgr_addr;

      // open a socket to be used for the session
      if ((m_notify_fd = socket(AF_INET, SOCK_DGRAM,0)) < 0)
      {
#ifdef WIN32
        int werr = WSAGetLastError();
        if (EMFILE == werr ||WSAENOBUFS == werr || ENFILE == werr)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (WSAEHOSTDOWN == werr)
          status = SNMP_CLASS_TL_FAILED;
        else
          status = SNMP_CLASS_TL_UNSUPPORTED;
#else
        if (EMFILE == errno || ENOBUFS == errno || ENFILE == errno)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (EHOSTDOWN == errno)
          status = SNMP_CLASS_TL_FAILED;
        else
          status = SNMP_CLASS_TL_UNSUPPORTED;
#endif
        cleanup();
        return status;
      }

      // set up the manager socket attributes
      unsigned long inaddr = inet_addr(IpAddress(m_notify_addr).get_printable());
      memset(&mgr_addr, 0, sizeof(mgr_addr));
      mgr_addr.sin_family = AF_INET;
      mgr_addr.sin_addr.s_addr = inaddr; // was htonl( INADDR_ANY);
      mgr_addr.sin_port = htons(m_notify_addr.get_port());
#ifdef CYGPKG_NET_OPENBSD_STACK
      mgr_addr.sin_len = sizeof(mgr_addr);
#endif

      // bind the socket
      if (bind(m_notify_fd, (struct sockaddr *) &mgr_addr,
               sizeof(mgr_addr)) < 0)
      {
#ifdef WIN32
        int werr = WSAGetLastError();
        if (WSAEADDRINUSE  == werr)
          status = SNMP_CLASS_TL_IN_USE;
        else if (WSAENOBUFS == werr)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (werr == WSAEAFNOSUPPORT)
          status = SNMP_CLASS_TL_UNSUPPORTED;
        else if (werr == WSAENETUNREACH)
          status = SNMP_CLASS_TL_FAILED;
        else if (werr == EACCES)
          status = SNMP_CLASS_TL_ACCESS_DENIED;
        else
          status = SNMP_CLASS_INTERNAL_ERROR;
#else
        if (EADDRINUSE  == errno)
          status = SNMP_CLASS_TL_IN_USE;
        else if (ENOBUFS == errno)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (errno == EAFNOSUPPORT)
          status = SNMP_CLASS_TL_UNSUPPORTED;
        else if (errno == ENETUNREACH)
          status = SNMP_CLASS_TL_FAILED;
        else if (errno == EACCES)
          status = SNMP_CLASS_TL_ACCESS_DENIED;
        else
        {
          debugprintf(0, "Uncatched errno value %d, returning internal error.",
                      errno);
          status = SNMP_CLASS_INTERNAL_ERROR;
        }
#endif
        debugprintf(0, "Fatal: could not bind to %s",
                    m_notify_addr.get_printable());
        cleanup();
        return status;
      }

      debugprintf(3, "Bind to %s for notifications, fd %d.",
                  m_notify_addr.get_printable(), m_notify_fd);
    } // is_v4_address
    else
    {
      // not is_v4_address
#ifdef SNMP_PP_IPv6
      // open a socket to be used for the session
      if ((m_notify_fd = socket(AF_INET6, SOCK_DGRAM,0)) < 0)
      {
#ifdef WIN32
        int werr = WSAGetLastError();
        if (EMFILE == werr ||WSAENOBUFS == werr || ENFILE == werr)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (WSAEHOSTDOWN == werr)
          status = SNMP_CLASS_TL_FAILED;
        else
          status = SNMP_CLASS_TL_UNSUPPORTED;
#else
        if (EMFILE == errno || ENOBUFS == errno || ENFILE == errno)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (EHOSTDOWN == errno)
          status = SNMP_CLASS_TL_FAILED;
        else
          status = SNMP_CLASS_TL_UNSUPPORTED;
#endif
        cleanup();
        return status;
      }

#ifdef NOTIFY_SET_IPV6_V6ONLY
      int on = 1;
      if (setsockopt(m_notify_fd, IPPROTO_IPV6, IPV6_V6ONLY,
		     (char *)&on, sizeof(on)) == -1)
      {
        LOG_BEGIN(loggerModuleName, WARNING_LOG | 1);
        LOG("Could not set option IPV6_V6ONLY on notify socket (errno)");
        LOG(errno);
        LOG_END;
      }
      else
      {
        LOG_BEGIN(loggerModuleName, INFO_LOG | 3);
        LOG("Have set IPV6_V6ONLY option on notify socket");
        LOG_END;
      }
#endif

      // set up the manager socket attributes
      struct sockaddr_in6 mgr_addr;
      memset(&mgr_addr, 0, sizeof(mgr_addr));

      unsigned int scope = 0;

      OctetStr addrstr = ((IpAddress &)m_notify_addr).IpAddress::get_printable();

      if (m_notify_addr.has_ipv6_scope())
      {
        scope = m_notify_addr.get_scope();

        int y = addrstr.len() - 1;
        while ((y>0) && (addrstr[y] != '%'))
        {
          addrstr.set_len(addrstr.len() - 1);
          y--;
        }
        if (addrstr[y] == '%')
          addrstr.set_len(addrstr.len() - 1);
      }

      if (inet_pton(AF_INET6, addrstr.get_printable(),
                    &mgr_addr.sin6_addr) < 0)
      {
	LOG_BEGIN(loggerModuleName, ERROR_LOG | 1);
        LOG("Notify transport: inet_pton returns (errno) (str)");
        LOG(errno);
        LOG(strerror(errno));
        LOG_END;
        cleanup();
        return SNMP_CLASS_INVALID_ADDRESS;
      }

      mgr_addr.sin6_family = AF_INET6;
      mgr_addr.sin6_port = htons(m_notify_addr.get_port());
      mgr_addr.sin6_scope_id = scope;

      // bind the socket
      if (bind(m_notify_fd, (struct sockaddr *) &mgr_addr,
               sizeof(mgr_addr)) < 0)
      {
#ifdef WIN32
        int werr = WSAGetLastError();
        if (WSAEADDRINUSE  == werr)
          status = SNMP_CLASS_TL_IN_USE;
        else if (WSAENOBUFS == werr)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (werr == WSAEAFNOSUPPORT)
          status = SNMP_CLASS_TL_UNSUPPORTED;
        else if (werr == WSAENETUNREACH)
          status = SNMP_CLASS_TL_FAILED;
        else if (werr == EACCES)
          status = SNMP_CLASS_TL_ACCESS_DENIED;
        else
          status = SNMP_CLASS_INTERNAL_ERROR;
#else
        if (EADDRINUSE  == errno)
          status = SNMP_CLASS_TL_IN_USE;
        else if (ENOBUFS == errno)
          status = SNMP_CLASS_RESOURCE_UNAVAIL;
        else if (errno == EAFNOSUPPORT)
          status = SNMP_CLASS_TL_UNSUPPORTED;
        else if (errno == ENETUNREACH)
          status = SNMP_CLASS_TL_FAILED;
        else if (errno == EACCES)
          status = SNMP_CLASS_TL_ACCESS_DENIED;
        else
        {
          debugprintf(0, "Uncatched errno value %d, returning internal error.",
                      errno);
          status = SNMP_CLASS_INTERNAL_ERROR;
        }
#endif
        debugprintf(0, "Fatal: could not bind to %s",
                    m_notify_addr.get_printable());
        cleanup();
        return status;
      }
      debugprintf(3, "Bind to %s for notifications, fd %d.",
                  m_notify_addr.get_printable(), m_notify_fd);
#else
      debugprintf(0, "User error: Enable IPv6 and recompile snmp++.");
      cleanup();
      return SNMP_CLASS_TL_UNSUPPORTED;
#endif
    } // not is_v4_address
  }

  CNotifyEvent *newEvent = new CNotifyEvent(snmp, trapids, targets);

  /*---------------------------------------------------------*/
  /* Insert entry at head of list, done automagically by the */
  /* constructor function, so don't use the return value.    */
  /*---------------------------------------------------------*/
  (void) new CNotifyEventQueueElt(newEvent, m_head.GetNext(), &m_head);
  m_msgCount++;

  return SNMP_CLASS_SUCCESS;
}