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