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 SNMPTrapListenerInterface implements a server which listens for trap and inform request
038: * messages sent from remote SNMP entities. The approach is that from version 1 and 2c of SNMP, using no
039: * encryption of data. Communication occurs via UDP, using port 162, the standard SNMP trap port, or an
040: * alternate (non-standard) port supplied in the constructor. This interface can handle both SNMPv1 and
041: * SNMPv2 traps (which have different PDU types), and SNMPv2 Inform Requests.
042: *
043: * Applications utilize this class with classes which implement the SNMPTrapListener or SNMPv2TrapListener or
044: * SNMPv2InformRequestListener interfaces. These must provide a processTrap(), processv2Trap() or processInformRequest()
045: * method, and are registered/unregistered with this class through its addv1TrapListener()/removev1TrapListener(),
046: * addv2TrapListener()/removev2TrapListener(), or addv2InformRequestListener()/removev2InformRequestListener()
047: * methods.
048: */
049:
050: public class SNMPTrapReceiverInterface implements Runnable {
051: public static final int SNMP_TRAP_PORT = 162;
052:
053: // largest size for datagram packet payload; based on
054: // RFC 1157, need to handle messages of at least 484 bytes
055: private int receiveBufferSize = 512;
056:
057: private DatagramSocket dSocket;
058: private Thread receiveThread;
059:
060: private Vector v1TrapListenerVector;
061: private Vector v2TrapListenerVector;
062: private Vector v2InformRequestListenerVector;
063: private PrintWriter errorLog;
064:
065: /**
066: * Construct a new trap receiver object to receive traps from remote SNMP hosts.
067: * Uses the standard SNMP trap reception port 162, and System.out to report errors.
068: */
069:
070: public SNMPTrapReceiverInterface() throws SocketException {
071: // set System.out as the error writer
072: this (new PrintWriter(System.out));
073: }
074:
075: /**
076: * Construct a new trap receiver object to receive traps from remote SNMP hosts.
077: * Uses the specified Writer to deliver error messages, and the standard SNMP trap
078: * port 162.
079: */
080:
081: public SNMPTrapReceiverInterface(PrintWriter errorReceiver)
082: throws SocketException {
083: this (errorReceiver, SNMP_TRAP_PORT);
084: }
085:
086: /**
087: * Construct a new trap receiver object to receive traps from remote SNMP hosts.
088: * Uses the specified port for trap reception, and System.out to report errors.
089: */
090:
091: public SNMPTrapReceiverInterface(int trapReceivePort)
092: throws SocketException {
093: this (new PrintWriter(System.out), trapReceivePort);
094: }
095:
096: /**
097: * Construct a new trap receiver object to receive traps from remote SNMP hosts.
098: * This version will accept messages from all hosts using any community name. Uses the
099: * specified Writer to deliver error messages and the specified port to listen on.
100: */
101:
102: public SNMPTrapReceiverInterface(PrintWriter errorReceiver,
103: int trapReceivePort) throws SocketException {
104: dSocket = new DatagramSocket(trapReceivePort);
105:
106: v1TrapListenerVector = new Vector();
107: v2TrapListenerVector = new Vector();
108: v2InformRequestListenerVector = new Vector();
109:
110: receiveThread = new Thread(this );
111:
112: errorLog = errorReceiver;
113:
114: }
115:
116: /**
117: * Set the specified PrintWriter to receive error messages.
118: */
119:
120: public void setErrorReceiver(PrintWriter errorReceiver) {
121: errorLog = errorReceiver;
122: }
123:
124: public void addv1TrapListener(SNMPv1TrapListener listener) {
125: // see if listener already added; if so, ignore
126: for (int i = 0; i < v1TrapListenerVector.size(); i++) {
127: if (listener == v1TrapListenerVector.elementAt(i)) {
128: return;
129: }
130: }
131:
132: // if got here, it's not in the list; add it
133: v1TrapListenerVector.add(listener);
134: }
135:
136: public void removev1TrapListener(SNMPv1TrapListener listener) {
137: // see if listener in list; if so, remove, if not, ignore
138: for (int i = 0; i < v1TrapListenerVector.size(); i++) {
139: if (listener == v1TrapListenerVector.elementAt(i)) {
140: v1TrapListenerVector.removeElementAt(i);
141: break;
142: }
143: }
144:
145: }
146:
147: public void addv2TrapListener(SNMPv2TrapListener listener) {
148: // see if listener already added; if so, ignore
149: for (int i = 0; i < v2TrapListenerVector.size(); i++) {
150: if (listener == v2TrapListenerVector.elementAt(i)) {
151: return;
152: }
153: }
154:
155: // if got here, it's not in the list; add it
156: v2TrapListenerVector.add(listener);
157: }
158:
159: public void removev2TrapListener(SNMPv2TrapListener listener) {
160: // see if listener in list; if so, remove, if not, ignore
161: for (int i = 0; i < v2TrapListenerVector.size(); i++) {
162: if (listener == v2TrapListenerVector.elementAt(i)) {
163: v2TrapListenerVector.removeElementAt(i);
164: break;
165: }
166: }
167:
168: }
169:
170: public void addv2InformRequestListener(
171: SNMPv2InformRequestListener listener) {
172: // see if listener already added; if so, ignore
173: for (int i = 0; i < v2InformRequestListenerVector.size(); i++) {
174: if (listener == v2InformRequestListenerVector.elementAt(i)) {
175: return;
176: }
177: }
178:
179: // if got here, it's not in the list; add it
180: v2InformRequestListenerVector.add(listener);
181: }
182:
183: public void removev2InformRequestListener(
184: SNMPv2InformRequestListener listener) {
185: // see if listener in list; if so, remove, if not, ignore
186: for (int i = 0; i < v2InformRequestListenerVector.size(); i++) {
187: if (listener == v2InformRequestListenerVector.elementAt(i)) {
188: v2InformRequestListenerVector.removeElementAt(i);
189: break;
190: }
191: }
192:
193: }
194:
195: /**
196: * Start listening for trap and inform messages.
197: */
198:
199: public void startReceiving() {
200: // if receiveThread not already running, start it
201: if (!receiveThread.isAlive()) {
202: receiveThread = new Thread(this );
203: receiveThread.start();
204: }
205: }
206:
207: /**
208: * Stop listening for trap and inform messages.
209: */
210:
211: public void stopReceiving() throws SocketException {
212: // interrupt receive thread so it will die a natural death
213: receiveThread.interrupt();
214: }
215:
216: /**
217: * The run() method for the trap interface's listener. Just waits for trap or inform messages to
218: * come in on port 162, then dispatches the recieved PDUs to each of the registered
219: * listeners by calling their processTrap() or processInform() methods.
220: */
221:
222: public void run() {
223:
224: int errorStatus = 0;
225: int errorIndex = 0;
226:
227: while (!receiveThread.isInterrupted()) {
228:
229: try {
230:
231: DatagramPacket inPacket = new DatagramPacket(
232: new byte[receiveBufferSize], receiveBufferSize);
233:
234: dSocket.receive(inPacket);
235:
236: byte[] encodedMessage = inPacket.getData();
237:
238: // get IP address of sender, to supply with SNMPv2 traps
239: // which don't include this in the PDU
240: InetAddress agentIPAddress = inPacket.getAddress();
241:
242: /*
243: errorLog.println("Message bytes length (in): " + inPacket.getLength());
244:
245: errorLog.println("Message bytes (in):");
246: for (int i = 0; i < encodedMessage.length; ++i)
247: {
248: errorLog.print(hexByte(encodedMessage[i]) + " ");
249: }
250: errorLog.println("\n");
251: */
252:
253: SNMPMessage receivedMessage = new SNMPMessage(
254: SNMPBERCodec.extractNextTLV(encodedMessage, 0).value);
255:
256: String communityName = receivedMessage
257: .getCommunityName();
258:
259: Object receivedPDU = receivedMessage.getPDUAsObject();
260:
261: if (!(receivedPDU instanceof SNMPv1TrapPDU)
262: && !(receivedPDU instanceof SNMPv2TrapPDU)
263: && !(receivedPDU instanceof SNMPv2InformRequestPDU)) {
264: throw new SNMPBadValueException(
265: "PDU received that's not a v1 or v2 trap or inform request; message payload of type "
266: + receivedPDU.getClass().toString());
267: }
268:
269: // pass the received trap PDU to the processTrap or procesv2Trap method of any listeners
270: if (receivedPDU instanceof SNMPv1TrapPDU) {
271: for (int i = 0; i < v1TrapListenerVector.size(); i++) {
272: SNMPv1TrapListener listener = (SNMPv1TrapListener) v1TrapListenerVector
273: .elementAt(i);
274:
275: listener.processv1Trap(
276: (SNMPv1TrapPDU) receivedPDU,
277: communityName);
278: }
279: } else if (receivedPDU instanceof SNMPv2TrapPDU) {
280: for (int i = 0; i < v2TrapListenerVector.size(); i++) {
281: SNMPv2TrapListener listener = (SNMPv2TrapListener) v2TrapListenerVector
282: .elementAt(i);
283:
284: listener.processv2Trap(
285: (SNMPv2TrapPDU) receivedPDU,
286: communityName, agentIPAddress);
287: }
288: } else if (receivedPDU instanceof SNMPv2InformRequestPDU) {
289: for (int i = 0; i < v2InformRequestListenerVector
290: .size(); i++) {
291: SNMPv2InformRequestListener listener = (SNMPv2InformRequestListener) v2InformRequestListenerVector
292: .elementAt(i);
293:
294: listener.processv2InformRequest(
295: (SNMPv2InformRequestPDU) receivedPDU,
296: communityName, agentIPAddress);
297: }
298: }
299:
300: } catch (IOException e) {
301: // just report the problem
302: errorLog
303: .println("IOException during request processing: "
304: + e.toString());
305: errorLog.flush();
306: } catch (SNMPBadValueException e) {
307: // just report the problem
308: errorLog
309: .println("SNMPBadValueException during request processing: "
310: + e.toString());
311: errorLog.flush();
312: } catch (Exception e) {
313: // just report the problem
314: errorLog
315: .println("Exception during request processing: "
316: + e.toString());
317: errorLog.flush();
318: }
319:
320: }
321:
322: }
323:
324: private String hexByte(byte b) {
325: int pos = b;
326: if (pos < 0)
327: pos += 256;
328: String returnString = new String();
329: returnString += Integer.toHexString(pos / 16);
330: returnString += Integer.toHexString(pos % 16);
331: return returnString;
332: }
333:
334: private String getHex(byte theByte) {
335: int b = theByte;
336:
337: if (b < 0)
338: b += 256;
339:
340: String returnString = new String(Integer.toHexString(b));
341:
342: // add leading 0 if needed
343: if (returnString.length() % 2 == 1)
344: returnString = "0" + returnString;
345:
346: return returnString;
347: }
348:
349: /**
350: * Set the size of the buffer used to receive response packets. RFC 1157 stipulates that an SNMP
351: * implementation must be able to receive packets of at least 484 bytes, so if you try to set the
352: * size to a value less than this, the receive buffer size will be set to 484 bytes. In addition,
353: * the maximum size of a UDP packet payload is 65535 bytes, so setting the buffer to a larger size
354: * will just waste memory. The default value is 512 bytes. The value may need to be increased if
355: * get-requests are issued for multiple OIDs.
356: */
357:
358: public void setReceiveBufferSize(int receiveBufferSize) {
359: if (receiveBufferSize >= 484) {
360: this .receiveBufferSize = receiveBufferSize;
361: } else {
362: this .receiveBufferSize = 484;
363: }
364: }
365:
366: /**
367: * Returns the current size of the buffer used to receive packets.
368: */
369:
370: public int getReceiveBufferSize() {
371: return this.receiveBufferSize;
372: }
373:
374: }
|