/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Nam Nguyen */ package com.caucho.server.snmp; import com.caucho.jmx.Jmx; import com.caucho.network.listen.AbstractProtocolConnection; import com.caucho.network.listen.SocketLink; import com.caucho.server.snmp.types.GetResponsePduValue; import com.caucho.server.snmp.types.IntegerValue; import com.caucho.server.snmp.types.NullValue; import com.caucho.server.snmp.types.ObjectIdentifierValue; import com.caucho.server.snmp.types.OctetStringValue; import com.caucho.server.snmp.types.SnmpMessageValue; import com.caucho.server.snmp.types.SnmpValue; import com.caucho.server.snmp.types.VarBindListValue; import com.caucho.server.snmp.types.VarBindValue; import com.caucho.util.L10N; import com.caucho.vfs.ReadStream; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import java.io.IOException; import java.io.OutputStream; import java.util.TreeMap; import java.util.logging.Logger; /* * Responds to SNMP requests. */ public class SnmpRequest extends AbstractProtocolConnection { private static final Logger log = Logger.getLogger(SnmpRequest.class.getName()); private static final L10N L = new L10N(SnmpRequest.class); public static final int NO_ERROR = 0; public static final int TOO_BIG = 1; public static final int NO_SUCH_NAME = 2; public static final int BAD_VALUE = 3; public static final int READ_ONLY = 4; public static final int GENERAL_ERROR = 5; private final SocketLink _connection; private IntegerValue _version = IntegerValue.ZERO; private final OctetStringValue _communityString; private TreeMap<String, Oid> _mibMap; public SnmpRequest(SocketLink connection, TreeMap<String, Oid> mibMap, OctetStringValue community) { _connection = connection; _mibMap = mibMap; _communityString = community; } /** * Initialize the connection. At this point, the current thread is the * connection thread. */ public void init() { } /** * Return true if the connection should wait for a read before * handling the request. */ public boolean isWaitForRead() { return true; } public void startConnection() { } /** * Handles a new connection. The controlling TcpServer may call * handleConnection again after the connection completes, so * the implementation must initialize any variables for each connection. */ public boolean handleRequest() throws IOException { ReadStream in = _connection.getReadStream(); SnmpParser parser = new SnmpParser(in); SnmpMessageValue req = parser.readMessage(); checkVersion(req); authenticate(req); SnmpMessageValue response = composeResponse(req); sendResponse(response); return true; } final protected void checkVersion(SnmpMessageValue req) { if (! _version.equals(req.getVersion())) { log.fine(L.l("expected version {0} != {1}", _version, req.getVersion())); throw new SnmpRuntimeException(L.l("expected version {0} != {1}", _version, req.getVersion())); } } final protected void authenticate(SnmpMessageValue req) { if (! _communityString.equals(req.getCommunityString())) { log.fine(L.l("non-matching community string")); throw new SnmpRuntimeException(L.l("non-matching community string")); } } final protected void sendResponse(SnmpValue response) throws IOException { OutputStream out = _connection.getWriteStream(); StringBuilder sb = new StringBuilder(); response.toAsn1(sb); for (int i = 0; i < sb.length(); i++) { out.write((byte) sb.charAt(i)); } out.flush(); } final protected SnmpMessageValue composeResponse(SnmpMessageValue req) { IntegerValue error = IntegerValue.ZERO; IntegerValue errorIndex = IntegerValue.ZERO; VarBindListValue varBindList = new VarBindListValue(); int i = -1; try { switch (req.getPdu().getType()) { case SnmpValue.GET_REQUEST_PDU: { ObjectIdentifierValue []oids = req.getVarBindList().getNames(); for (i = 0; i < oids.length; i++) { SnmpValue attr = getMBean(oids[i]); VarBindValue varBind; if (attr != null) varBind = new VarBindValue(oids[i], attr); else { varBind = new VarBindValue(oids[i], NullValue.NULL); if (error.getLong() == 0) { error = new IntegerValue(NO_SUCH_NAME); errorIndex = new IntegerValue(i + 1); } } varBindList.add(varBind); } break; } case SnmpValue.GET_NEXT_REQUEST_PDU: { ObjectIdentifierValue[] oids = req.getVarBindList().getNames(); for (ObjectIdentifierValue oid : oids) { String key = _mibMap.higherKey(oid.getString()); if (key == null) key = _mibMap.ceilingKey(oid.toString()); VarBindValue varBind; if (key != null) { ObjectIdentifierValue nextObjIdVal = new ObjectIdentifierValue(key); SnmpValue value = getMBean(nextObjIdVal); varBind = new VarBindValue(nextObjIdVal, value); } else { varBind = new VarBindValue(oid, NullValue.NULL); if (error.getLong() == 0) { error = new IntegerValue(NO_SUCH_NAME); errorIndex = new IntegerValue(i + 1); } } varBindList.add(varBind); } break; } case SnmpValue.SET_REQUEST_PDU: { break; } default: log.fine(L.l("invalid pdu type {0}", req.getType())); throw new SnmpRuntimeException(L.l("invalid pdu type {0}", req.getType())); } } catch (Exception e) { if (error.getLong() == 0) { error = new IntegerValue(GENERAL_ERROR); errorIndex = new IntegerValue(i + 1); } if (req.getVarBindList().size() > i) { VarBindValue varBind = new VarBindValue(req.getVarBindList().get(i).getName(), new OctetStringValue(e.getMessage())); varBindList.add(varBind); } } GetResponsePduValue pdu = new GetResponsePduValue(req.getRequestId(), error, errorIndex, varBindList); return new SnmpMessageValue(req.getVersion(), req.getCommunityString(), pdu); } //@In MBeanServer _jmx; protected SnmpValue getMBean(ObjectIdentifierValue objectIdentifier) { MBeanServer mbeanServer = getMBeanServer(); Oid oid = _mibMap.get(objectIdentifier.getString()); if (oid == null) return null; try { ObjectName mbean = new ObjectName(oid.getMbean()); Object attr = mbeanServer.getAttribute(mbean, oid.getAttribute()); //XXX: complex attributes that are not either Integer or String // i.e. from java.lang:type=Memory mbean return SnmpValue.create(attr, oid.getType()); } catch (MalformedObjectNameException e) { log.fine(e.getMessage()); throw new SnmpRuntimeException(e); } catch (AttributeNotFoundException e) { log.fine(e.getMessage()); return null; } catch (InstanceNotFoundException e) { log.fine(e.getMessage()); return null; } catch (MBeanException e) { log.fine(e.getMessage()); throw new SnmpRuntimeException(e); } catch (ReflectionException e) { log.fine(e.getMessage()); throw new SnmpRuntimeException(e); } catch (Exception e) { log.fine(e.getMessage()); return null; } } //@In MBeanServer _jmx; protected MBeanServer getMBeanServer() { return Jmx.getMBeanServer(); } /** * Resumes processing after a wait. */ public boolean handleResume() throws IOException { return false; } /** * Handles a close event when the connection is closed. */ public void onCloseConnection() { } /* * Sets the SNMP version. */ public void setVersion(int version) { _version = new IntegerValue(version); } public TreeMap<String, Oid> getMib() { return _mibMap; } }