org.snmp4j.agent.CommandProcessor Maven / Gradle / Ivy
/*_############################################################################
_##
_## SNMP4J-Agent 3 - CommandProcessor.java
_##
_## Copyright (C) 2005-2018 Frank Fock (SNMP4J.org)
_##
_## Licensed under the Apache License, Version 2.0 (the "License");
_## you may not use this file except in compliance with the License.
_## You may obtain a copy of the License at
_##
_## http://www.apache.org/licenses/LICENSE-2.0
_##
_## Unless required by applicable law or agreed to in writing, software
_## distributed under the License is distributed on an "AS IS" BASIS,
_## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
_## See the License for the specific language governing permissions and
_## limitations under the License.
_##
_##########################################################################*/
package org.snmp4j.agent;
import java.util.*;
import org.snmp4j.*;
import org.snmp4j.agent.mo.GenericManagedObject;
import org.snmp4j.agent.mo.lock.LockRequest;
import org.snmp4j.agent.request.*;
import org.snmp4j.agent.security.*;
import org.snmp4j.event.*;
import org.snmp4j.mp.*;
import org.snmp4j.smi.*;
import org.snmp4j.util.*;
import org.snmp4j.agent.util.TemporaryList;
import org.snmp4j.agent.mo.snmp.CoexistenceInfo;
import org.snmp4j.agent.mo.snmp.CoexistenceInfoProvider;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
/**
* The {@code CommandProcessor} is the central glue code that puts together
* the various sub-systems of a SNMP agent.
*
* @author Frank Fock
* @version 3.1
*/
public class CommandProcessor implements CommandResponder, NotificationOriginator {
private static final LogAdapter logger =
LogFactory.getLogger(CommandProcessor.class);
/**
* The maximum request timeout supported by this command processor
* (by default 300.000 ms = 5 min).
*/
private static final int MAX_INTERNAL_REQUEST_TIMEOUT = 300000;
protected WorkerPool threadPool = null;
protected VACM vacm = null;
protected List moServers;
protected List ownContextEngineIDs = new ArrayList(2);
protected final List> pduHandler = new ArrayList<>();
protected TemporaryList requestList;
protected RequestFactory, PDU, SnmpRequest> requestFactory;
protected NotificationOriginator notificationOriginator;
protected ProxyMap proxyForwarder;
protected CoexistenceInfoProvider coexistenceProvider;
private transient List counterListeners;
/**
* Creates a {@code CommandProcessor} and registers all PDU types
* with the supplied contextEngineID as well as with
* {@link MPv3#LOCAL_ENGINE_ID} as required by RFC 5343.
*
* @param contextEngineID
* the custom engine ID to use (should equal the engineID
* of the agent, i.e. USM).
*/
public CommandProcessor(OctetString contextEngineID) {
this.ownContextEngineIDs.add(contextEngineID);
this.ownContextEngineIDs.add(MPv3.LOCAL_ENGINE_ID);
moServers = new ArrayList<>();
requestList = new TemporaryList<>(MAX_INTERNAL_REQUEST_TIMEOUT);
pduHandler.add(new GetHandler());
pduHandler.add(new GetNextHandler());
pduHandler.add(new SetHandler());
pduHandler.add(new GetBulkHandler());
requestFactory = new DefaultRequestFactory();
}
/**
* Sets the internal request timeout. Any request must return within this
* amount of milli-seconds. Default is five minutes.
*
* @param timeoutMillis
* the maximum number of milli-seconds a request can be processed.
*
* @since 1.3
*/
public void setInternalRequestTimeout(int timeoutMillis) {
requestList.setTimeout(timeoutMillis);
}
/**
* Gets the internal request timeout millis.
*
* @return the maximum number of milli-seconds a request can be processed.
* @since 1.3
*/
public int getInternalRequestTimeout() {
return requestList.getTimeout();
}
@Override
public void processPdu(CommandResponderEvent event) {
if (event.getPDU() != null) {
CoexistenceInfo cinfo = null;
OctetString sname = new OctetString(event.getSecurityName());
if (event.getPDU() instanceof ScopedPDU) {
ScopedPDU spdu = (ScopedPDU) event.getPDU();
cinfo = new CoexistenceInfo(sname, spdu.getContextEngineID(), spdu.getContextName());
} else if (coexistenceProvider != null) {
CoexistenceInfo[] cinfos = coexistenceProvider.getCoexistenceInfo(sname);
if ((cinfos != null) && (cinfos.length > 0)) {
for (CoexistenceInfo cinfo1 : cinfos) {
if (coexistenceProvider.passesFilter(event.getPeerAddress(), cinfo1)) {
cinfo = cinfo1;
break;
}
}
if (cinfo == null) {
logger.warn("Access attempt from " + event.getPeerAddress() +
" denied because of source address filtering");
fireIncrementCounter(new CounterEvent(this, SnmpConstants.snmpInBadCommunityNames));
return;
}
event.setMaxSizeResponsePDU(cinfo.getMaxMessageSize());
} else {
if (logger.isInfoEnabled()) {
logger.info("Community name '" + sname + "' not found in SNMP-COMMUNITY-MIB");
}
fireIncrementCounter(new CounterEvent(this, SnmpConstants.snmpInBadCommunityNames));
return;
}
}
if ((cinfo == null) || (ownContextEngineIDs.contains(cinfo.getContextEngineID()))) {
event.setProcessed(true);
Command command = new Command<>(event, cinfo);
if (threadPool != null) {
threadPool.execute(command);
} else {
command.run();
}
} else if (proxyForwarder != null) {
ProxyForwardRequest request = new ProxyForwardRequest<>(event, cinfo);
ProxyForwarder proxy = proxyForwarder.get(cinfo.getContextEngineID(), request.getProxyType());
ProxyCommand command = new ProxyCommand<>(proxy, request);
if (proxy != null) {
if (logger.isDebugEnabled()) {
logger.debug("Processsing proxy request with proxy forwarder " +
proxy);
}
if (threadPool != null) {
threadPool.execute(command);
} else {
command.run();
}
} else {
fireIncrementCounter(new CounterEvent(this, SnmpConstants.snmpProxyDrops));
}
} else {
fireIncrementCounter(new CounterEvent(this, SnmpConstants.snmpSilentDrops));
}
}
}
/**
* Sets the internal thread pool for task execution.
*
* @param threadPool
* a pool of workers/threads which can execute tasks.
*
* @deprecated Use {@link #setWorkerPool} instead
*/
@Deprecated
public void setThreadPool(WorkerPool threadPool) {
this.threadPool = threadPool;
}
/**
* Sets the internal thread pool for task execution.
*
* @param threadPool
* a pool of workers/threads which can execute tasks.
*
* @since 1.9
*/
public void setWorkerPool(WorkerPool threadPool) {
this.threadPool = threadPool;
}
public VACM getVacm() {
return vacm;
}
public void setVacm(VACM vacm) {
this.vacm = vacm;
}
public OctetString getContextEngineID() {
return ownContextEngineIDs.get(0);
}
public void setContextEngineID(OctetString contextEngineID) {
this.ownContextEngineIDs.set(0, contextEngineID);
}
/**
* Sends notification/inform messages to all registered targets. This method
* uses the internal {@link ThreadPool} to send the message(s) via the
* {@code NotificationOriginator}
* (see {@link #getNotificationOriginator}) to the targets specified by the
* SnmpTargetMIB and SnmpNotificationMIB instances supplied to the
* notification originator.
*
* @param context
* the context name of the context on whose behalf this notification has
* been generated.
* @param notificationID
* the object ID that uniquely identifies this notification. For SNMPv1
* traps, the notification ID has to be build using the rules provided
* by RFC 2576.
* @param vbs
* an array of {@code VariableBinding} instances representing the
* payload of the notification.
*
* @return an array of ResponseEvent instances or NotificationTask instance if
* the notification has been send asynchronously. Since the
* {@code NotificationOriginator} determines on behalf of the
* SNMP-NOTIFICATION-MIB contents whether a notification is sent as
* trap/notification or as inform request, the returned array will contain
* an element for each addressed target, but only a response PDU for
* inform targets.
*
* {@code null} will be returned when sending the notification failed
* because there is no {@link NotificationOriginator} set.
*
* NOTE: If this command processor is using a ThreadPool then the returned
* object will be {@link NotificationTask} instance. If all response have
* been received {@link Object#notify()} will be called on the returned
* {@code NotificationTask} object by the sending thread.
*/
public Object notify(final OctetString context,
final OID notificationID,
final VariableBinding[] vbs) {
return notify(context, notificationID, null, vbs);
}
public Object notify(OctetString context, OID notificationID,
TimeTicks sysUpTime, VariableBinding[] vbs) {
if (notificationOriginator != null) {
NotificationTask notifyTask =
new NotificationTask(notificationOriginator,
context, notificationID,
sysUpTime, vbs);
if (threadPool != null) {
threadPool.execute(notifyTask);
return notifyTask;
} else {
notifyTask.run();
return notifyTask.getResponses();
}
} else {
logger.warn("Could not sent notification '" + notificationID + "'=" +
Arrays.asList(vbs) + " because NotificationOriginator not set");
}
return null;
}
public void setNotificationOriginator(NotificationOriginator
notificationOriginator) {
this.notificationOriginator = notificationOriginator;
}
public void setCoexistenceProvider(CoexistenceInfoProvider
coexistenceProvider) {
this.coexistenceProvider = coexistenceProvider;
}
public ProxyForwarder addProxyForwarder(ProxyForwarder proxyForwarder,
OctetString contextEngineID,
int proxyType) {
if (this.proxyForwarder == null) {
this.proxyForwarder = new ProxyMap();
}
return this.proxyForwarder.add(proxyForwarder, contextEngineID, proxyType);
}
public ProxyForwarder removeProxyForwarder(OctetString contextEngineID,
int proxyType) {
if (proxyForwarder != null) {
return proxyForwarder.remove(contextEngineID, proxyType);
}
return null;
}
protected RequestHandler getHandler(int pduType) {
synchronized (pduHandler) {
for (RequestHandler handler : pduHandler) {
if (handler.isSupported(pduType)) {
return handler;
}
}
}
return null;
}
protected void dispatchCommand(CommandResponderEvent command, CoexistenceInfo cinfo) {
try {
RequestHandler handler = getHandler(command.getPDU().getType());
if (handler != null) {
processRequest(command, cinfo, handler);
} else {
sendUnknownPDUHandlersReport(command);
}
} catch (Exception ex) {
logger.error("Failed to dispatch command "+command+ " with "+cinfo+": "+ex.getMessage(), ex);
}
}
private void sendUnknownPDUHandlersReport(CommandResponderEvent> command) {
logger.info("No PDU handler found for request " + command);
CounterEvent counter =
new CounterEvent(this, SnmpConstants.snmpUnknownPDUHandlers);
fireIncrementCounter(counter);
if ((command.getMessageProcessingModel() == MessageProcessingModel.MPv3) &&
(command.getPDU() instanceof ScopedPDU)) {
ScopedPDU request = (ScopedPDU) command.getPDU();
ScopedPDU report = new ScopedPDU();
report.setContextEngineID(request.getContextEngineID());
report.setContextName(request.getContextName());
report.setType(PDU.REPORT);
report.add(new VariableBinding(counter.getOid(),
counter.getCurrentValue()));
sendResponse(command, report);
} else {
PDU resp = (PDU) command.getPDU().clone();
resp.setErrorStatus(PDU.genErr);
sendResponse(command, resp);
}
}
protected void processRequest(CommandResponderEvent command, CoexistenceInfo cinfo,
RequestHandler handler) {
SnmpRequest req = requestFactory.createRequest(command, cinfo);
requestList.add(req);
MOServer server = null;
OctetString context = req.getContext();
OctetString viewName = getViewName(command, cinfo, req.getViewType());
if (viewName == null) {
setAuthorizationError(req, VACM.VACM_NO_SUCH_VIEW);
} else {
req.setViewName(viewName);
server = getServer(context);
processRequest(server, handler, req);
}
finalizeRequest(command, req, server);
}
protected void reprocessRequest(MOServer server, SnmpRequest req) {
RequestHandler handler =
getHandler(req.getSource().getPDU().getType());
if (handler != null) {
req.resetProcessedStatus();
req.incReprocessCounter();
processRequest(server, handler, req);
} else {
sendUnknownPDUHandlersReport(req.getSource());
}
}
/**
* Processes (or re-process) a request and try to complete the request (thus
* to complete any incomplete subrequests).
*
* @param server
* the {@code MOServer} instance to use for accessing instrumentation.
* @param handler
* the {@code RequestHandler} to use to process the request.
* @param req
* the {@code Request}.
* @param
* the {@code Request} type to process.
* @param