001: /*_############################################################################
002: _##
003: _## SNMP4J-AgentX - AgentXProtocol.java
004: _##
005: _## Copyright (C) 2005-2007 Frank Fock (SNMP4J.org)
006: _##
007: _## This program is free software; you can redistribute it and/or modify
008: _## it under the terms of the GNU General Public License version 2 as
009: _## published by the Free Software Foundation.
010: _##
011: _## This program is distributed in the hope that it will be useful,
012: _## but WITHOUT ANY WARRANTY; without even the implied warranty of
013: _## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: _## GNU General Public License for more details.
015: _##
016: _## You should have received a copy of the GNU General Public License
017: _## along with this program; if not, write to the Free Software
018: _## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
019: _## MA 02110-1301 USA
020: _##
021: _##########################################################################*/
022:
023: package org.snmp4j.agent.agentx;
024:
025: import java.net.*;
026: import java.nio.*;
027: import java.util.*;
028:
029: import org.snmp4j.agent.*;
030: import org.snmp4j.smi.*;
031: import org.snmp4j.transport.MessageLengthDecoder;
032: import org.snmp4j.transport.MessageLength;
033: import java.io.IOException;
034: import org.snmp4j.log.LogAdapter;
035: import org.snmp4j.log.LogFactory;
036:
037: public class AgentXProtocol implements MessageLengthDecoder {
038:
039: private static final LogAdapter logger = LogFactory
040: .getLogger(AgentXProtocol.class);
041:
042: public static final byte VERSION_1_0 = 1;
043:
044: public static final byte REASON_OTHER = 1;
045: public static final byte REASON_PARSE_ERROR = 2;
046: public static final byte REASON_PROTOCOL_ERROR = 3;
047: public static final byte REASON_TIMEOUTS = 4;
048: public static final byte REASON_SHUTDOWN = 5;
049: public static final byte REASON_BY_MANAGER = 6;
050:
051: public static final int AGENTX_OPEN_FAILED = 256;
052: public static final int AGENTX_NOT_OPEN = 257;
053: public static final int AGENTX_INDEX_WRONG_TYPE = 258;
054: public static final int AGENTX_INDEX_ALREADY_ALLOCATED = 259;
055: public static final int AGENTX_INDEX_NONE_AVAILABLE = 260;
056: public static final int AGENTX_INDEX_NOT_ALLOCATED = 261;
057: public static final int AGENTX_UNSUPPORTED_CONTEXT = 262;
058: public static final int AGENTX_DUPLICATE_REGISTRATION = 263;
059: public static final int AGENTX_UNKNOWN_REGISTRATION = 264;
060: public static final int AGENTX_UNKNOWN_AGENTCAPS = 265;
061: public static final int AGENTX_PARSE_ERROR = 266;
062: public static final int AGENTX_REQUEST_DENIED = 267;
063: public static final int AGENTX_PROCESSING_ERROR = 268;
064:
065: /* General errors */
066:
067: public static final int AGENTX_SUCCESS = 0;
068: public static final int AGENTX_ERROR = -1;
069: public static final int AGENTX_DISCONNECT = -5;
070: public static final int AGENTX_BADVER = -10;
071: public static final int AGENTX_TIMEOUT = -11;
072:
073: /* User errors */
074: public static final int AGENTX_NOREG = -40;
075: public static final int AGENTX_DUPMAP = -41;
076:
077: public static final byte FLAG_INSTANCE_REGISTRATION = 0x01;
078: public static final byte FLAG_NEW_INDEX = 0x02;
079: public static final byte FLAG_ANY_INDEX = 0x04;
080: public static final byte FLAG_NON_DEFAULT_CONTEXT = 0x08;
081: public static final byte FLAG_NETWORK_BYTE_ORDER = 0x10;
082:
083: private static final OID INTERNET = new OID(
084: new int[] { 1, 3, 6, 1 });
085:
086: private static final int IPADDRESS_OCTETS = 4;
087:
088: protected static final int AGENTX_INT_SIZE = 4;
089:
090: public static final int HEADER_LENGTH = 5 * AGENTX_INT_SIZE;
091:
092: public static final int DEFAULT_TIMEOUT_SECONDS = 5;
093: public static final int DEFAULT_MAX_CONSECUTIVE_TIMEOUTS = 3;
094: public static final int MAX_TIMEOUT_SECONDS = 255;
095:
096: private static boolean nonDefaultContextEnabled = true;
097:
098: public static void encodeOID(ByteBuffer buf, OID oid,
099: boolean include) {
100: if (oid == null) {
101: buf.put(new byte[] { 0, 0, 0, 0 });
102: } else {
103: int startPos = 0;
104: if ((oid.size() > INTERNET.size())
105: && (oid.startsWith(INTERNET))) {
106: buf.put((byte) (oid.size() - (INTERNET.size() + 1)));
107: buf.put((byte) oid.get(INTERNET.size()));
108: startPos = INTERNET.size() + 1;
109: } else {
110: buf.put((byte) oid.size());
111: buf.put((byte) 0);
112: }
113: if ((include) && (oid.size() > 0)) {
114: buf.put((byte) 1);
115: buf.put((byte) 0);
116: } else {
117: buf.put(new byte[] { 0, 0 });
118: }
119: for (int i = startPos; i < oid.size(); i++) {
120: buf.putInt(oid.get(i));
121: }
122: }
123: }
124:
125: public static int getOIDLength(OID oid) {
126: if (oid == null) {
127: return AGENTX_INT_SIZE;
128: }
129: int startPos = 0;
130: if ((oid.size() > INTERNET.size())
131: && (oid.startsWith(INTERNET))) {
132: startPos = INTERNET.size() + 1;
133: }
134: return AGENTX_INT_SIZE
135: + (AGENTX_INT_SIZE * (oid.size() - startPos));
136: }
137:
138: public static boolean decodeOID(ByteBuffer buf, OID oid) {
139: int size = buf.get();
140: int first = buf.get();
141: int[] value = new int[size
142: + ((first != 0) ? INTERNET.size() + 1 : 0)];
143: int startPos = 0;
144: if (first != 0) {
145: System.arraycopy(INTERNET.getValue(), 0, value, 0, INTERNET
146: .size());
147: value[INTERNET.size()] = first;
148: startPos = INTERNET.size() + 1;
149: }
150: boolean include = (buf.get() != 0);
151: buf.get(); // reserved
152: for (int i = 0; i < size; i++) {
153: value[startPos + i] = buf.getInt();
154: }
155: oid.setValue(value);
156: return include;
157: }
158:
159: public static void encodeVariableData(ByteBuffer buf, Variable v) {
160: if (v == null) {
161: return;
162: }
163: switch (v.getSyntax()) {
164: //case sNMP_SYNTAX_INT:
165: case SMIConstants.SYNTAX_GAUGE32:
166: case SMIConstants.SYNTAX_TIMETICKS:
167: case SMIConstants.SYNTAX_COUNTER32: {
168: buf
169: .putInt((int) (((UnsignedInteger32) v).getValue() & 0xFFFFFFFFL));
170: break;
171: }
172: case SMIConstants.SYNTAX_INTEGER32: {
173: buf.putInt(((Integer32) v).getValue());
174: break;
175: }
176: case SMIConstants.SYNTAX_COUNTER64: {
177: buf.putLong(((Counter64) v).getValue());
178: break;
179: }
180: case SMIConstants.SYNTAX_OCTET_STRING:
181: case SMIConstants.SYNTAX_OPAQUE: {
182: encodeOctetString(buf, (OctetString) v);
183: break;
184: }
185: case SMIConstants.SYNTAX_IPADDRESS: {
186: encodeOctetString(buf, new OctetString(((IpAddress) v)
187: .getInetAddress().getAddress()));
188: //buf.put(((IpAddress)v).getInetAddress().getAddress());
189: break;
190: }
191: case SMIConstants.SYNTAX_OBJECT_IDENTIFIER: {
192: encodeOID(buf, (OID) v, false);
193: break;
194: }
195: default:
196: break;
197: }
198: }
199:
200: public static int getVariableDataLength(Variable v) {
201: if (v == null) {
202: return 0;
203: }
204: switch (v.getSyntax()) {
205: //case sNMP_SYNTAX_INT:
206: case SMIConstants.SYNTAX_GAUGE32:
207: case SMIConstants.SYNTAX_TIMETICKS:
208: case SMIConstants.SYNTAX_COUNTER32:
209: case SMIConstants.SYNTAX_INTEGER32: {
210: return AGENTX_INT_SIZE;
211: }
212: case SMIConstants.SYNTAX_COUNTER64: {
213: return 2 * AGENTX_INT_SIZE;
214: }
215: case SMIConstants.SYNTAX_OCTET_STRING:
216: case SMIConstants.SYNTAX_OPAQUE: {
217: return getOctetStringLength((OctetString) v);
218: }
219: case SMIConstants.SYNTAX_IPADDRESS: {
220: return 2 * AGENTX_INT_SIZE;
221: }
222: case SMIConstants.SYNTAX_OBJECT_IDENTIFIER: {
223: return getOIDLength((OID) v);
224: }
225: default:
226: break;
227: }
228: return 0;
229: }
230:
231: public static Variable decodeVariableData(ByteBuffer buf, int syntax) {
232: switch (syntax) {
233: //case sNMP_SYNTAX_INT:
234: case SMIConstants.SYNTAX_GAUGE32:
235: return new Gauge32((buf.getInt() & 0xFFFFFFFFL));
236: case SMIConstants.SYNTAX_TIMETICKS:
237: return new TimeTicks((buf.getInt() & 0xFFFFFFFFL));
238: case SMIConstants.SYNTAX_COUNTER32:
239: return new Counter32((buf.getInt() & 0xFFFFFFFFL));
240: case SMIConstants.SYNTAX_INTEGER32:
241: return new Integer32(buf.getInt());
242: case SMIConstants.SYNTAX_COUNTER64:
243: return new Counter64(buf.getLong());
244: case SMIConstants.SYNTAX_OCTET_STRING:
245: return decodeOctetString(buf);
246: case SMIConstants.SYNTAX_OPAQUE:
247: return new Opaque(decodeOctetString(buf).getValue());
248: case SMIConstants.SYNTAX_IPADDRESS: {
249: byte[] addrBytes = decodeOctetString(buf).getValue();
250: // Workaround for incorrectly implemented sub-agents like
251: // NET-SNMP 5.4, that return addresses with more than 4 bytes
252: if (addrBytes.length > IPADDRESS_OCTETS) {
253: logger
254: .warn("Subagent returned IpAddress with length "
255: + addrBytes.length
256: + " > "
257: + IPADDRESS_OCTETS
258: + " which violates AgentX protocol specification");
259: byte[] fourBytes = new byte[IPADDRESS_OCTETS];
260: System.arraycopy(addrBytes, 0, fourBytes, 0,
261: IPADDRESS_OCTETS);
262: addrBytes = fourBytes;
263: }
264: InetAddress addr = null;
265: try {
266: addr = InetAddress.getByAddress(addrBytes);
267: } catch (UnknownHostException ex) {
268: logger.error(
269: "Failed to create IpAddress from address bytes "
270: + " with length " + addrBytes.length
271: + ", using default IpAddress instead",
272: ex);
273: return new IpAddress();
274: }
275: return new IpAddress(addr);
276: }
277: case SMIConstants.SYNTAX_OBJECT_IDENTIFIER: {
278: OID oid = new OID();
279: decodeOID(buf, oid);
280: return oid;
281: }
282: case SMIConstants.EXCEPTION_END_OF_MIB_VIEW:
283: case SMIConstants.EXCEPTION_NO_SUCH_INSTANCE:
284: case SMIConstants.EXCEPTION_NO_SUCH_OBJECT: {
285: return new Null(syntax);
286: }
287: case SMIConstants.SYNTAX_NULL: {
288: return new Null();
289: }
290: default: {
291: logger.error("Unknown AgentX variable syntax '" + syntax
292: + "', using Null instead");
293: return new Null();
294: }
295: }
296: }
297:
298: public static VariableBinding[] decodeVariableBindings(
299: ByteBuffer buf) {
300: ArrayList vbs = new ArrayList();
301: while (buf.remaining() > 0) {
302: int type = buf.getShort() & 0xFFFF;
303: buf.getShort();
304: OID oid = new OID();
305: decodeOID(buf, oid);
306: Variable v = decodeVariableData(buf, type);
307: vbs.add(new VariableBinding(oid, v));
308: }
309: return (VariableBinding[]) vbs.toArray(new VariableBinding[vbs
310: .size()]);
311: }
312:
313: public static void encodeVaribleBindings(ByteBuffer buf,
314: VariableBinding[] vbs) {
315: for (int i = 0; i < vbs.length; i++) {
316: buf.putShort((short) vbs[i].getSyntax());
317: buf.put(new byte[] { 0, 0 }); // reserved
318: encodeOID(buf, vbs[i].getOid(), false);
319: encodeVariableData(buf, vbs[i].getVariable());
320: }
321: }
322:
323: public static void encodeRanges(ByteBuffer buf,
324: MOScope[] searchRanges) {
325: for (int i = 0; i < searchRanges.length; i++) {
326: encodeOID(buf, searchRanges[i].getLowerBound(),
327: searchRanges[i].isLowerIncluded());
328: if (searchRanges[i].isUpperIncluded()) {
329: encodeOID(buf, searchRanges[i].getUpperBound()
330: .successor(), false);
331: } else {
332: encodeOID(buf, searchRanges[i].getUpperBound(), false);
333: }
334: }
335: }
336:
337: public static int getOctetStringLength(OctetString os) {
338: int padding = 0;
339: if ((os.length() % AGENTX_INT_SIZE) > 0) {
340: padding = AGENTX_INT_SIZE - (os.length() % AGENTX_INT_SIZE);
341: }
342: return AGENTX_INT_SIZE + os.length() + padding;
343: }
344:
345: public static void encodeOctetString(ByteBuffer buf, OctetString os) {
346: buf.putInt(os.length());
347: buf.put(os.getValue());
348: if ((os.length() % AGENTX_INT_SIZE) > 0) {
349: for (int i = 0; i < AGENTX_INT_SIZE
350: - (os.length() % AGENTX_INT_SIZE); i++) {
351: buf.put((byte) 0);
352: }
353: }
354: }
355:
356: public static OctetString decodeOctetString(ByteBuffer buf) {
357: int size = buf.getInt();
358: byte[] value = new byte[size];
359: buf.get(value);
360: if ((size % AGENTX_INT_SIZE) > 0) {
361: for (int i = 0; i < AGENTX_INT_SIZE
362: - (size % AGENTX_INT_SIZE); i++) {
363: buf.get(); // skip 0 bytes
364: }
365: }
366: return new OctetString(value);
367: }
368:
369: public static MOScope[] decodeRanges(ByteBuffer buf) {
370: return decodeRanges(buf, false);
371: }
372:
373: public static MOScope[] decodeRanges(ByteBuffer buf,
374: boolean lowerAlwaysIncluded) {
375: ArrayList ranges = new ArrayList();
376: while (buf.hasRemaining()) {
377: OID lowerBound = new OID();
378: boolean isLowerIncluded = lowerAlwaysIncluded
379: | decodeOID(buf, lowerBound);
380: OID upperBound = new OID();
381: decodeOID(buf, upperBound);
382: if (upperBound.size() == 0) {
383: upperBound = null;
384: }
385: ranges.add(new DefaultMOScope(lowerBound, isLowerIncluded,
386: upperBound, false));
387: }
388: return (MOScope[]) ranges.toArray(new MOScope[ranges.size()]);
389: }
390:
391: public static int getRangesLength(MOScope[] ranges) {
392: int length = 0;
393: for (int i = 0; i < ranges.length; i++) {
394: length += AgentXProtocol.getOIDLength(ranges[i]
395: .getLowerBound());
396: if (ranges[i].isUpperIncluded()) {
397: length += AgentXProtocol.getOIDLength(ranges[i]
398: .getUpperBound().successor());
399: } else {
400: length += AgentXProtocol.getOIDLength(ranges[i]
401: .getUpperBound());
402: }
403: }
404: return length;
405: }
406:
407: public static int getVariableBindingsLength(VariableBinding[] vbs) {
408: int length = 0;
409: for (int i = 0; i < vbs.length; i++) {
410: length += AGENTX_INT_SIZE + getOIDLength(vbs[i].getOid())
411: + getVariableDataLength(vbs[i].getVariable());
412: }
413: return length;
414: }
415:
416: public int getMinHeaderLength() {
417: return HEADER_LENGTH;
418: }
419:
420: public MessageLength getMessageLength(ByteBuffer buf)
421: throws IOException {
422: return decodeHeader(buf);
423: }
424:
425: public static final AgentXMessageHeader decodeHeader(ByteBuffer buf)
426: throws IOException {
427: byte version = buf.get();
428: if (version != AgentXProtocol.VERSION_1_0) {
429: throw new IOException("Unknown AgentX version: " + version);
430: }
431: byte type = buf.get();
432: byte flags = buf.get();
433: buf.get();
434: ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN;
435: if ((flags & AgentXProtocol.FLAG_NETWORK_BYTE_ORDER) != 0) {
436: byteOrder = ByteOrder.BIG_ENDIAN;
437: }
438: buf.order(byteOrder);
439: int sessionID = buf.getInt();
440: int transactionID = buf.getInt();
441: int packetID = buf.getInt();
442: int length = buf.getInt();
443: return new AgentXMessageHeader(type, flags, sessionID,
444: transactionID, packetID, length);
445: }
446:
447: public static void setNonDefaultContextsEnabled(boolean enabled) {
448: nonDefaultContextEnabled = enabled;
449: }
450:
451: public static boolean isNonDefaultContextsEnabled() {
452: return nonDefaultContextEnabled;
453: }
454:
455: public static final byte DEFAULT_PRIORITY = 127;
456: public static final int FLAG_ALLOCATE_INDEX = 0;
457: }
|