/*
* @(#)file SnmpRequestHandler.java
* @(#)author Sun Microsystems, Inc.
* @(#)version 4.31
* @(#)date 06/05/03
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*/
package com.sun.jmx.snmp.daemon;
// java import
//
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
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 com.sun.jmx.trace.Trace;
// 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 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(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 (isTraceOn()) {
trace("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 (isTraceOn() && (respPacket != null)) {
trace("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 (isDebugOn()) {
if (e.getMessage().equals(InterruptSysCallMsg))
debug("doRun", "interrupted");
else {
debug("doRun", "i/o exception");
debug("doRun", e);
}
}
}
catch(InterruptedIOException e) {
if (isDebugOn()) {
debug("doRun", "interrupted");
}
}
catch(Exception e) {
if (isDebugOn()) {
debug("doRun", "failure when sending response");
debug("doRun", 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 (isDebugOn()) {
debug("makeResponsePacket", "packet decoding failed");
debug("makeResponsePacket", 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 (isDebugOn()) {
debug("makeResponsePacket", "response message is too big");
}
try {
respMsg = newTooBigMessage(reqMsg) ;
reqPacket.setLength(respMsg.encodeMessage(reqPacket.getData())) ;
respPacket = reqPacket ;
}
catch(SnmpTooBigException xx) {
if (isDebugOn()) {
debug("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 (isDebugOn()) {
debug("makeResponseMessage", "message decoding failed");
debug("makeResponseMessage",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 (isDebugOn()) {
debug("makeResponseMessage",
"failure when encoding the response message");
debug("makeResponseMessage", x);
}
}
catch(SnmpTooBigException x) {
if (isDebugOn()) {
debug("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 (isDebugOn()) {
debug("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 (isDebugOn()) {
debug("makeResponseMessage",
"response message is still too big");
}
old= pos;
pos= xx.getVarBindCount();
if (isDebugOn()) {
debug("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 (isDebugOn()) {
debug("makeResponseMessage",
"failure when encoding the response message");
debug("makeResponseMessage", xx);
}
}
catch(SnmpTooBigException xx) {
try {
respPdu = newTooBigPdu(reqPdu) ;
respMsg = (SnmpMessage)pduFactory.
encodeSnmpPdu(respPdu, packet.getData().length) ;
}
catch(SnmpTooBigException xxx) {
respMsg = null ;
if (isDebugOn()) {
debug("makeResponseMessage",
"'too big' is 'too big' !!!");
}
adaptor.incSnmpSilentDrops(1);
}
catch(Exception xxx) {
debug("makeResponseMessage", xxx);
respMsg = null ;
}
}
catch(Exception xx) {
debug("makeResponseMessage", 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 (isTraceOn()) {
trace("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 (isDebugOn()) {
debug("makeResponsePdu",
"failure when sending authentication trap");
debug("makeResponsePdu", x);
}
}
}
}
}
return respPdu ;
}
//
// Generates a response packet, filling the values in the
// varbindlist with one of endOfMibView, noSuchObject, noSuchInstance
// according to the value of status
//
// @param statusTag should be one of:
//