1 /* 2 * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.jmx.snmp; 26 27 import java.net.InetAddress; 28 import java.io.Serializable; 29 import java.net.UnknownHostException; 30 import java.util.StringTokenizer; 31 import java.util.Arrays; 32 import java.util.NoSuchElementException; 33 34 import com.sun.jmx.snmp.internal.SnmpTools; 35 36 /** 37 * This class is handling an <CODE>SnmpEngineId</CODE> data. It copes with binary as well as <CODE>String</CODE> representation of an engine Id. A string format engine is an hex string starting with 0x. 38 * <p><b>This API is a Sun Microsystems internal API and is subject 39 * to change without notice.</b></p> 40 * @since 1.5 41 */ 42 public class SnmpEngineId implements Serializable { 43 private static final long serialVersionUID = 5434729655830763317L; 44 45 byte[] engineId = null; 46 String hexString = null; 47 String humanString = null; 48 /** 49 * New <CODE>SnmpEngineId</CODE> with an hex string value. Can handle engine Id format <host>:<port>. 50 * @param hexString Hexa string. 51 */ 52 SnmpEngineId(String hexString) { 53 engineId = SnmpTools.ascii2binary(hexString); 54 this.hexString = hexString.toLowerCase(); 55 } 56 /** 57 * New <CODE>SnmpEngineId</CODE> with a binary value. You can use <CODE> SnmpTools </CODE> to convert from hex string to binary format. 58 * @param bin Binary value 59 */ 60 SnmpEngineId(byte[] bin) { 61 engineId = bin; 62 hexString = SnmpTools.binary2ascii(bin).toLowerCase(); 63 } 64 65 /** 66 * If a string of the format <address>:<port>:<IANA number> has been provided at creation time, this string is returned. 67 * @return The Id as a readable string or null if not provided. 68 */ 69 public String getReadableId() { 70 return humanString; 71 } 72 73 /** 74 * Returns a string format engine Id. 75 * @return String format value. 76 */ 77 public String toString() { 78 return hexString; 79 } 80 /** 81 * Returns a binary engine Id. 82 * @return Binary value. 83 */ 84 public byte[] getBytes() { 85 return engineId; 86 } 87 88 /** 89 * In order to store the string used to create the engineId. 90 */ 91 void setStringValue(String val) { 92 humanString = val; 93 } 94 95 static void validateId(String str) throws IllegalArgumentException { 96 byte[] arr = SnmpTools.ascii2binary(str); 97 validateId(arr); 98 } 99 100 static void validateId(byte[] arr) throws IllegalArgumentException { 101 102 if(arr.length < 5) throw new IllegalArgumentException("Id size lower than 5 bytes."); 103 if(arr.length > 32) throw new IllegalArgumentException("Id size greater than 32 bytes."); 104 105 //octet strings with very first bit = 0 and length != 12 octets 106 if( ((arr[0] & 0x80) == 0) && arr.length != 12) 107 throw new IllegalArgumentException("Very first bit = 0 and length != 12 octets"); 108 109 byte[] zeroedArrays = new byte[arr.length]; 110 if(Arrays.equals(zeroedArrays, arr)) throw new IllegalArgumentException("Zeroed Id."); 111 byte[] FFArrays = new byte[arr.length]; 112 Arrays.fill(FFArrays, (byte)0xFF); 113 if(Arrays.equals(FFArrays, arr)) throw new IllegalArgumentException("0xFF Id."); 114 115 } 116 117 /** 118 * Generates an engine Id based on the passed array. 119 * @return The created engine Id or null if given arr is null or its length == 0; 120 * @exception IllegalArgumentException when: 121 * <ul> 122 * <li>octet string lower than 5 bytes.</li> 123 * <li>octet string greater than 32 bytes.</li> 124 * <li>octet string = all zeros.</li> 125 * <li>octet string = all 'ff'H.</li> 126 * <li>octet strings with very first bit = 0 and length != 12 octets</li> 127 * </ul> 128 */ 129 public static SnmpEngineId createEngineId(byte[] arr) throws IllegalArgumentException { 130 if( (arr == null) || arr.length == 0) return null; 131 validateId(arr); 132 return new SnmpEngineId(arr); 133 } 134 135 /** 136 * Generates an engine Id that is unique to the host the agent is running on. The engine Id unicity is system time based. The creation algorithm uses the SUN Microsystems IANA number (42). 137 * @return The generated engine Id. 138 */ 139 public static SnmpEngineId createEngineId() { 140 byte[] address = null; 141 byte[] engineid = new byte[13]; 142 int iana = 42; 143 long mask = 0xFF; 144 long time = System.currentTimeMillis(); 145 146 engineid[0] = (byte) ( (iana & 0xFF000000) >> 24 ); 147 engineid[0] |= 0x80; 148 engineid[1] = (byte) ( (iana & 0x00FF0000) >> 16 ); 149 engineid[2] = (byte) ( (iana & 0x0000FF00) >> 8 ); 150 engineid[3] = (byte) (iana & 0x000000FF); 151 engineid[4] = 0x05; 152 153 engineid[5] = (byte) ( (time & (mask << 56)) >>> 56 ); 154 engineid[6] = (byte) ( (time & (mask << 48) ) >>> 48 ); 155 engineid[7] = (byte) ( (time & (mask << 40) ) >>> 40 ); 156 engineid[8] = (byte) ( (time & (mask << 32) ) >>> 32 ); 157 engineid[9] = (byte) ( (time & (mask << 24) ) >>> 24 ); 158 engineid[10] = (byte) ( (time & (mask << 16) ) >>> 16 ); 159 engineid[11] = (byte) ( (time & (mask << 8) ) >>> 8 ); 160 engineid[12] = (byte) (time & mask); 161 162 return new SnmpEngineId(engineid); 163 } 164 165 /** 166 * Translates an engine Id in an SnmpOid format. This is useful when dealing with USM MIB indexes. 167 * The oid format is : <engine Id length>.<engine Id binary octet1>....<engine Id binary octetn - 1>.<engine Id binary octetn> 168 * Eg: "0x8000002a05819dcb6e00001f96" ==> 13.128.0.0.42.5.129.157.203.110.0.0.31.150 169 * 170 * @return SnmpOid The oid. 171 */ 172 public SnmpOid toOid() { 173 long[] oid = new long[engineId.length + 1]; 174 oid[0] = engineId.length; 175 for(int i = 1; i <= engineId.length; i++) 176 oid[i] = (long) (engineId[i-1] & 0xFF); 177 return new SnmpOid(oid); 178 } 179 180 /** 181 * <P>Generates a unique engine Id. Hexadecimal strings as well as a textual description are supported. The textual format is as follow: 182 * <BR> <address>:<port>:<IANA number></P> 183 * <P>The allowed formats :</P> 184 * <ul> 185 * <li> <address>:<port>:<IANA number> 186 * <BR> All these parameters are used to generate the Id. WARNING, this method is not compliant with IPv6 address format. Use { @link com.sun.jmx.snmp.SnmpEngineId#createEngineId(java.lang.String,java.lang.String) } instead.</li> 187 * <li> <address>:<port> 188 * <BR> The IANA number will be the SUN Microsystems one (42). </li> 189 * <li> address 190 * <BR> The port 161 will be used to generate the Id. IANA number will be the SUN Microsystems one (42). </li> 191 * <li> :port 192 * <BR> The host to use is localhost. IANA number will be the SUN Microsystems one (42). </li> 193 * <li> ::<IANA number> 194 * <BR> The port 161 and localhost will be used to generate the Id. </li> 195 * <li> :<port>:<IANA number> 196 * <BR> The host to use is localhost. </li> 197 * <li> <address>::<IANA number> 198 * <BR> The port 161 will be used to generate the Id. </li> 199 * <li> :: 200 * <BR> The port 161, localhost and the SUN Microsystems IANA number will be used to generate the Id. </li> 201 * </ul> 202 * @exception UnknownHostException if the host name contained in the textual format is unknown. 203 * @exception IllegalArgumentException when : 204 * <ul> 205 * <li>octet string lower than 5 bytes.</li> 206 * <li>octet string greater than 32 bytes.</li> 207 * <li>octet string = all zeros.</li> 208 * <li>octet string = all 'ff'H.</li> 209 * <li>octet strings with very first bit = 0 and length != 12 octets</li> 210 * <li>An IPv6 address format is used in conjonction with the ":" separator</li> 211 * </ul> 212 * @param str The string to parse. 213 * @return The generated engine Id or null if the passed string is null. 214 * 215 */ 216 public static SnmpEngineId createEngineId(String str) 217 throws IllegalArgumentException, UnknownHostException { 218 return createEngineId(str, null); 219 } 220 221 /** 222 * Idem { @link 223 * com.sun.jmx.snmp.SnmpEngineId#createEngineId(java.lang.String) } 224 * with the ability to provide your own separator. This allows IPv6 225 * address format handling (eg: providing @ as separator). 226 * @param str The string to parse. 227 * @param separator the separator to use. If null is provided, the default 228 * separator ":" is used. 229 * @return The generated engine Id or null if the passed string is null. 230 * @exception UnknownHostException if the host name contained in the 231 * textual format is unknown. 232 * @exception IllegalArgumentException when : 233 * <ul> 234 * <li>octet string lower than 5 bytes.</li> 235 * <li>octet string greater than 32 bytes.</li> 236 * <li>octet string = all zeros.</li> 237 * <li>octet string = all 'ff'H.</li> 238 * <li>octet strings with very first bit = 0 and length != 12 octets</li> 239 * <li>An IPv6 address format is used in conjonction with the ":" 240 * separator</li> 241 * </ul> 242 * @since 1.5 243 */ 244 public static SnmpEngineId createEngineId(String str, String separator) 245 throws IllegalArgumentException, UnknownHostException { 246 if(str == null) return null; 247 248 if(str.startsWith("0x") || str.startsWith("0X")) { 249 validateId(str); 250 return new SnmpEngineId(str); 251 } 252 separator = separator == null ? ":" : separator; 253 StringTokenizer token = new StringTokenizer(str, 254 separator, 255 true); 256 257 String address = null; 258 String port = null; 259 String iana = null; 260 int objPort = 161; 261 int objIana = 42; 262 InetAddress objAddress = null; 263 SnmpEngineId eng = null; 264 try { 265 //Deal with address 266 try { 267 address = token.nextToken(); 268 }catch(NoSuchElementException e) { 269 throw new IllegalArgumentException("Passed string is invalid : ["+str+"]"); 270 } 271 if(!address.equals(separator)) { 272 objAddress = InetAddress.getByName(address); 273 try { 274 token.nextToken(); 275 }catch(NoSuchElementException e) { 276 //No need to go further, no port. 277 eng = SnmpEngineId.createEngineId(objAddress, 278 objPort, 279 objIana); 280 eng.setStringValue(str); 281 return eng; 282 } 283 } 284 else 285 objAddress = InetAddress.getLocalHost(); 286 287 //Deal with port 288 try { 289 port = token.nextToken(); 290 }catch(NoSuchElementException e) { 291 //No need to go further, no port. 292 eng = SnmpEngineId.createEngineId(objAddress, 293 objPort, 294 objIana); 295 eng.setStringValue(str); 296 return eng; 297 } 298 299 if(!port.equals(separator)) { 300 objPort = Integer.parseInt(port); 301 try { 302 token.nextToken(); 303 }catch(NoSuchElementException e) { 304 //No need to go further, no iana. 305 eng = SnmpEngineId.createEngineId(objAddress, 306 objPort, 307 objIana); 308 eng.setStringValue(str); 309 return eng; 310 } 311 } 312 313 //Deal with iana 314 try { 315 iana = token.nextToken(); 316 }catch(NoSuchElementException e) { 317 //No need to go further, no port. 318 eng = SnmpEngineId.createEngineId(objAddress, 319 objPort, 320 objIana); 321 eng.setStringValue(str); 322 return eng; 323 } 324 325 if(!iana.equals(separator)) 326 objIana = Integer.parseInt(iana); 327 328 eng = SnmpEngineId.createEngineId(objAddress, 329 objPort, 330 objIana); 331 eng.setStringValue(str); 332 333 return eng; 334 335 } catch(Exception e) { 336 throw new IllegalArgumentException("Passed string is invalid : ["+str+"]. Check that the used separator ["+ separator + "] is compatible with IPv6 address format."); 337 } 338 339 } 340 341 /** 342 * Generates a unique engine Id. The engine Id unicity is based on 343 * the host IP address and port. The IP address used is the 344 * localhost one. The creation algorithm uses the SUN Microsystems IANA 345 * number (42). 346 * @param port The TCP/IP port the SNMPv3 Adaptor Server is listening to. 347 * @return The generated engine Id. 348 * @exception UnknownHostException if the local host name 349 * used to calculate the id is unknown. 350 */ 351 public static SnmpEngineId createEngineId(int port) 352 throws UnknownHostException { 353 int suniana = 42; 354 InetAddress address = null; 355 address = InetAddress.getLocalHost(); 356 return createEngineId(address, port, suniana); 357 } 358 /** 359 * Generates a unique engine Id. The engine Id unicity is based on 360 * the host IP address and port. The IP address used is the passed 361 * one. The creation algorithm uses the SUN Microsystems IANA 362 * number (42). 363 * @param address The IP address the SNMPv3 Adaptor Server is listening to. 364 * @param port The TCP/IP port the SNMPv3 Adaptor Server is listening to. 365 * @return The generated engine Id. 366 * @exception UnknownHostException. if the provided address is null. 367 */ 368 public static SnmpEngineId createEngineId(InetAddress address, int port) 369 throws IllegalArgumentException { 370 int suniana = 42; 371 if(address == null) 372 throw new IllegalArgumentException("InetAddress is null."); 373 return createEngineId(address, port, suniana); 374 } 375 376 /** 377 * Generates a unique engine Id. The engine Id unicity is based on 378 * the host IP address and port. The IP address is the localhost one. 379 * The creation algorithm uses the passed IANA number. 380 * @param port The TCP/IP port the SNMPv3 Adaptor Server is listening to. 381 * @param iana Your enterprise IANA number. 382 * @exception UnknownHostException if the local host name used to calculate the id is unknown. 383 * @return The generated engine Id. 384 */ 385 public static SnmpEngineId createEngineId(int port, int iana) throws UnknownHostException { 386 InetAddress address = null; 387 address = InetAddress.getLocalHost(); 388 return createEngineId(address, port, iana); 389 } 390 391 /** 392 * Generates a unique engine Id. The engine Id unicity is based on the host IP address and port. The IP address is the passed one, it handles IPv4 and IPv6 hosts. The creation algorithm uses the passed IANA number. 393 * @param addr The IP address the SNMPv3 Adaptor Server is listening to. 394 * @param port The TCP/IP port the SNMPv3 Adaptor Server is listening to. 395 * @param iana Your enterprise IANA number. 396 * @return The generated engine Id. 397 * @exception UnknownHostException if the provided <CODE>InetAddress </CODE> is null. 398 */ 399 public static SnmpEngineId createEngineId(InetAddress addr, 400 int port, 401 int iana) { 402 if(addr == null) throw new IllegalArgumentException("InetAddress is null."); 403 byte[] address = addr.getAddress(); 404 byte[] engineid = new byte[9 + address.length]; 405 engineid[0] = (byte) ( (iana & 0xFF000000) >> 24 ); 406 engineid[0] |= 0x80; 407 engineid[1] = (byte) ( (iana & 0x00FF0000) >> 16 ); 408 engineid[2] = (byte) ( (iana & 0x0000FF00) >> 8 ); 409 410 engineid[3] = (byte) (iana & 0x000000FF); 411 engineid[4] = 0x05; 412 413 if(address.length == 4) 414 engineid[4] = 0x01; 415 416 if(address.length == 16) 417 engineid[4] = 0x02; 418 419 for(int i = 0; i < address.length; i++) { 420 engineid[i + 5] = address[i]; 421 } 422 423 engineid[5 + address.length] = (byte) ( (port & 0xFF000000) >> 24 ); 424 engineid[6 + address.length] = (byte) ( (port & 0x00FF0000) >> 16 ); 425 engineid[7 + address.length] = (byte) ( (port & 0x0000FF00) >> 8 ); 426 engineid[8 + address.length] = (byte) ( port & 0x000000FF ); 427 428 return new SnmpEngineId(engineid); 429 } 430 431 /** 432 * Generates an engine Id based on an InetAddress. Handles IPv4 and IPv6 addresses. The creation algorithm uses the passed IANA number. 433 * @param iana Your enterprise IANA number. 434 * @param addr The IP address the SNMPv3 Adaptor Server is listening to. 435 * @return The generated engine Id. 436 * @since 1.5 437 * @exception UnknownHostException if the provided <CODE>InetAddress </CODE> is null. 438 */ 439 public static SnmpEngineId createEngineId(int iana, InetAddress addr) 440 { 441 if(addr == null) throw new IllegalArgumentException("InetAddress is null."); 442 byte[] address = addr.getAddress(); 443 byte[] engineid = new byte[5 + address.length]; 444 engineid[0] = (byte) ( (iana & 0xFF000000) >> 24 ); 445 engineid[0] |= 0x80; 446 engineid[1] = (byte) ( (iana & 0x00FF0000) >> 16 ); 447 engineid[2] = (byte) ( (iana & 0x0000FF00) >> 8 ); 448 449 engineid[3] = (byte) (iana & 0x000000FF); 450 if(address.length == 4) 451 engineid[4] = 0x01; 452 453 if(address.length == 16) 454 engineid[4] = 0x02; 455 456 for(int i = 0; i < address.length; i++) { 457 engineid[i + 5] = address[i]; 458 } 459 460 return new SnmpEngineId(engineid); 461 } 462 463 /** 464 * Generates an engine Id based on an InetAddress. Handles IPv4 and IPv6 465 * addresses. The creation algorithm uses the sun IANA number (42). 466 * @param addr The IP address the SNMPv3 Adaptor Server is listening to. 467 * @return The generated engine Id. 468 * @since 1.5 469 * @exception UnknownHostException if the provided 470 * <CODE>InetAddress</CODE> is null. 471 */ 472 public static SnmpEngineId createEngineId(InetAddress addr) { 473 return createEngineId(42, addr); 474 } 475 476 477 /** 478 * Tests <CODE>SnmpEngineId</CODE> instance equality. Two <CODE>SnmpEngineId</CODE> are equal if they have the same value. 479 * @return <CODE>true</CODE> if the two <CODE>SnmpEngineId</CODE> are equals, <CODE>false</CODE> otherwise. 480 */ 481 public boolean equals(Object a) { 482 if(!(a instanceof SnmpEngineId) ) return false; 483 return hexString.equals(((SnmpEngineId) a).toString()); 484 } 485 486 public int hashCode() { 487 return hexString.hashCode(); 488 } 489 }