001: /*
002: * SNMP Package
003: *
004: * Copyright (C) 2004, Jonathan Sevy <jsevy@mcs.drexel.edu>
005: *
006: * This is free software. Redistribution and use in source and binary forms, with
007: * or without modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright notice, this
011: * list of conditions and the following disclaimer.
012: * 2. Redistributions in binary form must reproduce the above copyright notice,
013: * this list of conditions and the following disclaimer in the documentation
014: * and/or other materials provided with the distribution.
015: * 3. The name of the author may not be used to endorse or promote products
016: * derived from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
019: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
021: * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
023: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
025: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
026: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: */
029:
030: package snmp;
031:
032: import java.io.*;
033: import java.net.*;
034: import java.util.*;
035:
036: /**
037: * The class SNMPv1AgentInterface implements an interface for responding to requests sent from a remote SNMP
038: * manager. The agent simply listens for requests for information, and passes requested OIDs on to concrete
039: * subclasses of SNMPRequestListener. These are expected to retrieve requested information from the system,
040: * and return this to the agent interface for inclusion in a response to the manager.
041: * The approach is that from version 1 of SNMP, using no encryption of data. Communication occurs
042: * via UDP, using port 162, the standard SNMP trap port, as the destination port.
043: */
044:
045: public class SNMPv1AgentInterface implements Runnable {
046: public static final int SNMP_PORT = 161;
047:
048: // largest size for datagram packet payload; based on
049: // RFC 1157, need to handle messages of at least 484 bytes
050: public int receiveBufferSize = 512;
051:
052: int version = 0;
053:
054: private DatagramSocket dSocket;
055: private Thread receiveThread;
056: private Vector listenerVector;
057:
058: private PrintWriter errorLog;
059:
060: /**
061: * Construct a new agent object to listen for requests from remote SNMP managers. The agent listens
062: * on the standard SNMP UDP port 161.
063: */
064:
065: public SNMPv1AgentInterface(int version) throws SocketException {
066: this (version, SNMP_PORT, new PrintWriter(System.out));
067: }
068:
069: /**
070: * Construct a new agent object to listen for requests from remote SNMP managers. The agent listens
071: * on the supplied port.
072: */
073:
074: public SNMPv1AgentInterface(int version, int localPort)
075: throws SocketException {
076: this (version, localPort, new PrintWriter(System.out));
077: }
078:
079: /**
080: * Construct a new agent object to listen for requests from remote SNMP managers. The agent listens
081: * on the supplied port, and sends error messages to the specified PrintWriter.
082: */
083:
084: public SNMPv1AgentInterface(int version, PrintWriter errorReceiver)
085: throws SocketException {
086: this (version, SNMP_PORT, errorReceiver);
087: }
088:
089: /**
090: * Construct a new agent object to listen for requests from remote SNMP managers. The agent listens
091: * on the supplied port, and sends error messages to the specified PrintWriter.
092: */
093:
094: public SNMPv1AgentInterface(int version, int localPort,
095: PrintWriter errorReceiver) throws SocketException {
096:
097: this .version = version;
098:
099: dSocket = new DatagramSocket(localPort);
100:
101: listenerVector = new Vector();
102:
103: receiveThread = new Thread(this );
104:
105: errorLog = errorReceiver;
106:
107: }
108:
109: /**
110: * Set the specified PrintWriter to receive error messages.
111: */
112:
113: public void setErrorReceiver(PrintWriter errorReceiver) {
114: errorLog = errorReceiver;
115: }
116:
117: public void addRequestListener(SNMPRequestListener listener) {
118: // see if listener already added; if so, ignore
119: for (int i = 0; i < listenerVector.size(); i++) {
120: if (listener == listenerVector.elementAt(i)) {
121: return;
122: }
123: }
124:
125: // if got here, it's not in the list; add it
126: listenerVector.add(listener);
127: }
128:
129: public void removeRequestListener(SNMPRequestListener listener) {
130: // see if listener in list; if so, remove, if not, ignore
131: for (int i = 0; i < listenerVector.size(); i++) {
132: if (listener == listenerVector.elementAt(i)) {
133: listenerVector.removeElementAt(i);
134: break;
135: }
136: }
137:
138: }
139:
140: /**
141: * Start listening for requests from remote managers.
142: */
143:
144: public void startReceiving() {
145: // if receiveThread not already running, start it
146: if (!receiveThread.isAlive()) {
147: receiveThread = new Thread(this );
148: receiveThread.start();
149: }
150: }
151:
152: /**
153: * Stop listening for requests from remote managers.
154: */
155:
156: public void stopReceiving() throws SocketException {
157: // interrupt receive thread so it will die a natural death
158: receiveThread.interrupt();
159: }
160:
161: /**
162: * The run() method for the agent interface's listener. Just waits for SNMP request messages to
163: * come in on port 161 (or the port supplied in the constructor), then dispatches the retrieved
164: * SNMPPDU and community name to each of the registered SNMPRequestListeners by calling their
165: * processRequest() methods.
166: */
167:
168: public void run() {
169:
170: while (!receiveThread.isInterrupted()) {
171:
172: try {
173:
174: DatagramPacket inPacket = new DatagramPacket(
175: new byte[receiveBufferSize], receiveBufferSize);
176:
177: dSocket.receive(inPacket);
178:
179: InetAddress requesterAddress = inPacket.getAddress();
180: int requesterPort = inPacket.getPort();
181:
182: byte[] encodedMessage = inPacket.getData();
183:
184: /*
185: System.out.println("Message bytes length (in): " + inPacket.getLength());
186:
187: System.out.println("Message bytes (in):");
188: for (int i = 0; i < encodedMessage.length; ++i)
189: {
190: System.out.print(hexByte(encodedMessage[i]) + " ");
191: }
192: System.out.println("\n");
193: */
194:
195: SNMPMessage receivedMessage = new SNMPMessage(
196: SNMPBERCodec.extractNextTLV(encodedMessage, 0).value);
197:
198: String communityName = receivedMessage
199: .getCommunityName();
200: SNMPPDU receivedPDU = receivedMessage.getPDU();
201: byte requestPDUType = receivedPDU.getPDUType();
202:
203: //System.out.println("Received message; community = " + communityName + ", pdu type = " + Byte.toString(requestPDUType));
204: //System.out.println(" read community = " + readCommunityName + ", write community = " + writeCommunityName);
205:
206: SNMPSequence requestedVarList = receivedPDU
207: .getVarBindList();
208:
209: Hashtable variablePairHashtable = new Hashtable();
210: SNMPSequence responseVarList = new SNMPSequence();
211: int errorIndex = 0;
212: int errorStatus = SNMPRequestException.NO_ERROR;
213: int requestID = receivedPDU.getRequestID();
214:
215: try {
216:
217: // pass the received PDU and community name to the processRequest method of any listeners;
218: // handle differently depending on whether the request is a get-next, or a get or set
219:
220: if ((requestPDUType == SNMPBERCodec.SNMPGETREQUEST)
221: || (requestPDUType == SNMPBERCodec.SNMPSETREQUEST)) {
222:
223: // pass the received PDU and community name to any registered listeners
224: for (int i = 0; i < listenerVector.size(); i++) {
225: SNMPRequestListener listener = (SNMPRequestListener) listenerVector
226: .elementAt(i);
227:
228: // return value is sequence of variable pairs for those OIDs handled by the listener
229: SNMPSequence handledVarList = listener
230: .processRequest(receivedPDU,
231: communityName);
232:
233: // add to Hashtable of handled OIDs, if not already there
234: for (int j = 0; j < handledVarList.size(); j++) {
235:
236: SNMPSequence handledPair = (SNMPSequence) handledVarList
237: .getSNMPObjectAt(j);
238: SNMPObjectIdentifier snmpOID = (SNMPObjectIdentifier) handledPair
239: .getSNMPObjectAt(0);
240: SNMPObject snmpObject = (SNMPObject) handledPair
241: .getSNMPObjectAt(1);
242:
243: if (!variablePairHashtable
244: .containsKey(snmpOID)) {
245: variablePairHashtable.put(snmpOID,
246: snmpObject);
247: }
248:
249: }
250:
251: }
252:
253: // construct response containing the handled OIDs; if any OID not handled, throw exception
254: for (int j = 0; j < requestedVarList.size(); j++) {
255: SNMPSequence requestPair = (SNMPSequence) requestedVarList
256: .getSNMPObjectAt(j);
257: SNMPObjectIdentifier snmpOID = (SNMPObjectIdentifier) requestPair
258: .getSNMPObjectAt(0);
259:
260: // find corresponding SNMP object in hashtable
261: if (!variablePairHashtable
262: .containsKey(snmpOID)) {
263: errorIndex = j + 1;
264: errorStatus = SNMPRequestException.VALUE_NOT_AVAILABLE;
265:
266: if (requestPDUType == SNMPBERCodec.SNMPGETREQUEST)
267: throw new SNMPGetException("OID "
268: + snmpOID + " not handled",
269: errorIndex, errorStatus);
270: else
271: throw new SNMPSetException("OID "
272: + snmpOID + " not handled",
273: errorIndex, errorStatus);
274: }
275:
276: SNMPObject snmpObject = (SNMPObject) variablePairHashtable
277: .get(snmpOID);
278: SNMPVariablePair responsePair = new SNMPVariablePair(
279: snmpOID, snmpObject);
280:
281: responseVarList.addSNMPObject(responsePair);
282:
283: }
284:
285: } else if (requestPDUType == SNMPBERCodec.SNMPGETNEXTREQUEST) {
286: // pass the received PDU and community name to any registered listeners
287: for (int i = 0; i < listenerVector.size(); i++) {
288: SNMPRequestListener listener = (SNMPRequestListener) listenerVector
289: .elementAt(i);
290:
291: // return value is sequence of nested variable pairs for those OIDs handled by the listener:
292: // consists of (supplied OID, (following OID, value)) nested variable pairs
293: SNMPSequence handledVarList = listener
294: .processGetNextRequest(receivedPDU,
295: communityName);
296:
297: // add variable pair to Hashtable of handled OIDs, if not already there
298: for (int j = 0; j < handledVarList.size(); j++) {
299:
300: SNMPSequence handledPair = (SNMPSequence) handledVarList
301: .getSNMPObjectAt(j);
302: SNMPObjectIdentifier snmpOID = (SNMPObjectIdentifier) handledPair
303: .getSNMPObjectAt(0);
304: SNMPObject snmpObject = (SNMPObject) handledPair
305: .getSNMPObjectAt(1);
306:
307: if (!variablePairHashtable
308: .containsKey(snmpOID)) {
309: variablePairHashtable.put(snmpOID,
310: snmpObject);
311: }
312:
313: }
314:
315: }
316:
317: // construct response containing the handled OIDs; if any OID not handled, throw exception
318: for (int j = 0; j < requestedVarList.size(); j++) {
319: SNMPSequence requestPair = (SNMPSequence) requestedVarList
320: .getSNMPObjectAt(j);
321: SNMPObjectIdentifier snmpOID = (SNMPObjectIdentifier) requestPair
322: .getSNMPObjectAt(0);
323:
324: // find corresponding SNMP object in hashtable
325: if (!variablePairHashtable
326: .containsKey(snmpOID)) {
327: errorIndex = j + 1;
328: errorStatus = SNMPRequestException.VALUE_NOT_AVAILABLE;
329:
330: throw new SNMPGetException("OID "
331: + snmpOID + " not handled",
332: errorIndex, errorStatus);
333: }
334:
335: // value in hashtable is complete variable pair
336: SNMPVariablePair responsePair = (SNMPVariablePair) variablePairHashtable
337: .get(snmpOID);
338:
339: responseVarList.addSNMPObject(responsePair);
340:
341: }
342:
343: } else {
344: // some other PDU type; silently ignore
345: continue;
346: }
347:
348: } catch (SNMPRequestException e) {
349: // exception should contain the index and cause of error; return this in message
350: errorIndex = e.errorIndex;
351: errorStatus = e.errorStatus;
352:
353: // just return request variable list as response variable list
354: responseVarList = requestedVarList;
355: } catch (Exception e) {
356: // don't have a specific index and cause of error; return message as general error, index 0
357: errorIndex = 0;
358: errorStatus = SNMPRequestException.FAILED;
359:
360: // just return request variable list as response variable list
361: responseVarList = requestedVarList;
362:
363: // also report the exception locally
364: errorLog
365: .println("Exception while processing request: "
366: + e.toString());
367: errorLog.flush();
368: }
369:
370: SNMPPDU pdu = new SNMPPDU(SNMPBERCodec.SNMPGETRESPONSE,
371: requestID, errorStatus, errorIndex,
372: responseVarList);
373: SNMPMessage message = new SNMPMessage(version,
374: communityName, pdu);
375: byte[] messageEncoding = message.getBEREncoding();
376: DatagramPacket outPacket = new DatagramPacket(
377: messageEncoding, messageEncoding.length,
378: requesterAddress, requesterPort);
379:
380: dSocket.send(outPacket);
381:
382: } catch (IOException e) {
383: // just report the problem
384: errorLog
385: .println("IOException during request processing: "
386: + e.getMessage());
387: errorLog.flush();
388: } catch (SNMPBadValueException e) {
389: // just report the problem
390: errorLog
391: .println("SNMPBadValueException during request processing: "
392: + e.getMessage());
393: errorLog.flush();
394: } catch (Exception e) {
395: // just report the problem
396: errorLog
397: .println("Exception during request processing: "
398: + e.toString());
399: errorLog.flush();
400: }
401:
402: }
403:
404: }
405:
406: private String hexByte(byte b) {
407: int pos = b;
408: if (pos < 0)
409: pos += 256;
410: String returnString = new String();
411: returnString += Integer.toHexString(pos / 16);
412: returnString += Integer.toHexString(pos % 16);
413: return returnString;
414: }
415:
416: /**
417: * Set the size of the buffer used to receive response packets. RFC 1157 stipulates that an SNMP
418: * implementation must be able to receive packets of at least 484 bytes, so if you try to set the
419: * size to a value less than this, the receive buffer size will be set to 484 bytes. In addition,
420: * the maximum size of a UDP packet payload is 65535 bytes, so setting the buffer to a larger size
421: * will just waste memory. The default value is 512 bytes. The value may need to be increased if
422: * get-requests are issued for multiple OIDs.
423: */
424:
425: public void setReceiveBufferSize(int receiveBufferSize) {
426: if (receiveBufferSize >= 484) {
427: this .receiveBufferSize = receiveBufferSize;
428: } else {
429: this .receiveBufferSize = 484;
430: }
431: }
432:
433: /**
434: * Returns the current size of the buffer used to receive response packets.
435: */
436:
437: public int getReceiveBufferSize() {
438: return this.receiveBufferSize;
439: }
440:
441: }
|