/*----------------------------------------------------------------
* $Id: EthernetAddress.java,v 1.2 2001/10/11 21:13:28 pkb Exp $
*
* (c)2001 - Paul Blankenbaker
*
* Revision Log
*
* $Log: EthernetAddress.java,v $
* Revision 1.2 2001/10/11 21:13:28 pkb
* Changed organization of native code - moved binaries to
* $COMHOME/native/OS/ARCH directories
*
* Revision 1.1 2001/10/04 19:42:33 pkb
*
* Added package related to Ethernet addresses (turns out to be a
* non-trivial exercise to get a ethernet address in a cross platform
* manner). Currently uses native code (as I don't know of another way)
* and supports Windows, Linux, Solaris. Tested on (Windows 98, RedHat
* 7.1 and Solaris 8).
*
*
*/
//----------------------------------------------------------------
package com.ccg.net.ethernet;
import java.util.*;
//----------------------------------------------------------------
/** Manage ethernet address objects and provide a means to determine
* the ethernet address of the machine the JVM is running on.
*
* <p>This class is used to examine (work with) ethernet addresses. It
* was primarily created to provide a means to determine the ethernet
* address(es) of the local machine (which turned out to be a
* non-trivial project).
*
* <p><b>IMPORTANT INSTALLATION INSTRUCTIONS</b></p>
*
* <p>This class relies on native code when determining the ethernet
* address. Because of this, a shared library module needs to be
* installed BEFORE you will be able to use the methods in this class
* related to the local ethernet address of the machine.
*
* <p>To do the installation, you need to:
*
* <ul>
* <li>Determine which shared library module you need.
* <li>Copy the shared library module to its final location.
* </ul>
*
* <p>It is important to note that the shared libraries need to be
* copied to a location that is within the library search path for
* your environment. I've found that the $(JREHOME)/bin directory
* tends to always be in the search path (at least for
* Linux/Windows). For Sun's JRE installation, look for
* $(JREHOME)/lib/ARCH (like "/opt/jdk/jre/lib/sparc"). If you are
* unable to copy the library to this location, you may need to update
* your library search path before executing code.
*
* <p>The source code for each of the libraries is available, however,
* it is often easier not to have to locate a compiler and simply use
* one of the pre-compiled binary files. The following binary files
* are available:
*
* <dl>
*
* <dt><b>$COMHOME/ccg/native/linux/x86/libEthernetAddress.so</b></dt><dd>This
* library is intended for use on Intel x86 based Linux
* platforms. This file needs to be installed within your shared
* library search path with a final name of "libEthernetAddress.so". A
* developer can typically install this library with the following
* command (as root):
*
* <pre>
* cp $COMHOME/ccg/native/linux/x86/libEthernetAddress.so \
* $JREHOME/bin/libEthernetAddress.so</pre></dd>
*
* <dt><b>$COMHOME/ccg/native/solaris/sparc/libEthernetAddress.so</b></dt><dd>This
* library is intended for use on Sparc based Solaris platforms. This
* file needs to be installed within your shared library search path
* with a final name of "libEthernetAddress.so". A developer can
* typically install this library with the following command (as
* root):
*
* <pre>
* cp $COMHOME/ccg/native/solaris/sparc/libEthernetAddress.so \
* $JREHOME/lib/sparc/libEthernetAddress.so</pre></dd>
*
* <dt><b>$COMHOME/native/win/x86/EthernetAddress.dll</b></dt><dd>This
* library is intended for use on Intel x86 based Windows
* platforms. This file needs to be installed within your shared
* library search path with a final name of "EthernetAddress.dll". If
* you put this file in the same directory as your "java.exe" file, it
* seems to be found. A developer can typically install this library
* with the following command:
*
* <pre>
* copy %COMHOME%/ccg/native/win/x86/EthernetAddress.dll \
* %JREHOME%/bin/EthernetAddress.dll</pre></dd>
*
* </dl>
*
* <p><b>Developer Notes:</b></p>
*
* <p>If you need to add support for additional platforms (such as a
* Mac/Beos/etc), you should take one of the source 'C' files (like
* EthernetAddress_linux.c) as your starting point and create a new
* 'C' source file for the native platform you'd like to support.
*
* @version $Revision: 1.2 $
*
* @since 1.0
*
* @author $Author: pkb $
*
* @see #getPrimaryAdapter
* @see <a href=doc-files/PrintMAC.java>PrintMAC.java</a> */
//----------------------------------------------------------------
public final class EthernetAddress {
//----------------------------------------------------------------
/** Native method to look up the ethernet address for a adapter.
*
* @param i
*
* ID of the next ethernet address you want to check.
*
* @param ea
*
* Byte array which is at least 6 bytes long to store the
* ethernet address in.
*
* @return
*
* true if able to determine address for adapter, false if not.
*
* @since 1.0 */
//----------------------------------------------------------------
private static native boolean getLocalEthernet(int i, byte[] ea);
//----------------------------------------------------------------
/** Tries to create a EthernetAddress object for adapter N.
*
* @param n
*
* ID of adapter you want to get address of (start at 0).
*
* @return
*
* EthernetAddress object if able to determine, or null if not.
*
* @since 1.0 */
//----------------------------------------------------------------
private static EthernetAddress getLocalEthernetAddress(int i) {
// load native code
// load ALL adapters we can find on the system
byte[] ea = new byte[6];
if (!getLocalEthernet(i,ea)) return null;
return fromBytes(ea);
}
//----------------------------------------------------------------
/** Check to see if all bytes of the ethernet address are zero.
*
* <p>This method checks all of the bytes of a ethernet address to
* see if they are zero. If they are, then the ethernet address is
* "0:0:0:0:0:0", which we consider the "null" ethernet address.
*
* @return
*
* true if all bytes of the ethernet address are 0.
*
* @since 1.0
*
* @see #NULL */
//----------------------------------------------------------------
public boolean isNull() {
for (int i = 0; i < _Bytes.length; i++) if (_Bytes[i] != 0) return false;
return true;
}
//----------------------------------------------------------------
/** Constant ethernet address object which has the "null address".
*
* <p>This constant can be used when you want a non-null
* EthernetAddress object reference, but want a invalid (or null)
* ethernet address contained.
*
* <p>The {@link #isNull isNull()} method will ALWAYS return true
* for this constant.
*
* @serial
*
* @since 1.0
*
* @see #isNull */
//----------------------------------------------------------------
public static final EthernetAddress NULL=new EthernetAddress();
//----------------------------------------------------------------
/** Try to determine the primary ethernet address of the machine.
*
* <p>This method will try to return the primary ethernet address of
* the machine. In order for this to succeed:
*
* <ul>
*
* <li>The necessary native library must be installed (as
* described in the {@link EthernetAddress class overview}.
*
* <li>The native code must find at least one ethernet address for
* the system.
*
* </ul>
*
* @throws UnsatisfiedLinkError
*
* This exception is thrown if we are unable to load the native
* library (like: libEthernetAddress.so or EthernetAddress.dll)
* which is required to query the system for the ethernet
* address.
*
* @return
*
* Ethernet address of the machine if able to determine/guess -
* otherwise null.
*
* @since 1.0
*
* @see #getAllAdapters */
//----------------------------------------------------------------
public static EthernetAddress getPrimaryAdapter()
throws UnsatisfiedLinkError {
return getLocalEthernetAddress(0);
}
//----------------------------------------------------------------
/** Get all of the ethernet addresses associated with the local machine.
*
* <p>This method will try and find ALL of the ethernet adapters
* which are currently available on the system. This is heavily OS
* dependent and may not be supported on all platforms. When not
* supported, you should still get back a collection with the {@link
* #getPrimaryAdapter primary adapter} in it.
*
* @throws UnsatisfiedLinkError
*
* This exception is thrown if we are unable to load the native
* library (like: libEthernetAddress.so or EthernetAddress.dll)
* which is required to query the system for the ethernet
* address.
*
* @return
*
* Array of all ethernet adapters (never returns null, but may
* return a 0 length array if no adapters could be found).
*
* @see #getPrimaryAdapter */
//----------------------------------------------------------------
public static Collection getAllAdapters()
throws UnsatisfiedLinkError {
// allocate vector to hold info
Vector av = new Vector();
EthernetAddress ea=null;
for (int i = 0; (ea = getLocalEthernetAddress(i)) != null; i++) {
av.addElement(ea);
}
return av;
}
//----------------------------------------------------------------
/** Constructs object with "null values" (address of "0:0:0:0:0:0").
*
* @since 1.0 */
//----------------------------------------------------------------
EthernetAddress() {
_Bytes = new byte[6];
}
//----------------------------------------------------------------
/** Holds the binary ID of your ethernet adapter.
*
* @serial
*
* @since 1.0 */
//----------------------------------------------------------------
private byte[] _Bytes;
//----------------------------------------------------------------
/** Set the binary ID of your ethernet adapter.
*
* @param val
*
* New byte[] value to assign.
*
* @see #getBytes */
//----------------------------------------------------------------
public static EthernetAddress fromBytes(byte[] val)
throws BadAddressException {
if (val == null || val.length != 6) {
throw new BadAddressException("ethernet address not 6 bytes long");
}
EthernetAddress ea = new EthernetAddress();
for (int i = 0; i < val.length; i++) ea._Bytes[i] = val[i];
return ea;
}
//----------------------------------------------------------------
/** Get the binary ID of your ethernet adapter.
*
* @return
*
* Copy of the current byte[] value assigned.
*
* @see #fromBytes */
//----------------------------------------------------------------
public byte[] getBytes() {
return (byte[]) _Bytes.clone();
}
//----------------------------------------------------------------
/** Parse a ethernet address object from a string.
*
* <p>Ethernet addresses are typically shown as 6 hexadecimal values
* (range: [0,ff]) separated by colons. They have the form:
*
* <pre>
* x:x:x:x:x:x
* </pre>
*
* <p>This method is fairly lenient in its parsing. It allows any
* character (and omission) of the separator (shown above). And each
* hex value may be one or two digits long and upper or lower case.
*
* <p>The following shows several different ways to list the same
* ethernet address:
*
* <pre>
* 00:E0:98:06:92:0E
* 0:e0:98:6:92:e
* 0-e0-98 6-92-e
* 00e0980692e0
* </pre>
*
* @param sval
* String value to try and parse a ethernet address from (must
* not be null).
*
* @throws BadAddressException
* If we could not parse a ethernet address from the string you
* passed.
*
* @see #toString */
//----------------------------------------------------------------
public static EthernetAddress fromString(String sval)
throws BadAddressException {
byte[] eab = new byte[6];
int ei = 0;
boolean needHiNyb = true;
boolean lastWasSep = true;
int val = -1;
int slen = sval.length();
for (int i = 0; i < slen; i++) {
char c = sval.charAt(i);
int cval = Character.digit(c,16);
if (cval == -1) { // if not hex digit
if (lastWasSep) { // if last char was separator
ei = 0; // reset to zero bytes
}
else if (val != -1) { // if we have value to store
if (ei >= eab.length) {
throw new BadAddressException("too many bytes in \""+sval+"\"");
}
eab[ei++] = (byte) val;
val = -1;
needHiNyb = true;
}
}
else { // got hex digit
lastWasSep = false;
if (needHiNyb) { // if need hi-nybble, save value
val = cval;
needHiNyb = false;
}
else { // if lo-nybble, then update array
val <<= 4;
val += cval;
needHiNyb = true;
if (ei >= eab.length) {
throw new BadAddressException("too many bytes in \""+sval+"\"");
}
eab[ei++] = (byte) val;
val = -1;
}
}
}
// if last byte value is single digit,
// catch it here outside of loop
if ((val != -1) && !needHiNyb) {
if (ei >= eab.length) {
throw new BadAddressException("too many bytes in \""+sval+"\"");
}
eab[ei++] = (byte) val;
}
if (ei != eab.length) {
throw new BadAddressException("not enough bytes in \""+sval+"\"");
}
EthernetAddress ea = new EthernetAddress();
ea._Bytes = eab;
return ea;
}
//----------------------------------------------------------------
/** Get a hash code for the object.
*
* <p>This method obeys the hash code contract and returns a hash
* value that will try to be random, but will be identical for
* objects which are {@link #equals equal}.
*
* @return
*
* A reasonable hash code for the object.
*
* @since 1.0 */
//----------------------------------------------------------------
public int hashCode() {
int blen = _Bytes.length;
if (blen == 0) return 0;
int hc = _Bytes[0];
for (int i = 1; i < blen; i++) {
hc *= 37;
hc += _Bytes[i];
}
return hc;
}
//----------------------------------------------------------------
/** Determine if two ethernet address objects are "equal".
*
* @param o
*
* Other object to compare to (you can pass null).
*
* @return
*
* true if two objects have same Ethernet address, false if not.
*
* @since 1.0 */
//----------------------------------------------------------------
public boolean equals(Object o) {
if (!(o instanceof EthernetAddress)) return false;
byte[] bao = ((EthernetAddress) o)._Bytes;
if (bao.length != _Bytes.length) return false;
for (int i = 0; i < bao.length; i++) if (bao[i] != _Bytes[i]) return false;
return true;
}
//----------------------------------------------------------------
/** Get the string representation of the ethernet address.
*
* @return
*
* String representation of ehternet address in form:
* "xx:xx:xx:xx:xx:xx".
*
* @see #fromString */
//----------------------------------------------------------------
public String toString() {
int blen = _Bytes.length;
StringBuffer sb = new StringBuffer(blen*3);
for (int i = 0; i < blen; i++) {
int lo = _Bytes[i];
int hi = ((lo >> 4) & 0xF);
lo &= 0xF;
if (i != 0) sb.append(':');
sb.append(Character.forDigit(hi,16));
sb.append(Character.forDigit(lo,16));
}
return sb.toString();
}
//----------------------------------------------------------------
// Static class method to load native library first time class is
// loaded
//----------------------------------------------------------------
/* 08-Sep-2002, TSa: Commented out to allow for alternative
* dynamic library loading (loading is handled from outside this
* class now, to allow dynamically choosing the correct lib as well
* as catching possible exceptions)
*/
/*
static {
try {
System.loadLibrary("EthernetAddress");
} catch (Throwable t) {
com.ccg.util.Log.error("problem loading \"EthernetAddress"+
"\" native library",t);
}
}
*/
}