/* * 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]; } }; }