001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Nam Nguyen
028: */
029:
030: package com.caucho.server.snmp;
031:
032: import java.io.IOException;
033: import java.io.OutputStream;
034: import java.util.HashMap;
035: import java.util.logging.Level;
036: import java.util.logging.Logger;
037:
038: import javax.management.AttributeNotFoundException;
039: import javax.management.InstanceNotFoundException;
040: import javax.management.MBeanException;
041: import javax.management.MBeanServer;
042: import javax.management.MalformedObjectNameException;
043: import javax.management.ObjectName;
044: import javax.management.ReflectionException;
045: import javax.webbeans.In;
046:
047: import com.caucho.jmx.Jmx;
048: import com.caucho.server.connection.Connection;
049: import com.caucho.server.port.ServerRequest;
050: import com.caucho.server.snmp.types.*;
051: import com.caucho.server.thread.ResinThreadPoolExecutor;
052: import com.caucho.util.L10N;
053: import com.caucho.vfs.ReadStream;
054:
055: /*
056: * Responds to SNMP requests.
057: */
058: public class SnmpRequest implements ServerRequest {
059: private static final Logger log = Logger
060: .getLogger(SnmpRequest.class.getName());
061:
062: private static final L10N L = new L10N(SnmpRequest.class);
063:
064: public static final int NO_ERROR = 0;
065: public static final int TOO_BIG = 1;
066: public static final int NO_SUCH_NAME = 2;
067: public static final int BAD_VALUE = 3;
068: public static final int READ_ONLY = 4;
069: public static final int GENERAL_ERROR = 5;
070:
071: private final Connection _connection;
072:
073: private IntegerValue _version = IntegerValue.ZERO;
074: private final OctetStringValue _communityString;
075:
076: private HashMap<String, Oid> _mibMap;
077:
078: public SnmpRequest(Connection connection,
079: HashMap<String, Oid> mibMap, OctetStringValue community) {
080: _connection = connection;
081: _mibMap = mibMap;
082:
083: _communityString = community;
084: }
085:
086: /**
087: * Initialize the connection. At this point, the current thread is the
088: * connection thread.
089: */
090: public void init() {
091: }
092:
093: /**
094: * Return true if the connection should wait for a read before
095: * handling the request.
096: */
097: public boolean isWaitForRead() {
098: return true;
099: }
100:
101: /**
102: * Handles a new connection. The controlling TcpServer may call
103: * handleConnection again after the connection completes, so
104: * the implementation must initialize any variables for each connection.
105: */
106: public boolean handleRequest() throws IOException {
107: ReadStream in = _connection.getReadStream();
108:
109: SnmpParser parser = new SnmpParser(in);
110: SnmpMessageValue req = parser.readMessage();
111:
112: checkVersion(req);
113: authenticate(req);
114:
115: SnmpMessageValue response = composeResponse(req);
116:
117: sendResponse(response);
118:
119: return true;
120: }
121:
122: final protected void checkVersion(SnmpMessageValue req) {
123: if (!_version.equals(req.getVersion())) {
124: log.fine(L.l("expected version {0} != {1}", _version, req
125: .getVersion()));
126:
127: throw new SnmpRuntimeException(L.l(
128: "expected version {0} != {1}", _version, req
129: .getVersion()));
130: }
131: }
132:
133: final protected void authenticate(SnmpMessageValue req) {
134: if (!_communityString.equals(req.getCommunityString())) {
135: log.fine(L.l("non-matching community string"));
136:
137: throw new SnmpRuntimeException(L
138: .l("non-matching community string"));
139: }
140: }
141:
142: final protected void sendResponse(SnmpValue response)
143: throws IOException {
144: OutputStream out = _connection.getWriteStream();
145:
146: StringBuilder sb = new StringBuilder();
147: response.toAsn1(sb);
148:
149: for (int i = 0; i < sb.length(); i++) {
150: out.write((byte) sb.charAt(i));
151: }
152: out.flush();
153: }
154:
155: final protected SnmpMessageValue composeResponse(
156: SnmpMessageValue req) {
157: IntegerValue error = IntegerValue.ZERO;
158: IntegerValue errorIndex = IntegerValue.ZERO;
159: VarBindListValue varBindList = new VarBindListValue();
160:
161: int i = -1;
162: try {
163: switch (req.getPdu().getType()) {
164: case SnmpValue.GET_REQUEST_PDU: {
165: ObjectIdentifierValue[] oids = req.getVarBindList()
166: .getNames();
167:
168: for (i = 0; i < oids.length; i++) {
169: SnmpValue attr = getMBean(oids[i]);
170:
171: VarBindValue varBind;
172:
173: if (attr != null)
174: varBind = new VarBindValue(oids[i], attr);
175: else {
176: varBind = new VarBindValue(oids[i],
177: NullValue.NULL);
178:
179: if (error.getLong() == 0) {
180: error = new IntegerValue(NO_SUCH_NAME);
181: errorIndex = new IntegerValue(i + 1);
182: }
183: }
184:
185: varBindList.add(varBind);
186: }
187:
188: break;
189: }
190: case SnmpValue.GET_NEXT_REQUEST_PDU: {
191: break;
192: }
193: case SnmpValue.SET_REQUEST_PDU: {
194: break;
195: }
196: default:
197: log.fine(L.l("invalid pdu type {0}", req.getType()));
198:
199: throw new SnmpRuntimeException(L.l(
200: "invalid pdu type {0}", req.getType()));
201: }
202: } catch (Exception e) {
203: if (error.getLong() == 0) {
204: error = new IntegerValue(GENERAL_ERROR);
205: errorIndex = new IntegerValue(i + 1);
206: }
207:
208: if (req.getVarBindList().size() > i) {
209: VarBindValue varBind = new VarBindValue(req
210: .getVarBindList().get(i).getName(),
211: new OctetStringValue(e.getMessage()));
212:
213: varBindList.add(varBind);
214: }
215: }
216:
217: GetResponsePduValue pdu = new GetResponsePduValue(req
218: .getRequestId(), error, errorIndex, varBindList);
219:
220: return new SnmpMessageValue(req.getVersion(), req
221: .getCommunityString(), pdu);
222:
223: }
224:
225: //@In MBeanServer _jmx;
226: protected SnmpValue getMBean(ObjectIdentifierValue objectIdentifier) {
227: MBeanServer mbeanServer = getMBeanServer();
228:
229: Oid oid = _mibMap.get(objectIdentifier.getString());
230:
231: if (oid == null)
232: return null;
233:
234: try {
235: ObjectName mbean = new ObjectName(oid.getMbean());
236:
237: Object attr = mbeanServer.getAttribute(mbean, oid
238: .getAttribute());
239:
240: //XXX: complex attributes that are not either Integer or String
241: // i.e. from java.lang:type=Memory mbean
242:
243: return SnmpValue.create(attr, oid.getType());
244: } catch (MalformedObjectNameException e) {
245: log.fine(e.getMessage());
246:
247: throw new SnmpRuntimeException(e);
248: } catch (AttributeNotFoundException e) {
249: log.fine(e.getMessage());
250:
251: return null;
252: } catch (InstanceNotFoundException e) {
253: log.fine(e.getMessage());
254:
255: return null;
256: } catch (MBeanException e) {
257: log.fine(e.getMessage());
258:
259: throw new SnmpRuntimeException(e);
260: } catch (ReflectionException e) {
261: log.fine(e.getMessage());
262:
263: throw new SnmpRuntimeException(e);
264: } catch (Exception e) {
265: log.fine(e.getMessage());
266:
267: return null;
268: }
269: }
270:
271: //@In MBeanServer _jmx;
272: protected MBeanServer getMBeanServer() {
273: return Jmx.getMBeanServer();
274: }
275:
276: /**
277: * Resumes processing after a wait.
278: */
279: public boolean handleResume() throws IOException {
280: return false;
281: }
282:
283: /**
284: * Handles a close event when the connection is closed.
285: */
286: public void protocolCloseEvent() {
287: }
288:
289: /*
290: * Sets the SNMP version.
291: */
292: public void setVersion(int version) {
293: _version = new IntegerValue(version);
294: }
295:
296: public HashMap<String, Oid> getMib() {
297: return _mibMap;
298: }
299: }
|