blob: 53b9f0bd7df56ce3646ed5944452840a3babe472 [file] [log] [blame]
/*
* 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.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.logging.Level;
import java.io.InterruptedIOException;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.SocketException;
// jmx imports
//
import javax.management.MBeanServer;
import javax.management.ObjectName;
import com.sun.jmx.snmp.SnmpMessage;
import com.sun.jmx.snmp.SnmpPduFactory;
import com.sun.jmx.snmp.SnmpPduBulk;
import com.sun.jmx.snmp.SnmpPduPacket;
import com.sun.jmx.snmp.SnmpPduRequest;
import com.sun.jmx.snmp.SnmpPduTrap;
import com.sun.jmx.snmp.SnmpValue;
import com.sun.jmx.snmp.SnmpVarBind;
import com.sun.jmx.snmp.SnmpVarBindList;
import com.sun.jmx.snmp.SnmpDefinitions;
import com.sun.jmx.snmp.SnmpStatusException;
import com.sun.jmx.snmp.SnmpTooBigException;
import com.sun.jmx.snmp.SnmpDataTypeEnums;
// RI imports
//
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
// SNMP runtime import
//
import com.sun.jmx.snmp.agent.SnmpMibAgent;
import com.sun.jmx.snmp.agent.SnmpUserDataFactory;
//import com.sun.jmx.snmp.IPAcl.IPAcl;
import com.sun.jmx.snmp.InetAddressAcl;
class SnmpRequestHandler extends ClientHandler implements SnmpDefinitions {
private transient DatagramSocket socket = null ;
private transient DatagramPacket packet = null ;
private transient Vector mibs = null ;
/**
* Contains the list of sub-requests associated to the current request.
*/
private transient Hashtable<SnmpMibAgent, SnmpSubRequestHandler> subs = null;
/**
* Reference on the MIBS
*/
private transient SnmpMibTree root;
private transient Object ipacl = null ;
private transient SnmpPduFactory pduFactory = null ;
private transient SnmpUserDataFactory userDataFactory = null ;
private transient SnmpAdaptorServer adaptor = null;
/**
* Full constructor
*/
public SnmpRequestHandler(SnmpAdaptorServer server, int id,
DatagramSocket s, DatagramPacket p,
SnmpMibTree tree, Vector m, Object a,
SnmpPduFactory factory,
SnmpUserDataFactory dataFactory,
MBeanServer f, ObjectName n)
{
super(server, id, f, n);
// Need a reference on SnmpAdaptorServer for getNext & getBulk,
// in case of oid equality (mib overlapping).
//
adaptor = server;
socket = s;
packet = p;
root= tree;
mibs = (Vector) m.clone();
subs= new Hashtable<SnmpMibAgent, SnmpSubRequestHandler>(mibs.size());
ipacl = a;
pduFactory = factory ;
userDataFactory = dataFactory ;
//thread.start();
}
/**
* Treat the request available in 'packet' and send the result
* back to the client.
* Note: we overwrite 'packet' with the response bytes.
*/
public void doRun() {
// Trace the input packet
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"doRun","Packet received:\n" +
SnmpMessage.dumpHexBuffer(packet.getData(), 0, packet.getLength()));
}
// Let's build the response packet
//
DatagramPacket respPacket = makeResponsePacket(packet) ;
// Trace the output packet
//
if ((SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) && (respPacket != null)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"doRun","Packet to be sent:\n" +
SnmpMessage.dumpHexBuffer(respPacket.getData(), 0, respPacket.getLength()));
}
// Send the response packet if any
//
if (respPacket != null) {
try {
socket.send(respPacket) ;
} catch (SocketException e) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
if (e.getMessage().equals(InterruptSysCallMsg)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"doRun", "interrupted");
} else {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"doRun", "I/O exception", e);
}
}
} catch(InterruptedIOException e) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"doRun", "interrupted");
}
} catch(Exception e) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"doRun", "failure when sending response", e);
}
}
}
}
/**
* Here we make a response packet from a request packet.
* We return null if there no response packet to sent.
*/
private DatagramPacket makeResponsePacket(DatagramPacket reqPacket) {
DatagramPacket respPacket = null ;
// Transform the request packet into a request SnmpMessage
//
SnmpMessage reqMsg = new SnmpMessage() ;
try {
reqMsg.decodeMessage(reqPacket.getData(), reqPacket.getLength()) ;
reqMsg.address = reqPacket.getAddress() ;
reqMsg.port = reqPacket.getPort() ;
}
catch(SnmpStatusException x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponsePacket", "packet decoding failed", x);
}
reqMsg = null ;
((SnmpAdaptorServer)adaptorServer).incSnmpInASNParseErrs(1) ;
}
// Make the response SnmpMessage if any
//
SnmpMessage respMsg = null ;
if (reqMsg != null) {
respMsg = makeResponseMessage(reqMsg) ;
}
// Try to transform the response SnmpMessage into response packet.
// NOTE: we overwrite the request packet.
//
if (respMsg != null) {
try {
reqPacket.setLength(respMsg.encodeMessage(reqPacket.getData())) ;
respPacket = reqPacket ;
}
catch(SnmpTooBigException x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponsePacket", "response message is too big");
}
try {
respMsg = newTooBigMessage(reqMsg) ;
reqPacket.setLength(respMsg.encodeMessage(reqPacket.getData())) ;
respPacket = reqPacket ;
}
catch(SnmpTooBigException xx) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponsePacket", "'too big' is 'too big' !!!");
}
adaptor.incSnmpSilentDrops(1);
}
}
}
return respPacket ;
}
/**
* Here we make a response message from a request message.
* We return null if there is no message to reply.
*/
private SnmpMessage makeResponseMessage(SnmpMessage reqMsg) {
SnmpMessage respMsg = null ;
// Transform the request message into a request pdu
//
SnmpPduPacket reqPdu = null ;
Object userData = null;
try {
reqPdu = (SnmpPduPacket)pduFactory.decodeSnmpPdu(reqMsg) ;
if (reqPdu != null && userDataFactory != null)
userData = userDataFactory.allocateUserData(reqPdu);
}
catch(SnmpStatusException x) {
reqPdu = null ;
SnmpAdaptorServer snmpServer = (SnmpAdaptorServer)adaptorServer ;
snmpServer.incSnmpInASNParseErrs(1) ;
if (x.getStatus()== SnmpDefinitions.snmpWrongSnmpVersion)
snmpServer.incSnmpInBadVersions(1) ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "message decoding failed", x);
}
}
// Make the response pdu if any
//
SnmpPduPacket respPdu = null ;
if (reqPdu != null) {
respPdu = makeResponsePdu(reqPdu,userData) ;
try {
if (userDataFactory != null)
userDataFactory.releaseUserData(userData,respPdu);
} catch (SnmpStatusException x) {
respPdu = null;
}
}
// Try to transform the response pdu into a response message if any
//
if (respPdu != null) {
try {
respMsg = (SnmpMessage)pduFactory.
encodeSnmpPdu(respPdu, packet.getData().length) ;
}
catch(SnmpStatusException x) {
respMsg = null ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "failure when encoding the response message", x);
}
}
catch(SnmpTooBigException x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "response message is too big");
}
try {
// if the PDU is too small, why should we try to do
// recovery ?
//
if (packet.getData().length <=32)
throw x;
int pos= x.getVarBindCount();
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "fail on element" + pos);
}
int old= 0;
while (true) {
try {
respPdu = reduceResponsePdu(reqPdu, respPdu, pos) ;
respMsg = (SnmpMessage)pduFactory.
encodeSnmpPdu(respPdu,
packet.getData().length -32) ;
break;
} catch (SnmpTooBigException xx) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "response message is still too big");
}
old= pos;
pos= xx.getVarBindCount();
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage","fail on element" + pos);
}
if (pos == old) {
// we can not go any further in trying to
// reduce the message !
//
throw xx;
}
}
}// end of loop
} catch(SnmpStatusException xx) {
respMsg = null ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "failure when encoding the response message", xx);
}
}
catch(SnmpTooBigException xx) {
try {
respPdu = newTooBigPdu(reqPdu) ;
respMsg = (SnmpMessage)pduFactory.
encodeSnmpPdu(respPdu, packet.getData().length) ;
}
catch(SnmpTooBigException xxx) {
respMsg = null ;
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "'too big' is 'too big' !!!");
}
adaptor.incSnmpSilentDrops(1);
}
catch(Exception xxx) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "Got unexpected exception", xxx);
}
respMsg = null ;
}
}
catch(Exception xx) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponseMessage", "Got unexpected exception", xx);
}
respMsg = null ;
}
}
}
return respMsg ;
}
/**
* Here we make a response pdu from a request pdu.
* We return null if there is no pdu to reply.
*/
private SnmpPduPacket makeResponsePdu(SnmpPduPacket reqPdu,
Object userData) {
SnmpAdaptorServer snmpServer = (SnmpAdaptorServer)adaptorServer ;
SnmpPduPacket respPdu = null ;
snmpServer.updateRequestCounters(reqPdu.type) ;
if (reqPdu.varBindList != null)
snmpServer.updateVarCounters(reqPdu.type,
reqPdu.varBindList.length) ;
if (checkPduType(reqPdu)) {
respPdu = checkAcl(reqPdu) ;
if (respPdu == null) { // reqPdu is accepted by ACLs
if (mibs.size() < 1) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"makeResponsePdu", "Request " + reqPdu.requestId +
" received but no MIB registered.");
}
return makeNoMibErrorPdu((SnmpPduRequest)reqPdu, userData);
}
switch(reqPdu.type) {
case SnmpPduPacket.pduGetRequestPdu:
case SnmpPduPacket.pduGetNextRequestPdu:
case SnmpPduPacket.pduSetRequestPdu:
respPdu = makeGetSetResponsePdu((SnmpPduRequest)reqPdu,
userData) ;
break ;
case SnmpPduPacket.pduGetBulkRequestPdu:
respPdu = makeGetBulkResponsePdu((SnmpPduBulk)reqPdu,
userData) ;
break ;
}
}
else { // reqPdu is rejected by ACLs
// respPdu contains the error response to be sent.
// We send this response only if authResEnabled is true.
if (!snmpServer.getAuthRespEnabled()) { // No response should be sent
respPdu = null ;
}
if (snmpServer.getAuthTrapEnabled()) { // A trap must be sent
try {
snmpServer.snmpV1Trap(SnmpPduTrap.
trapAuthenticationFailure, 0,
new SnmpVarBindList()) ;
}
catch(Exception x) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"makeResponsePdu", "Failure when sending authentication trap", x);
}
}
}
}
}
return respPdu ;
}
//
// Generates a response packet, filling the values in the
// varbindlist with one of endOfMibView, noSuchObject, noSuchInstance
// according to the value of <code>status</code>
//
// @param statusTag should be one of:
// <li>SnmpDataTypeEnums.errEndOfMibViewTag</li>
// <li>SnmpDataTypeEnums.errNoSuchObjectTag</li>
// <li>SnmpDataTypeEnums.errNoSuchInstanceTag</li>
//
SnmpPduPacket makeErrorVarbindPdu(SnmpPduPacket req, int statusTag) {
final SnmpVarBind[] vblist = req.varBindList;
final int length = vblist.length;
switch (statusTag) {
case SnmpDataTypeEnums.errEndOfMibViewTag:
for (int i=0 ; i<length ; i++)
vblist[i].value = SnmpVarBind.endOfMibView;
break;
case SnmpDataTypeEnums.errNoSuchObjectTag:
for (int i=0 ; i<length ; i++)
vblist[i].value = SnmpVarBind.noSuchObject;
break;
case SnmpDataTypeEnums.errNoSuchInstanceTag:
for (int i=0 ; i<length ; i++)
vblist[i].value = SnmpVarBind.noSuchInstance;
break;
default:
return newErrorResponsePdu(req,snmpRspGenErr,1);
}
return newValidResponsePdu(req,vblist);
}
// Generates an appropriate response when no mib is registered in
// the adaptor.
//
// <li>If the version is V1:</li>
// <ul><li>Generates a NoSuchName error V1 response PDU</li></ul>
// <li>If the version is V2:</li>
// <ul><li>If the request is a GET, fills the varbind list with
// NoSuchObject's</li>
// <li>If the request is a GET-NEXT/GET-BULK, fills the varbind
// list with EndOfMibView's</li>
// <li>If the request is a SET, generates a NoAccess error V2
// response PDU</li>
// </ul>
//
//
SnmpPduPacket makeNoMibErrorPdu(SnmpPduRequest req, Object userData) {
// There is no agent registered
//
if (req.version == SnmpDefinitions.snmpVersionOne) {
// Version 1: => NoSuchName
return
newErrorResponsePdu(req,snmpRspNoSuchName,1);
} else if (req.version == SnmpDefinitions.snmpVersionTwo) {
// Version 2: => depends on PDU type
switch (req.type) {
case pduSetRequestPdu :
case pduWalkRequest :
// SET request => NoAccess
return
newErrorResponsePdu(req,snmpRspNoAccess,1);
case pduGetRequestPdu :
// GET request => NoSuchObject
return
makeErrorVarbindPdu(req,SnmpDataTypeEnums.
errNoSuchObjectTag);
case pduGetNextRequestPdu :
case pduGetBulkRequestPdu :
// GET-NEXT or GET-BULK => EndOfMibView
return
makeErrorVarbindPdu(req,SnmpDataTypeEnums.
errEndOfMibViewTag);
default:
}
}
// Something wrong here: => snmpRspGenErr
return newErrorResponsePdu(req,snmpRspGenErr,1);
}
/**
* Here we make the response pdu from a get/set request pdu.
* At this level, the result is never null.
*/
private SnmpPduPacket makeGetSetResponsePdu(SnmpPduRequest req,
Object userData) {
// Create the trhead group specific for handling sub-requests
// associated to the current request. Use the invoke id
//
// Nice idea to use a thread group on a request basis.
// However the impact on performance is terrible !
// theGroup= new ThreadGroup(thread.getThreadGroup(),
// "request " + String.valueOf(req.requestId));
// Let's build the varBindList for the response pdu
//
if (req.varBindList == null) {
// Good ! Let's make a full response pdu.
//
return newValidResponsePdu(req, null) ;
}
// First we need to split the request into subrequests
//
splitRequest(req);
int nbSubRequest= subs.size();
if (nbSubRequest == 1)
return turboProcessingGetSet(req,userData);
// Execute all the subrequests resulting from the split of the
// varbind list.
//
SnmpPduPacket result= executeSubRequest(req,userData);
if (result != null)
// It means that an error occured. The error is already
// formatted by the executeSubRequest
// method.
return result;
// So far so good. So we need to concatenate all the answers.
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"makeGetSetResponsePdu",
"Build the unified response for request " + req.requestId);
}
return mergeResponses(req);
}
/**
* The method runs all the sub-requests associated to the current
* instance of SnmpRequestHandler.
*/
private SnmpPduPacket executeSubRequest(SnmpPduPacket req,
Object userData) {
int errorStatus = SnmpDefinitions.snmpRspNoError ;
int nbSubRequest= subs.size();
int i=0;
// If it's a set request, we must first check any varBind
//
if (req.type == pduSetRequestPdu) {
i=0;
for(Enumeration e= subs.elements(); e.hasMoreElements() ; i++) {
// Indicate to the sub request that a check must be invoked ...
// OK we should have defined out own tag for that !
//
SnmpSubRequestHandler sub= (SnmpSubRequestHandler)
e.nextElement();
sub.setUserData(userData);
sub.type= pduWalkRequest;
sub.run();
sub.type= pduSetRequestPdu;
if (sub.getErrorStatus() != SnmpDefinitions.snmpRspNoError) {
// No point to go any further.
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"executeSubRequest", "an error occurs");
}
return newErrorResponsePdu(req, errorStatus,
sub.getErrorIndex() + 1) ;
}
}
}// end processing check operation for a set PDU.
// Let's start the sub-requests.
//
i=0;
for(Enumeration e= subs.elements(); e.hasMoreElements() ;i++) {
SnmpSubRequestHandler sub= (SnmpSubRequestHandler) e.nextElement();
/* NPCTE fix for bugId 4492741, esc 0, 16-August 2001 */
sub.setUserData(userData);
/* end of NPCTE fix for bugId 4492741 */
sub.run();
if (sub.getErrorStatus() != SnmpDefinitions.snmpRspNoError) {
// No point to go any further.
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"executeSubRequest", "an error occurs");
}
return newErrorResponsePdu(req, errorStatus,
sub.getErrorIndex() + 1) ;
}
}
// everything is ok
//
return null;
}
/**
* Optimize when there is only one sub request
*/
private SnmpPduPacket turboProcessingGetSet(SnmpPduRequest req,
Object userData) {
int errorStatus = SnmpDefinitions.snmpRspNoError ;
SnmpSubRequestHandler sub = subs.elements().nextElement();
sub.setUserData(userData);
// Indicate to the sub request that a check must be invoked ...
// OK we should have defined out own tag for that !
//
if (req.type == SnmpDefinitions.pduSetRequestPdu) {
sub.type= pduWalkRequest;
sub.run();
sub.type= pduSetRequestPdu;
// Check the error status.
//
errorStatus= sub.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
// No point to go any further.
//
return newErrorResponsePdu(req, errorStatus,
sub.getErrorIndex() + 1) ;
}
}
// process the operation
//
sub.run();
errorStatus= sub.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
// No point to go any further.
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"turboProcessingGetSet", "an error occurs");
}
int realIndex= sub.getErrorIndex() + 1;
return newErrorResponsePdu(req, errorStatus, realIndex) ;
}
// So far so good. So we need to concatenate all the answers.
//
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"turboProcessingGetSet", "build the unified response for request "
+ req.requestId);
}
return mergeResponses(req);
}
/**
* Here we make the response pdu for a bulk request.
* At this level, the result is never null.
*/
private SnmpPduPacket makeGetBulkResponsePdu(SnmpPduBulk req,
Object userData) {
SnmpVarBind[] respVarBindList = null ;
// RFC 1905, Section 4.2.3, p14
int L = req.varBindList.length ;
int N = Math.max(Math.min(req.nonRepeaters, L), 0) ;
int M = Math.max(req.maxRepetitions, 0) ;
int R = L - N ;
if (req.varBindList == null) {
// Good ! Let's make a full response pdu.
//
return newValidResponsePdu(req, null) ;
}
// Split the request into subrequests.
//
splitBulkRequest(req, N, M, R);
SnmpPduPacket result= executeSubRequest(req,userData);
if (result != null)
return result;
respVarBindList= mergeBulkResponses(N + (M * R));
// Now we remove useless trailing endOfMibView.
//
int m2 ; // respVarBindList[m2] item and next are going to be removed
int t = respVarBindList.length ;
while ((t > N) && (respVarBindList[t-1].
value.equals(SnmpVarBind.endOfMibView))) {
t-- ;
}
if (t == N)
m2 = N + R ;
else
m2 = N + ((t -1 -N) / R + 2) * R ; // Trivial, of course...
if (m2 < respVarBindList.length) {
SnmpVarBind[] truncatedList = new SnmpVarBind[m2] ;
for (int i = 0 ; i < m2 ; i++) {
truncatedList[i] = respVarBindList[i] ;
}
respVarBindList = truncatedList ;
}
// Good ! Let's make a full response pdu.
//
return newValidResponsePdu(req, respVarBindList) ;
}
/**
* Check the type of the pdu: only the get/set/bulk request
* are accepted.
*/
private boolean checkPduType(SnmpPduPacket pdu) {
boolean result = true ;
switch(pdu.type) {
case SnmpDefinitions.pduGetRequestPdu:
case SnmpDefinitions.pduGetNextRequestPdu:
case SnmpDefinitions.pduSetRequestPdu:
case SnmpDefinitions.pduGetBulkRequestPdu:
result = true ;
break;
default:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"checkPduType", "cannot respond to this kind of PDU");
}
result = false ;
break;
}
return result ;
}
/**
* Check if the specified pdu is conform to the ACL.
* This method returns null if the pdu is ok. If not, it returns
* the response pdu to be replied.
*/
private SnmpPduPacket checkAcl(SnmpPduPacket pdu) {
SnmpPduPacket response = null ;
String community = new String(pdu.community) ;
// We check the pdu type and create an error response if
// the check failed.
//
if (ipacl != null) {
if (pdu.type == SnmpDefinitions.pduSetRequestPdu) {
if (!((InetAddressAcl)ipacl).
checkWritePermission(pdu.address, community)) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"checkAcl", "sender is " + pdu.address +
" with " + community +". Sender has no write permission");
}
int err = SnmpSubRequestHandler.
mapErrorStatus(SnmpDefinitions.
snmpRspAuthorizationError,
pdu.version, pdu.type);
response = newErrorResponsePdu(pdu, err, 0) ;
}
else {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"checkAcl", "sender is " + pdu.address +
" with " + community +". Sender has write permission");
}
}
}
else {
if (!((InetAddressAcl)ipacl).checkReadPermission(pdu.address, community)) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"checkAcl", "sender is " + pdu.address +
" with " + community +". Sender has no read permission");
}
int err = SnmpSubRequestHandler.
mapErrorStatus(SnmpDefinitions.
snmpRspAuthorizationError,
pdu.version, pdu.type);
response = newErrorResponsePdu(pdu,
err,
0);
SnmpAdaptorServer snmpServer =
(SnmpAdaptorServer)adaptorServer;
snmpServer.updateErrorCounters(SnmpDefinitions.
snmpRspNoSuchName);
}
else {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"checkAcl", "sender is " + pdu.address +
" with " + community +". Sender has read permission");
}
}
}
}
// If the response is not null, this means the pdu is rejected.
// So let's update the statistics.
//
if (response != null) {
SnmpAdaptorServer snmpServer = (SnmpAdaptorServer)adaptorServer ;
snmpServer.incSnmpInBadCommunityUses(1) ;
if (((InetAddressAcl)ipacl).checkCommunity(community) == false)
snmpServer.incSnmpInBadCommunityNames(1) ;
}
return response ;
}
/**
* Make a response pdu with the specified error status and index.
* NOTE: the response pdu share its varBindList with the request pdu.
*/
private SnmpPduRequest newValidResponsePdu(SnmpPduPacket reqPdu,
SnmpVarBind[] varBindList) {
SnmpPduRequest result = new SnmpPduRequest() ;
result.address = reqPdu.address ;
result.port = reqPdu.port ;
result.version = reqPdu.version ;
result.community = reqPdu.community ;
result.type = result.pduGetResponsePdu ;
result.requestId = reqPdu.requestId ;
result.errorStatus = SnmpDefinitions.snmpRspNoError ;
result.errorIndex = 0 ;
result.varBindList = varBindList ;
((SnmpAdaptorServer)adaptorServer).
updateErrorCounters(result.errorStatus) ;
return result ;
}
/**
* Make a response pdu with the specified error status and index.
* NOTE: the response pdu share its varBindList with the request pdu.
*/
private SnmpPduRequest newErrorResponsePdu(SnmpPduPacket req,int s,int i) {
SnmpPduRequest result = newValidResponsePdu(req, null) ;
result.errorStatus = s ;
result.errorIndex = i ;
result.varBindList = req.varBindList ;
((SnmpAdaptorServer)adaptorServer).
updateErrorCounters(result.errorStatus) ;
return result ;
}
private SnmpMessage newTooBigMessage(SnmpMessage reqMsg)
throws SnmpTooBigException {
SnmpMessage result = null ;
SnmpPduPacket reqPdu = null ;
try {
reqPdu = (SnmpPduPacket)pduFactory.decodeSnmpPdu(reqMsg) ;
if (reqPdu != null) {
SnmpPduPacket respPdu = newTooBigPdu(reqPdu) ;
result = (SnmpMessage)pduFactory.
encodeSnmpPdu(respPdu, packet.getData().length) ;
}
}
catch(SnmpStatusException x) {
// This should not occur because decodeIncomingRequest has normally
// been successfully called before.
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"newTooBigMessage", "Internal error", x);
}
throw new InternalError() ;
}
return result ;
}
private SnmpPduPacket newTooBigPdu(SnmpPduPacket req) {
SnmpPduRequest result =
newErrorResponsePdu(req, SnmpDefinitions.snmpRspTooBig, 0) ;
result.varBindList = null ;
return result ;
}
private SnmpPduPacket reduceResponsePdu(SnmpPduPacket req,
SnmpPduPacket resp,
int acceptedVbCount)
throws SnmpTooBigException {
// Reduction can be attempted only on bulk response
//
if (req.type != req.pduGetBulkRequestPdu) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"reduceResponsePdu", "cannot remove anything");
}
throw new SnmpTooBigException(acceptedVbCount) ;
}
// We're going to reduce the varbind list.
// First determine which items should be removed.
// Next duplicate and replace the existing list by the reduced one.
//
// acceptedVbCount is the number of varbind which have been
// successfully encoded before reaching bufferSize:
// * when it is >= 2, we split the varbindlist at this
// position (-1 to be safe),
// * when it is 1, we only put one (big?) item in the varbindlist
// * when it is 0 (in fact, acceptedVbCount is not available),
// we split the varbindlist by 2.
//
int vbCount = resp.varBindList.length ;
if (acceptedVbCount >= 3)
vbCount = Math.min(acceptedVbCount - 1, resp.varBindList.length) ;
else if (acceptedVbCount == 1)
vbCount = 1 ;
else // acceptedCount == 0 ie it is unknown
vbCount = resp.varBindList.length / 2 ;
if (vbCount < 1) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"reduceResponsePdu", "cannot remove anything");
}
throw new SnmpTooBigException(acceptedVbCount) ;
}
else {
SnmpVarBind[] newVbList = new SnmpVarBind[vbCount] ;
for (int i = 0 ; i < vbCount ; i++) {
newVbList[i] = resp.varBindList[i] ;
}
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
"reduceResponsePdu", (resp.varBindList.length - newVbList.length) +
" items have been removed");
}
resp.varBindList = newVbList ;
}
return resp ;
}
/**
* The method takes the incoming requests and split it into subrequests.
*/
private void splitRequest(SnmpPduRequest req) {
int nbAgents= mibs.size();
SnmpMibAgent agent= (SnmpMibAgent) mibs.firstElement();
if (nbAgents == 1) {
// Take all the oids contained in the request and
//
subs.put(agent, new SnmpSubRequestHandler(agent, req, true));
return;
}
// For the get next operation we are going to send the varbind list
// to all agents
//
if (req.type == pduGetNextRequestPdu) {
for(Enumeration e= mibs.elements(); e.hasMoreElements(); ) {
SnmpMibAgent ag= (SnmpMibAgent) e.nextElement();
subs.put(ag, new SnmpSubNextRequestHandler(adaptor, ag, req));
}
return;
}
int nbReqs= req.varBindList.length;
SnmpVarBind[] vars= req.varBindList;
SnmpSubRequestHandler sub;
for(int i=0; i < nbReqs; i++) {
agent= root.getAgentMib(vars[i].oid);
sub= subs.get(agent);
if (sub == null) {
// We need to create the sub request handler and update
// the hashtable
//
sub= new SnmpSubRequestHandler(agent, req);
subs.put(agent, sub);
}
// Update the translation table within the subrequest
//
sub.updateRequest(vars[i], i);
}
}
/**
* The method takes the incoming get bulk requests and split it into
* subrequests.
*/
private void splitBulkRequest(SnmpPduBulk req,
int nonRepeaters,
int maxRepetitions,
int R) {
// Send the getBulk to all agents
//
for(Enumeration e= mibs.elements(); e.hasMoreElements(); ) {
SnmpMibAgent agent = (SnmpMibAgent) e.nextElement();
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
"splitBulkRequest", "Create a sub with : " + agent + " " + nonRepeaters
+ " " + maxRepetitions + " " + R);
}
subs.put(agent,
new SnmpSubBulkRequestHandler(adaptor,
agent,
req,
nonRepeaters,
maxRepetitions,
R));
}
return;
}
private SnmpPduPacket mergeResponses(SnmpPduRequest req) {
if (req.type == pduGetNextRequestPdu) {
return mergeNextResponses(req);
}
SnmpVarBind[] result= req.varBindList;
// Go through the list of subrequests and concatenate.
// Hopefully, by now all the sub-requests should be finished
//
for(Enumeration e= subs.elements(); e.hasMoreElements();) {
SnmpSubRequestHandler sub= (SnmpSubRequestHandler) e.nextElement();
sub.updateResult(result);
}
return newValidResponsePdu(req,result);
}
private SnmpPduPacket mergeNextResponses(SnmpPduRequest req) {
int max= req.varBindList.length;
SnmpVarBind[] result= new SnmpVarBind[max];
// Go through the list of subrequests and concatenate.
// Hopefully, by now all the sub-requests should be finished
//
for(Enumeration e= subs.elements(); e.hasMoreElements();) {
SnmpSubRequestHandler sub= (SnmpSubRequestHandler) e.nextElement();
sub.updateResult(result);
}
if (req.version == snmpVersionTwo) {
return newValidResponsePdu(req,result);
}
// In v1 make sure there is no endOfMibView ...
//
for(int i=0; i < max; i++) {
SnmpValue val= result[i].value;
if (val == SnmpVarBind.endOfMibView)
return newErrorResponsePdu(req,
SnmpDefinitions.snmpRspNoSuchName, i+1);
}
// So far so good ...
//
return newValidResponsePdu(req,result);
}
private SnmpVarBind[] mergeBulkResponses(int size) {
// Let's allocate the array for storing the result
//
SnmpVarBind[] result= new SnmpVarBind[size];
for(int i= size-1; i >=0; --i) {
result[i]= new SnmpVarBind();
result[i].value= SnmpVarBind.endOfMibView;
}
// Go through the list of subrequests and concatenate.
// Hopefully, by now all the sub-requests should be finished
//
for(Enumeration e= subs.elements(); e.hasMoreElements();) {
SnmpSubRequestHandler sub= (SnmpSubRequestHandler) e.nextElement();
sub.updateResult(result);
}
return result;
}
protected String makeDebugTag() {
return "SnmpRequestHandler[" + adaptorServer.getProtocol() + ":" +
adaptorServer.getPort() + "]";
}
Thread createThread(Runnable r) {
return null;
}
static final private String InterruptSysCallMsg =
"Interrupted system call";
static final private SnmpStatusException noSuchNameException =
new SnmpStatusException(SnmpDefinitions.snmpRspNoSuchName) ;
}