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