/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.protocols.snmp;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import org.opennms.protocols.snmp.asn1.ASN1;
import org.opennms.protocols.snmp.asn1.AsnEncoder;
import org.opennms.protocols.snmp.asn1.AsnEncodingException;
/**
* <P>
* The trap session is used to send and receives SNMPv1 & v2 trap messages. The
* messages are received on the configured port, or the default(162) port and
* then decoded using the set ASN.1 codec. When messages are sent they are
* encoded using the passed SnmpParameters object that is part of the SnmpPeer
* object.
* </P>
*
* <P>
* A trap message handler must be bound to the session in order to send or
* receive messages.
* </P>
*
* @author <a href="http://www.opennms.org">OpenNMS </a>
* @author <a href="mailto:weave@oculan.com">Brian Weaver </a>
* @author Sowmya
* @version 1.1.1.1 2001/11/11 17:27:22
*
* @see SnmpTrapHandler
*/
public final class SnmpTrapSession extends Object {
/**
* <P>
* Defines a error due to a thown exception. When the snmpTrapSessionError
* method is invoked in the trap handler, the exception object is passed as
* the ref parameter.
* </P>
*
* @see SnmpTrapHandler#snmpTrapSessionError
*
*/
public final static int ERROR_EXCEPTION = -1;
/**
* <P>
* Defines an error condition with an invalid PDU. For the moment this is
* not actually used, but reserved for future use. When the session trap
* handler error method is invoke the pdu in error should be passed as the
* ref parameters
*
* @see SnmpTrapHandler#snmpTrapSessionError
*/
public final static int ERROR_INVALID_PDU = -2;
/**
* This is the default port where traps should be sent and received as
* defined by the RFC.
*
*/
public final static int DEFAULT_PORT = 162;
/**
* The default SNMP trap callback handler. If this is not set and it is
* needed then an SnmpHandlerNotDefinedException is thrown.
*
*/
private SnmpPortal m_portal;
/**
* ASN.1 codec used to encode/decode SNMP traps that are sent and received
* by this session.
*/
private AsnEncoder m_encoder;
/**
* The public trap handler that process received traps.
*
*/
private SnmpTrapHandler m_handler;
/**
* If this boolean value is set then the receiver thread is terminated due
* to an exception that was generated in either a handler or a socket error.
* This is considered a fatal exception.
*/
private boolean m_threadException;
/**
* This is the saved fatal exception that can be rethrown by the application
*/
private Throwable m_why;
/**
* <P>
* The internal trap handler class is designed to receive information from
* the enclosed SnmpPortal class. The information is the processed and
* forwarded when appropiate to the SnmpTrapHandler registered with the
* session.
* </P>
*
*/
private class TrapHandler implements SnmpPacketHandler {
/**
* Who to pass as the session parameter
*/
private SnmpTrapSession m_forWhom;
/**
* <P>
* Creates a in internal trap handler to be the intermediary for the
* interface between the SnmpPortal and the TrapSession.
* </P>
*
* @param sess
* The trap session reference.
*
*/
public TrapHandler(SnmpTrapSession sess) {
m_forWhom = sess;
}
/**
* <P>
* Processes the default V1 & V2 messages.
* </P>
*
* @param agent
* The sending agent
* @param port
* The remote port.
* @param version
* The SNMP Version of the message.
* @param community
* The community string from the message.
* @param pduType
* The type of pdu
* @param pdu
* The actual pdu
*
* @exception SnmpPduEncodingException
* Thrown if the pdu fails to decode.
*/
public void processSnmpMessage(InetAddress agent, int port, SnmpInt32 version, SnmpOctetString community, int pduType, SnmpPduPacket pdu) throws SnmpPduEncodingException {
if (version.getValue() != SnmpSMI.SNMPV2 && pduType != SnmpPduPacket.V2TRAP)
return;
try {
m_handler.snmpReceivedTrap(m_forWhom, agent, port, community, pdu);
} catch (Exception e) {
// discard
}
}
/**
* <P>
* Processes V1 trap messages.
* </P>
*
* @param agent
* The sending agent
* @param port
* The remote port.
* @param community
* The community string from the message.
* @param pdu
* The actual pdu
*
* @exception SnmpPduEncodingException
* Thrown if the pdu fails to decode.
*/
public void processSnmpTrap(InetAddress agent, int port, SnmpOctetString community, SnmpPduTrap pdu) throws SnmpPduEncodingException {
try {
m_handler.snmpReceivedTrap(m_forWhom, agent, port, community, pdu);
} catch (Exception e) {
// discard
}
}
/**
* <P>
* Invoked when bad datagrams are received.
* </P>
*
* @param p
* The datagram packet in question.
*
*/
public void processBadDatagram(DatagramPacket p) {
// do nothing - discard?
}
/**
* <P>
* Invoked when an exception occurs in the session.
* </P>
*
* @param e
* The exception.
*/
public void processException(Exception e) {
try {
m_handler.snmpTrapSessionError(m_forWhom, ERROR_EXCEPTION, e);
} catch (Exception e1) {
// discard
}
}
}
/**
* Used to disallow the default constructor.
*
* @exception java.lang.UnsupportedOperationException
* Thrown if the constructor is called.
*
*/
@SuppressWarnings("unused")
private SnmpTrapSession() throws java.lang.UnsupportedOperationException {
throw new java.lang.UnsupportedOperationException("Illegal constructor call");
}
/**
* The default SnmpTrapSession constructor.
*
* @param handler
* The handler associated for message processing.
*
* @exception java.net.SocketException
* If thrown it is from the creation of a DatagramSocket.
* @exception java.lang.SecurityException
* Thrown if the security manager disallows the creation of
* the handler.
*/
public SnmpTrapSession(final SnmpTrapHandler handler) throws SocketException {
m_encoder = (new SnmpParameters()).getEncoder();
m_threadException = false;
m_why = null;
m_handler = handler;
m_portal = new SnmpPortal(new TrapHandler(this), m_encoder, DEFAULT_PORT);
}
/**
* The default SnmpTrapSession constructor that takes a packet handler as
* parameter. Also changes the default port to listen on
*
* @exception java.net.SocketException
* If thrown it is from the creation of a DatagramSocket.
*/
public SnmpTrapSession(final SnmpTrapHandler handler, final int port) throws SocketException {
m_encoder = (new SnmpParameters()).getEncoder();
m_handler = handler;
m_portal = new SnmpPortal(new TrapHandler(this), m_encoder, port);
m_threadException = false;
m_why = null;
}
public SnmpTrapSession(final SnmpTrapHandler handler, final InetAddress address, final int snmpTrapPort) throws SocketException {
m_encoder = (new SnmpParameters()).getEncoder();
m_handler = handler;
m_portal = new SnmpPortal(new TrapHandler(this), m_encoder, address, snmpTrapPort);
m_threadException = false;
m_why = null;
}
/**
* Returns the trap handler for this trap session.
*
* @return The SnmpTrapHandler
*/
public SnmpTrapHandler getHandler() {
return m_handler;
}
/**
* Sets the trap handler for the session.
*
* @param hdl
* The new packet handler
*
*/
public void setHandler(SnmpTrapHandler hdl) {
m_handler = hdl;
}
/**
* Sets the default encoder.
*
* @param encoder
* The new encoder
*
*/
public void setAsnEncoder(AsnEncoder encoder) {
m_encoder = encoder;
m_portal.setAsnEncoder(encoder);
}
/**
* Gets the AsnEncoder for the session.
*
* @return the AsnEncoder
*/
public AsnEncoder getAsnEncoder() {
return m_encoder;
}
/**
* Returns true if the <CODE>close</CODE> method has been called. The
* session cannot be used to send request after <CODE>close</CODE> has
* been executed.
*
*/
public boolean isClosed() {
return m_portal.isClosed();
}
/**
* Used to close the session. Once called the session should be considered
* invalid and unusable.
*
* @throws java.lang.IllegalStateException
* Thrown if the session was already closed.
*/
public void close() {
if (m_portal.isClosed())
throw new IllegalStateException("Illegal operation, the session is already closed");
m_portal.close();
}
/**
* If an exception occurs in the SNMP receiver thread then raise() will
* rethrow the exception.
*
* @exception java.lang.Throwable
* The base for thrown exceptions.
*/
public void raise() throws Throwable {
if (m_threadException)
throw m_why;
}
/**
* Transmits the specified SnmpPduTrap to the SnmpPeer defined The
* SnmpPduTrap is encoded using the peer AsnEncoder, as defined by the
* SnmpParameters. Once the packet is encoded it is transmitted to the agent
* defined by SnmpPeer. If an error occurs an appropiate exception is
* generated.
*
* @param peer
* The remote peer to send to.
* @param trap
* The SnmpPduTrap to transmit
*
* @exception SnmpPduEncodingException
* Thrown if an encoding exception occurs at the session
* level
* @exception org.opennms.protocols.snmp.asn1.AsnEncodingException
* Thrown if an encoding exception occurs in the AsnEncoder
* object.
* @exception java.io.IOException
* Thrown if an error occurs sending the encoded datagram
* @exception java.lang.IllegalStateException
* Thrown if the session has been closed.
*
* @see SnmpRequest
* @see SnmpParameters
* @see SnmpPeer
*
*/
public void send(SnmpPeer peer, SnmpPduTrap trap) throws SnmpPduEncodingException, AsnEncodingException, java.io.IOException {
if (m_portal.isClosed())
throw new IllegalStateException("Illegal operation, the session has been closed");
SnmpParameters parms = peer.getParameters();
if (parms.getVersion() != SnmpSMI.SNMPV1) {
throw new SnmpPduEncodingException("Cannot send pdu, invalid SNMP version");
}
//
// Get the encoder and start
// the encoding process
//
AsnEncoder encoder = parms.getEncoder();
//
// get a suitable buffer (16k)
//
int offset = 0;
byte[] buf = new byte[16 * 1024];
//
// encode the SNMP version
//
SnmpInt32 version = new SnmpInt32(parms.getVersion());
offset = version.encodeASN(buf, offset, encoder);
//
// get the correct community string. The
// SET command uses the write community, all
// others use the read community
//
SnmpOctetString community = new SnmpOctetString(parms.getReadCommunity().getBytes());
//
// encode the community strings
//
offset = community.encodeASN(buf, offset, encoder);
//
// Encode the actual trap
//
offset = trap.encodeASN(buf, offset, encoder);
//
// build the header, don't forget to mark the
// pivot point
//
int pivot = offset;
offset = encoder.buildHeader(buf, offset, (byte) (ASN1.SEQUENCE | ASN1.CONSTRUCTOR), pivot);
//
// rotate the buffer around the pivot point
//
SnmpUtil.rotate(buf, 0, pivot, offset);
//
// transmit the datagram
//
m_portal.send(peer, buf, offset);
}
/**
* Transmits the specified SnmpRequest to the SnmpPeer defined. First the
* SnmpPdu contained within the request is encoded using the peer
* AsnEncoder, as defined by the SnmpParameters. Once the packet is encoded
* it is transmitted to the agent defined by SnmpPeer. If an error occurs an
* appropiate exception is generated.
*
* @param peer
* The remote peer to send to.
* @param pdu
* The pdu to transmit
*
* @exception SnmpPduEncodingException
* Thrown if an encoding exception occurs at the session
* level
* @exception org.opennms.protocols.snmp.asn1.AsnEncodingException
* Thrown if an encoding exception occurs in the AsnEncoder
* object.
* @exception java.io.IOException
* Thrown if an error occurs sending the encoded datagram
* @exception java.lang.IllegalStateException
* Thrown if the session has been closed.
*
* @see SnmpRequest
* @see SnmpParameters
* @see SnmpPeer
*
*/
public void send(SnmpPeer peer, SnmpPduPacket pdu) throws SnmpPduEncodingException, AsnEncodingException, java.io.IOException {
if (m_portal.isClosed())
throw new IllegalStateException("Illegal operation, the session has been closed");
//
// break down the pieces into usable variables
//
SnmpParameters parms = peer.getParameters();
//
// verify that for a SNMPV1 session that no
// SNMPV2 packets are transmitted!
//
switch (pdu.getCommand()) {
case SnmpPduPacket.V2TRAP:
if (parms.getVersion() < SnmpSMI.SNMPV2) {
throw new SnmpPduEncodingException("Cannot send pdu, invalid SNMP version");
}
break;
default:
throw new SnmpPduEncodingException("Invalid pdu, not a trap");
}
//
// Get the encoder and start
// the encoding process
//
AsnEncoder encoder = parms.getEncoder();
//
// get a suitable buffer (16k)
//
int offset = 0;
byte[] buf = new byte[16 * 1024];
//
// encode the SNMP version
//
SnmpInt32 version = new SnmpInt32(parms.getVersion());
offset = version.encodeASN(buf, offset, encoder);
//
// get the correct community string. The
// SET command uses the write community, all
// others use the read community
//
SnmpOctetString community = new SnmpOctetString(parms.getReadCommunity().getBytes());
//
// encode the community strings
//
offset = community.encodeASN(buf, offset, encoder);
offset = pdu.encodeASN(buf, offset, encoder);
//
// build the header, don't forget to mark the
// pivot point
//
int pivot = offset;
offset = encoder.buildHeader(buf, offset, (byte) (ASN1.SEQUENCE | ASN1.CONSTRUCTOR), pivot);
//
// rotate the buffer around the pivot point
//
SnmpUtil.rotate(buf, 0, pivot, offset);
//
// transmit the datagram
//
m_portal.send(peer, buf, offset);
}
} // end of SnmpTrapSession class