/*******************************************************************************
* 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.util.ArrayList;
import java.util.Date;
import java.util.Random;
import org.opennms.protocols.snmp.asn1.ASN1;
import org.opennms.protocols.snmp.asn1.AsnDecodingException;
import org.opennms.protocols.snmp.asn1.AsnEncoder;
import org.opennms.protocols.snmp.asn1.AsnEncodingException;
/**
* Base class for all Protocol Data Unit (PDU) implementations. The class
* defines methods to handle most v1 and v2 implementation of SNMP with only
* minor work needed by the derived class.
*
* @see SnmpPduRequest
* @see SnmpPduBulk
*
* @author <a href="mailto:weave@oculan.com">Brian Weaver </a>
*
*/
public abstract class SnmpPduPacket extends Object implements SnmpSyntax, Cloneable {
/**
* The static variable for the class. This variable is used to get a
* "unique" sequence number for each PDU
*/
private static int sm_seq = 0;
/**
* The SNMP command for the pdu. See the list of command later on in the
* module for more information.
*
* @see SnmpPduPacket#GET
* @see SnmpPduPacket#GETNEXT
* @see SnmpPduPacket#SET
* @see SnmpPduPacket#RESPONSE
* @see SnmpPduPacket#INFORM
* @see SnmpPduPacket#REPORT
* @see SnmpPduPacket#GETBULK
*/
private int m_command; // from pdu
/**
* The request id for this specific packet.
*/
private int m_requestId; // from pdu
/**
* The peer of this packet, if we are agent
*/
private SnmpPeer m_peer = null;
/**
* The list of variables for this particular PDU. The list may be quite
* large so long as the packet can be received by the remote appliction
*/
private ArrayList<SnmpVarBind> m_variables; // from pdu
/**
* The error status in a normal pdu, is is used as the non-repeaters in the
* getbulk.
*
* @see SnmpPduRequest
* @see SnmpPduBulk
*/
protected int m_errStatus; // is non-repeaters in Bulk!
/**
* The error index in a normal pdu, it is used as the maximum repititions in
* the get bulk pdu.
*
* @see SnmpPduRequest
* @see SnmpPduBulk
*/
protected int m_errIndex; // is max-repition in Bulk!
/**
* Default class constructor. Initialzies all primitive members to zero, and
* allocates a new array list for the variables.
*
*/
protected SnmpPduPacket() {
m_command = 0;
m_requestId = 0;
m_errStatus = 0;
m_errIndex = 0;
m_variables = new ArrayList<SnmpVarBind>();
}
/**
* Class copy constructor. Constructs the object with all the same values as
* the passed packet. The variables are duplicated into a new array so that
* changes to the source pdu will not affect the newly create pdu.
*
* @param second
* The source pdu to copy values from.
*
*/
protected SnmpPduPacket(SnmpPduPacket second) {
m_command = second.m_command;
m_requestId = second.m_requestId;
m_errStatus = second.m_errStatus;
m_errIndex = second.m_errIndex;
int sz = second.m_variables.size();
m_variables = new ArrayList<SnmpVarBind>(sz);
for (int x = 0; x < sz; x++) {
m_variables.add(x, second.m_variables.get(x).duplicate());
}
}
/**
* creates a new pdu with the command set to the passed value.
*
* @param command
* The type of pdu packet.
*
*/
protected SnmpPduPacket(int command) {
this();
m_command = command;
}
/**
* Creates a new pdu with the spcified command and the list of variables.
*
* @param command
* The type of pdu packet.
* @param vars
* The variable list for the pdu.
*
*/
protected SnmpPduPacket(int command, SnmpVarBind[] vars) {
this(command);
if (vars != null) {
m_variables.ensureCapacity(vars.length);
for (int x = 0; x < vars.length; x++) {
m_variables.add(vars[x].duplicate());
}
}
}
/**
* Use to sequence the all pdu request across the entire library. If the
* sequence id is equal to zero then a random number generator is created
* and is used to seed the sequence.
*
* @return The new sequnce identifier
*/
public static synchronized int nextSequence() {
if (sm_seq == 0) {
Date seed = new Date();
Random rnd = new Random(seed.getTime());
sm_seq = rnd.nextInt(1000000);
}
return sm_seq++;
}
//
// V1 commands
//
// Add 256 to make them unsigned quantities!
// ASN.CONTEXT is equal 0x80 and will be sign extended. The
// value of 256 will cause the value to be appropiately unsigned!
//
/**
* Defines a SNMPv1 Get Request PDU message.
*/
public static final int GET = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 0) + 256;
/**
* Defines a SNMPv1 Get Next Request PDU message.
*/
public static final int GETNEXT = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 1) + 256;
/**
* Defines a SNMPv1 Response PDU message.
*/
public static final int RESPONSE = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 2) + 256;
/**
* Defines a SNMPv1 PDU Set Request message. The set request uses the
* write-only string from the session. All others use the read-only
* community string.
*/
public static final int SET = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 3) + 256;
//
// V2 commands
//
// Add 256 to make them unsigned quantities!
// ASN.CONTEXT is equal 0x80 and will be sign extended. The
// value of 256 will cause the value to be appropiately unsigned!
//
/**
* Defines a SNMPv2 Get Bulk Request message.
*/
public static final int GETBULK = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 5) + 256;
/**
* Defines a SNMPv2 Inform Request message
*/
public static final int INFORM = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 6) + 256;
/**
* Defines a SNMPv2 Trap message
*/
public static final int V2TRAP = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 7) + 256;
/**
* Defines a SNMPv2 Report message.
*/
public static final int REPORT = (int) (ASN1.CONTEXT | ASN1.CONSTRUCTOR | 8) + 256;
//
// V1 errors
//
/**
* No error occured in the request. Also known as a successful request.
*/
public static final int ErrNoError = 0;
/**
* The PDU was too large for the agent to process
*/
public static final int ErrTooBig = 1;
/**
* There was no such object identifier defined in the agent's tables.
*/
public static final int ErrNoSuchName = 2;
/**
* If the object type does not match the object value in the agent's tables.
*/
public static final int ErrBadValue = 3;
/**
* Attempting to set a read-only object in the agent's tables.
*/
public static final int ErrReadOnly = 4;
/**
* A generic SNMPv1 error occured.
*/
public static final int ErrGenError = 5;
//
// V2 Errors
//
/**
* The specified SET request could not access the specified instance.
*/
public static final int ErrNoAccess = 6;
/**
* The specified object is not the correct type.
*/
public static final int ErrWrongType = 7;
/**
* The specified object is not the correct length.
*/
public static final int ErrWrongLength = 8;
/**
* The specified object is not correctly encoded.
*/
public static final int ErrWrongEncoding = 9;
/**
* The specified object doe not have the correct value.
*/
public static final int ErrWrongValue = 10;
/**
* The manager does not have the permission to create the specified
* object(s).
*/
public static final int ErrNoCreation = 11;
/**
* The specified value are not consistant.
*/
public static final int ErrInconsistentValue = 12;
/**
* The requested resource are not available.
*/
public static final int ErrResourceUnavailable = 13;
/**
* Unable to commit the required values.
*/
public static final int ErrCommitFailed = 14;
/**
* Unable to perform the undo request
*/
public static final int ErrUndoFailed = 15;
/**
* The authorization failed.
*/
public static final int ErrAuthorizationError = 16;
/**
* The specified instance or table is not writable
*/
public static final int ErrNotWritable = 17;
/**
* The passed object identifier is not consistent.
*/
public static final int ErrInconsistentName = 18;
/**
* Returns the type of PDU.
*
* @return The current PDU command
*
*/
public int getCommand() {
return m_command;
}
/**
* Sets the PDU's current command
*
* @param cmd
* The new command.
*
*/
public void setCommand(int cmd) {
m_command = cmd;
}
/**
* Returns the current request id for this packet.
*
* @return The sequence identifier
*
*/
public int getRequestId() {
return m_requestId;
}
/**
* Sets the Peer for the Packet
*
* @param peer
* The peer of this packet
*
*/
public void setPeer(SnmpPeer peer) {
m_peer = peer;
}
/**
* Returns the current peer for this packet.
*
* @return The peer or null, if its a own request
*
*/
public SnmpPeer getPeer() {
return m_peer;
}
/**
* Sets the protocol data unit's sequence identifer
*
* @param reqid
* The new request id
*
*/
public void setRequestId(int reqid) {
m_requestId = reqid;
}
/**
* /** Returns the number of variables in the data unit.
*
* @return The number of variables.
*/
public int getLength() {
return m_variables.size();
}
/**
* Adds a new variable to the protocol data unit. The variable is added at
* the end of the list
*
* @param vb
* The new variable to add
*/
public void addVarBind(SnmpVarBind vb) {
m_variables.add(vb);
}
/**
* Adds a variable at a specific index.
*
* @param ndx
* The index of the variable
* @param vb
* The new variable.
*
*/
public void addVarBindAt(int ndx, SnmpVarBind vb) {
m_variables.add(ndx, vb);
}
/**
* Retrieves the variable at the specific index.
*
* @param ndx
* The index of the variable
*
* @return The variable at the specified index
*
*/
public SnmpVarBind getVarBindAt(int ndx) {
return ((SnmpVarBind) (m_variables.get(ndx)));
}
/**
* Sets the specific variable at the requested location.
*
* @param ndx
* The location to set
* @param vb
* The new variable
*
*/
public void setVarBindAt(int ndx, SnmpVarBind vb) {
m_variables.set(ndx, vb);
}
/**
* Removes the variable as defined by the index
*
* @param ndx
* The index of the variable to remove
*
* @return The removed variable
*
*/
public SnmpVarBind removeVarBindAt(int ndx) {
return (SnmpVarBind) m_variables.remove(ndx);
}
/**
* Returns a list of all the variables managed by this protocol data unit.
*
* @return An array of the internal variable.
*
*/
public SnmpVarBind[] toVarBindArray() {
Object[] list = m_variables.toArray();
SnmpVarBind[] vblist = new SnmpVarBind[list.length];
for (int x = 0; x < list.length; x++) {
vblist[x] = (SnmpVarBind) (list[x]);
}
return vblist;
}
/**
* Returns the PDU commmand in an 8-bit format
*
* @return The pdu command
*/
public byte typeId() {
return (byte) (m_command & 0xff);
}
/**
* Encodes the protocol data unit using the passed encoder and stores the
* results in the passed buffer. An exception is thrown if an error occurs
* with the encoding of the information.
*
* @param buf
* The buffer to write the encoded information.
* @param offset
* The offset to start writing information
* @param encoder
* The encoder object.
*
* @return The offset of the byte immediantly after the last encoded byte.
*
* @exception AsnEncodingException
* Thrown if the encoder finds an error in the buffer.
*/
public int encodeASN(byte[] buf, int offset, AsnEncoder encoder) throws AsnEncodingException {
int begin = offset;
//
// encode the request id, error status (non-repeaters),
// and error index (max-repititions).
//
SnmpInt32 val = new SnmpInt32(m_requestId);
offset = val.encodeASN(buf, offset, encoder);
val.setValue(m_errStatus);
offset = val.encodeASN(buf, offset, encoder);
val.setValue(m_errIndex);
offset = val.encodeASN(buf, offset, encoder);
//
// mark the beginning of the vblist
//
int vbbegin = offset;
//
// Now encode the SnmpVarBinds!
//
int sz = m_variables.size();
for (int x = 0; x < sz; x++) {
SnmpVarBind ref = (SnmpVarBind) m_variables.get(x);
offset = ref.encodeASN(buf, offset, encoder);
}
//
// now mark the end of the varbinds
//
int pivot = offset;
//
// build the header for the varbind list
//
offset = encoder.buildHeader(buf, offset, SnmpVarBind.ASNTYPE, pivot - vbbegin);
//
// rotate the varbind header to the front.
// Then reset the pivot point
//
SnmpUtil.rotate(buf, vbbegin, pivot, offset);
pivot = offset;
//
// Now encode the header for the PDU,
// then rotate the header to the front.
//
offset = encoder.buildHeader(buf, offset, typeId(), pivot - begin);
SnmpUtil.rotate(buf, begin, pivot, offset);
return offset;
}
/**
* Decodes the protocol data unit from the passed buffer. If an error occurs
* during the decoding sequence then an AsnDecodingException is thrown by
* the method. The value is decoded using the AsnEncoder passed to the
* object.
*
* @param buf
* The encode buffer
* @param offset
* The offset byte to begin decoding
* @param encoder
* The decoder object.
*
* @return The index of the byte immediantly after the last decoded byte of
* information.
*
* @exception AsnDecodingException
* Thrown by the encoder if an error occurs trying to decode
* the data buffer.
*/
public int decodeASN(byte[] buf, int offset, AsnEncoder encoder) throws AsnDecodingException {
Object[] rVals = encoder.parseHeader(buf, offset);
offset = ((Integer) rVals[0]).intValue();
int cmd = ((Byte) rVals[1]).intValue();
int length = ((Integer) rVals[2]).intValue();
int begin = offset;
//
// set the command
//
if (cmd < 0)
cmd += 256; // wrap the value to a positive quantity!
m_command = cmd;
//
// get an 32-bit integer to decode values
//
SnmpInt32 val = new SnmpInt32();
offset = val.decodeASN(buf, offset, encoder);
m_requestId = val.getValue();
offset = val.decodeASN(buf, offset, encoder);
m_errStatus = val.getValue();
offset = val.decodeASN(buf, offset, encoder);
m_errIndex = val.getValue();
//
// get the total length of all
// the variables
//
rVals = encoder.parseHeader(buf, offset);
offset = ((Integer) rVals[0]).intValue();
length = ((Integer) rVals[2]).intValue();
byte asnType = ((Byte) rVals[1]).byteValue();
//
// check the ASN.1 type
//
if (asnType != SnmpVarBind.ASNTYPE)
throw new AsnDecodingException("Invalid SNMP variable list");
//
// set the beginning
//
begin = offset;
//
// clean out the current variables
//
m_variables.clear();
//
// decode the SnmpVarBinds
//
SnmpVarBind vb = new SnmpVarBind();
while (length > 0) {
offset = vb.decodeASN(buf, offset, encoder);
length -= (offset - begin);
begin = offset;
//
// add the varbind
//
m_variables.add(vb.duplicate());
}
return offset;
}
/**
* Defined for derived classes to return a duplicate of self. This method
* not defined.
*
*/
public abstract SnmpSyntax duplicate();
/**
* Defined for derived classes to return a duplicate of self. This method
* not defined.
*
*/
public abstract Object clone();
}