/*
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jmx.snmp.daemon;
// java import
//
import java.util.logging.Level;
import java.util.Vector;
// jmx imports
//
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
import com.sun.jmx.snmp.SnmpPdu;
import com.sun.jmx.snmp.SnmpVarBind;
import com.sun.jmx.snmp.SnmpDefinitions;
import com.sun.jmx.snmp.SnmpStatusException;
import com.sun.jmx.snmp.SnmpEngine;
// SNMP Runtime import
//
import com.sun.jmx.snmp.agent.SnmpMibAgent;
import com.sun.jmx.snmp.agent.SnmpMibRequest;
import com.sun.jmx.snmp.ThreadContext;
import com.sun.jmx.snmp.internal.SnmpIncomingRequest;
class SnmpSubRequestHandler implements SnmpDefinitions, Runnable {
protected SnmpIncomingRequest incRequest = null;
protected SnmpEngine engine = null;
/**
* V3 enabled Adaptor. Each Oid is added using updateRequest method.
*/
protected SnmpSubRequestHandler(SnmpEngine engine,
SnmpIncomingRequest incRequest,
SnmpMibAgent agent,
SnmpPdu req) {
this(agent, req);
init(engine, incRequest);
}
/**
* V3 enabled Adaptor.
*/
protected SnmpSubRequestHandler(SnmpEngine engine,
SnmpIncomingRequest incRequest,
SnmpMibAgent agent,
SnmpPdu req,
boolean nouse) {
this(agent, req, nouse);
init(engine, incRequest);
}
/**
* SNMP V1/V2 . To be called with updateRequest.
*/
protected SnmpSubRequestHandler(SnmpMibAgent agent, SnmpPdu req) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSubRequestHandler.class.getName(),
"constructor", "creating instance for request " + String.valueOf(req.requestId));
}
version= req.version;
type= req.type;
this.agent= agent;
// We get a ref on the pdu in order to pass it to SnmpMibRequest.
reqPdu = req;
//Pre-allocate room for storing varbindlist and translation table.
//
int length= req.varBindList.length;
translation= new int[length];
varBind= new NonSyncVector<SnmpVarBind>(length);
}
/**
* SNMP V1/V2 The constuctor initialize the subrequest with the whole varbind list contained
* in the original request.
*/
@SuppressWarnings("unchecked") // cast to NonSyncVector<SnmpVarBind>
protected SnmpSubRequestHandler(SnmpMibAgent agent,
SnmpPdu req,
boolean nouse) {
this(agent,req);
// The translation table is easy in this case ...
//
int max= translation.length;
SnmpVarBind[] list= req.varBindList;
for(int i=0; i < max; i++) {
translation[i]= i;
((NonSyncVector<SnmpVarBind>)varBind).addNonSyncElement(list[i]);
}
}
SnmpMibRequest createMibRequest(Vector<SnmpVarBind> vblist,
int protocolVersion,
Object userData) {
// This is an optimization:
// The SnmpMibRequest created in the check() phase is
// reused in the set() phase.
//
if (type == pduSetRequestPdu && mibRequest != null)
return mibRequest;
//This is a request comming from an SnmpV3AdaptorServer.
//Full power.
SnmpMibRequest result = null;
if(incRequest != null) {
result = SnmpMibAgent.newMibRequest(engine,
reqPdu,
vblist,
protocolVersion,
userData,
incRequest.getPrincipal(),
incRequest.getSecurityLevel(),
incRequest.getSecurityModel(),
incRequest.getContextName(),
incRequest.getAccessContext());
} else {
result = SnmpMibAgent.newMibRequest(reqPdu,
vblist,
protocolVersion,
userData);
}
// If we're doing the check() phase, we store the SnmpMibRequest
// so that we can reuse it in the set() phase.
//
if (type == pduWalkRequest)
mibRequest = result;
return result;
}
void setUserData(Object userData) {
data = userData;
}
public void run() {
try {
final ThreadContext oldContext =
ThreadContext.push("SnmpUserData",data);
try {
switch(type) {
case pduGetRequestPdu:
// Invoke a get operation
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() +
"]:get operation on " + agent.getMibName());
}
agent.get(createMibRequest(varBind,version,data));
break;
case pduGetNextRequestPdu:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() +
"]:getNext operation on " + agent.getMibName());
}
//#ifdef DEBUG
agent.getNext(createMibRequest(varBind,version,data));
break;
case pduSetRequestPdu:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() +
"]:set operation on " + agent.getMibName());
}
agent.set(createMibRequest(varBind,version,data));
break;
case pduWalkRequest:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() +
"]:check operation on " + agent.getMibName());
}
agent.check(createMibRequest(varBind,version,data));
break;
default:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() +
"]:unknown operation (" + type + ") on " +
agent.getMibName());
}
errorStatus= snmpRspGenErr;
errorIndex= 1;
break;
}// end of switch
} finally {
ThreadContext.restore(oldContext);
}
} catch(SnmpStatusException x) {
errorStatus = x.getStatus() ;
errorIndex= x.getErrorIndex();
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() +
"]:an Snmp error occured during the operation", x);
}
}
catch(Exception x) {
errorStatus = SnmpDefinitions.snmpRspGenErr ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() +
"]:a generic error occured during the operation", x);
}
}
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSubRequestHandler.class.getName(),
"run", "[" + Thread.currentThread() + "]:operation completed");
}
}
// -------------------------------------------------------------
//
// This function does a best-effort to map global error status
// to SNMP v1 valid global error status.
//
// An SnmpStatusException can contain either:
// <li> v2 local error codes (that should be stored in the varbind)</li>
// <li> v2 global error codes </li>
// <li> v1 global error codes </li>
//
// v2 local error codes (noSuchInstance, noSuchObject) are
// transformed in a global v1 snmpRspNoSuchName error.
//
// v2 global error codes are transformed in the following way:
//
// If the request was a GET/GETNEXT then either
// snmpRspNoSuchName or snmpRspGenErr is returned.
//
// Otherwise:
// snmpRspNoAccess, snmpRspInconsistentName
// => snmpRspNoSuchName
// snmpRspAuthorizationError, snmpRspNotWritable, snmpRspNoCreation
// => snmpRspReadOnly (snmpRspNoSuchName for GET/GETNEXT)
// snmpRspWrong*
// => snmpRspBadValue (snmpRspNoSuchName for GET/GETNEXT)
// snmpRspResourceUnavailable, snmpRspRspCommitFailed,
// snmpRspUndoFailed
// => snmpRspGenErr
//
// -------------------------------------------------------------
//
static final int mapErrorStatusToV1(int errorStatus, int reqPduType) {
// Map v2 codes onto v1 codes
//
if (errorStatus == SnmpDefinitions.snmpRspNoError)
return SnmpDefinitions.snmpRspNoError;
if (errorStatus == SnmpDefinitions.snmpRspGenErr)
return SnmpDefinitions.snmpRspGenErr;
if (errorStatus == SnmpDefinitions.snmpRspNoSuchName)
return SnmpDefinitions.snmpRspNoSuchName;
if ((errorStatus == SnmpStatusException.noSuchInstance) ||
(errorStatus == SnmpStatusException.noSuchObject) ||
(errorStatus == SnmpDefinitions.snmpRspNoAccess) ||
(errorStatus == SnmpDefinitions.snmpRspInconsistentName) ||
(errorStatus == SnmpDefinitions.snmpRspAuthorizationError)){
return SnmpDefinitions.snmpRspNoSuchName;
} else if ((errorStatus ==
SnmpDefinitions.snmpRspAuthorizationError) ||
(errorStatus == SnmpDefinitions.snmpRspNotWritable)) {
if (reqPduType == SnmpDefinitions.pduWalkRequest)
return SnmpDefinitions.snmpRspReadOnly;
else
return SnmpDefinitions.snmpRspNoSuchName;
} else if ((errorStatus == SnmpDefinitions.snmpRspNoCreation)) {
return SnmpDefinitions.snmpRspNoSuchName;
} else if ((errorStatus == SnmpDefinitions.snmpRspWrongType) ||
(errorStatus == SnmpDefinitions.snmpRspWrongLength) ||
(errorStatus == SnmpDefinitions.snmpRspWrongEncoding) ||
(errorStatus == SnmpDefinitions.snmpRspWrongValue) ||
(errorStatus == SnmpDefinitions.snmpRspWrongLength) ||
(errorStatus ==
SnmpDefinitions.snmpRspInconsistentValue)) {
if ((reqPduType == SnmpDefinitions.pduSetRequestPdu) ||
(reqPduType == SnmpDefinitions.pduWalkRequest))
return SnmpDefinitions.snmpRspBadValue;
else
return SnmpDefinitions.snmpRspNoSuchName;
} else if ((errorStatus ==
SnmpDefinitions.snmpRspResourceUnavailable) ||
(errorStatus ==
SnmpDefinitions.snmpRspCommitFailed) ||
(errorStatus == SnmpDefinitions.snmpRspUndoFailed)) {
return SnmpDefinitions.snmpRspGenErr;
}
// At this point we should have a V1 error code
//
if (errorStatus == SnmpDefinitions.snmpRspTooBig)
return SnmpDefinitions.snmpRspTooBig;
if( (errorStatus == SnmpDefinitions.snmpRspBadValue) ||
(errorStatus == SnmpDefinitions.snmpRspReadOnly)) {
if ((reqPduType == SnmpDefinitions.pduSetRequestPdu) ||
(reqPduType == SnmpDefinitions.pduWalkRequest))
return errorStatus;
else
return SnmpDefinitions.snmpRspNoSuchName;
}
// We have a snmpRspGenErr, or something which is not defined
// in RFC1905 => return a snmpRspGenErr
//
return SnmpDefinitions.snmpRspGenErr;
}
// -------------------------------------------------------------
//
// This function does a best-effort to map global error status
// to SNMP v2 valid global error status.
//
// An SnmpStatusException can contain either:
// <li> v2 local error codes (that should be stored in the varbind)</li>
// <li> v2 global error codes </li>
// <li> v1 global error codes </li>
//
// v2 local error codes (noSuchInstance, noSuchObject)
// should not raise this level: they should have been stored in the
// varbind earlier. If they, do there is nothing much we can do except
// to transform them into:
// <li> a global snmpRspGenErr (if the request is a GET/GETNEXT) </li>
// <li> a global snmpRspNoSuchName otherwise. </li>
//
// v2 global error codes are transformed in the following way:
//
// If the request was a GET/GETNEXT then snmpRspGenErr is returned.
// (snmpRspGenErr is the only global error that is expected to be
// raised by a GET/GETNEXT request).
//
// Otherwise the v2 code itself is returned
//
// v1 global error codes are transformed in the following way:
//
// snmpRspNoSuchName
// => snmpRspNoAccess (snmpRspGenErr for GET/GETNEXT)
// snmpRspReadOnly
// => snmpRspNotWritable (snmpRspGenErr for GET/GETNEXT)
// snmpRspBadValue
// => snmpRspWrongValue (snmpRspGenErr for GET/GETNEXT)
//
// -------------------------------------------------------------
//
static final int mapErrorStatusToV2(int errorStatus, int reqPduType) {
// Map v1 codes onto v2 codes
//
if (errorStatus == SnmpDefinitions.snmpRspNoError)
return SnmpDefinitions.snmpRspNoError;
if (errorStatus == SnmpDefinitions.snmpRspGenErr)
return SnmpDefinitions.snmpRspGenErr;
if (errorStatus == SnmpDefinitions.snmpRspTooBig)
return SnmpDefinitions.snmpRspTooBig;
// For get / getNext / getBulk the only global error
// (PDU-level) possible is genErr.
//
if ((reqPduType != SnmpDefinitions.pduSetRequestPdu) &&
(reqPduType != SnmpDefinitions.pduWalkRequest)) {
if(errorStatus == SnmpDefinitions.snmpRspAuthorizationError)
return errorStatus;
else
return SnmpDefinitions.snmpRspGenErr;
}
// Map to noSuchName
// if ((errorStatus == SnmpDefinitions.snmpRspNoSuchName) ||
// (errorStatus == SnmpStatusException.noSuchInstance) ||
// (errorStatus == SnmpStatusException.noSuchObject))
// return SnmpDefinitions.snmpRspNoSuchName;
// SnmpStatusException.noSuchInstance and
// SnmpStatusException.noSuchObject can't happen...
if (errorStatus == SnmpDefinitions.snmpRspNoSuchName)
return SnmpDefinitions.snmpRspNoAccess;
// Map to notWritable
if (errorStatus == SnmpDefinitions.snmpRspReadOnly)
return SnmpDefinitions.snmpRspNotWritable;
// Map to wrongValue
if (errorStatus == SnmpDefinitions.snmpRspBadValue)
return SnmpDefinitions.snmpRspWrongValue;
// Other valid V2 codes
if ((errorStatus == SnmpDefinitions.snmpRspNoAccess) ||
(errorStatus == SnmpDefinitions.snmpRspInconsistentName) ||
(errorStatus == SnmpDefinitions.snmpRspAuthorizationError) ||
(errorStatus == SnmpDefinitions.snmpRspNotWritable) ||
(errorStatus == SnmpDefinitions.snmpRspNoCreation) ||
(errorStatus == SnmpDefinitions.snmpRspWrongType) ||
(errorStatus == SnmpDefinitions.snmpRspWrongLength) ||
(errorStatus == SnmpDefinitions.snmpRspWrongEncoding) ||
(errorStatus == SnmpDefinitions.snmpRspWrongValue) ||
(errorStatus == SnmpDefinitions.snmpRspWrongLength) ||
(errorStatus == SnmpDefinitions.snmpRspInconsistentValue) ||
(errorStatus == SnmpDefinitions.snmpRspResourceUnavailable) ||
(errorStatus == SnmpDefinitions.snmpRspCommitFailed) ||
(errorStatus == SnmpDefinitions.snmpRspUndoFailed))
return errorStatus;
// Ivalid V2 code => genErr
return SnmpDefinitions.snmpRspGenErr;
}
static final int mapErrorStatus(int errorStatus,
int protocolVersion,
int reqPduType) {
if (errorStatus == SnmpDefinitions.snmpRspNoError)
return SnmpDefinitions.snmpRspNoError;
// Too bad, an error occurs ... we need to translate it ...
//
if (protocolVersion == SnmpDefinitions.snmpVersionOne)
return mapErrorStatusToV1(errorStatus,reqPduType);
if (protocolVersion == SnmpDefinitions.snmpVersionTwo ||
protocolVersion == SnmpDefinitions.snmpVersionThree)
return mapErrorStatusToV2(errorStatus,reqPduType);
return SnmpDefinitions.snmpRspGenErr;
}
/**
* The method returns the error status of the operation.
* The method takes into account the protocol version.
*/
protected int getErrorStatus() {
if (errorStatus == snmpRspNoError)
return snmpRspNoError;
return mapErrorStatus(errorStatus,version,type);
}
/**
* The method returns the error index as a position in the var bind list.
* The value returned by the method corresponds to the index in the original
* var bind list as received by the SNMP protocol adaptor.
*/
protected int getErrorIndex() {
if (errorStatus == snmpRspNoError)
return -1;
// An error occurs. We need to be carefull because the index
// we are getting is a valid SNMP index (so range starts at 1).
// FIX ME: Shall we double-check the range here ?
// The response is : YES :
if ((errorIndex == 0) || (errorIndex == -1))
errorIndex = 1;
return translation[errorIndex -1];
}
/**
* The method updates the varbind list of the subrequest.
*/
protected void updateRequest(SnmpVarBind var, int pos) {
int size= varBind.size();
translation[size]= pos;
varBind.addElement(var);
}
/**
* The method updates a given var bind list with the result of a
* previsouly invoked operation.
* Prior to calling the method, one must make sure that the operation was
* successful. As such the method getErrorIndex or getErrorStatus should be
* called.
*/
protected void updateResult(SnmpVarBind[] result) {
if (result == null) return;
final int max=varBind.size();
final int len=result.length;
for(int i= 0; i< max ; i++) {
// bugId 4641694: must check position in order to avoid
// ArrayIndexOutOfBoundException
final int pos=translation[i];
if (pos < len) {
result[pos] =
(SnmpVarBind)((NonSyncVector)varBind).elementAtNonSync(i);
} else {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSubRequestHandler.class.getName(),
"updateResult","Position `"+pos+"' is out of bound...");
}
}
}
}
private void init(SnmpEngine engine,
SnmpIncomingRequest incRequest) {
this.incRequest = incRequest;
this.engine = engine;
}
// PRIVATE VARIABLES
//------------------
/**
* Store the protocol version to handle
*/
protected int version= snmpVersionOne;
/**
* Store the operation type. Remember if the type is Walk, it means
* that we have to invoke the check method ...
*/
protected int type= 0;
/**
* Agent directly handled by the sub-request handler.
*/
protected SnmpMibAgent agent;
/**
* Error status.
*/
protected int errorStatus= snmpRspNoError;
/**
* Index of error.
* A value of -1 means no error.
*/
protected int errorIndex= -1;
/**
* The varbind list specific to the current sub request.
* The vector must contain object of type SnmpVarBind.
*/
protected Vector<SnmpVarBind> varBind;
/**
* The array giving the index translation between the content of
* <VAR>varBind</VAR> and the varbind list as specified in the request.
*/
protected int[] translation;
/**
* Contextual object allocated by the SnmpUserDataFactory.
**/
protected Object data;
/**
* The SnmpMibRequest that will be passed to the agent.
*
**/
private SnmpMibRequest mibRequest = null;
/**
* The SnmpPdu that will be passed to the request.
*
**/
private SnmpPdu reqPdu = null;
// All the methods of the Vector class are synchronized.
// Synchronization is a very expensive operation. In our case it is not always
// required...
//
@SuppressWarnings("serial") // we never serialize this
class NonSyncVector<E> extends Vector<E> {
public NonSyncVector(int size) {
super(size);
}
final void addNonSyncElement(E obj) {
ensureCapacity(elementCount + 1);
elementData[elementCount++] = obj;
}
@SuppressWarnings("unchecked") // cast to E
final E elementAtNonSync(int index) {
return (E) elementData[index];
}
};
}