net.padlocksoftware.padlock.DefaultMacAddressProvider.java Source code

Java tutorial

Introduction

Here is the source code for net.padlocksoftware.padlock.DefaultMacAddressProvider.java

Source

/*
* Copyright (c) 2009-2012 Jason Nichols
    
* Permission is hereby granted, free of charge, to any person obtaining a copy 
* of this software and associated documentation files (the "Software"), to deal 
* in the Software without restriction, including without limitation the rights to 
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
* of the Software, and to permit persons to whom the Software is furnished to do 
* so, subject to the following conditions:
    
* The above copyright notice and this permission notice shall be included in all 
* copies or substantial portions of the Software.
    
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
* SOFTWARE.
*/

package net.padlocksoftware.padlock;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.String;
import java.lang.reflect.Method;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Hex;

/**
 *
 * @author Jason Nichols (jason@padlocksoftware.net)
 * @since 2.1
 */
public class DefaultMacAddressProvider implements MacAddressProvider {

    ///////////////////////////// Class Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    private static final Logger logger = Logger.getLogger(DefaultMacAddressProvider.class.getName());

    private static final Map<String, byte[]> DEFAULT_VIRTUAL_ADDRESSES = new ConcurrentHashMap<String, byte[]>();

    static {

        // VMWare Host
        byte[] vmwareHost = { 0x00, 0x50, 0x56 };
        DEFAULT_VIRTUAL_ADDRESSES.put("VmWare Host", vmwareHost);

        // VMWare Client
        byte[] vmwareClient = { 0x00, 0x0C, 0x29 };
        DEFAULT_VIRTUAL_ADDRESSES.put("VmWare Client", vmwareClient);

        // VirtualBox host
        byte[] virtualBoxHost = { 0x0a, 0x00, 0x27, 0x00, 0x00, 0x00 };
        DEFAULT_VIRTUAL_ADDRESSES.put("VirtualBox Host", virtualBoxHost);
    }

    // do we force using the shell command?
    private static final boolean FORCE_MACADDR_SHELL = true;

    private enum Platform {

        Unknown(null), Windows("ipconfig /all"), Linux("/sbin/ifconfig"), MacOS("ifconfig"), Solaris(
                "/usr/sbin/arp"), Bsd("ifconfig");
        String command;

        private Platform(String command) {
            this.command = command;
        }
    }

    static final Platform PLATFORM;

    static {
        String os = System.getProperty("os.name").trim().toLowerCase();
        if (os.indexOf("windows") >= 0) {
            PLATFORM = Platform.Windows;
        } else if (os.indexOf("linux") >= 0) {
            PLATFORM = Platform.Linux;
        } else if (os.indexOf("mac os x") >= 0 || os.indexOf("macosx") >= 0) {
            PLATFORM = Platform.MacOS;
        } else if (os.indexOf("solaris") >= 0 || os.indexOf("sunos") >= 0) {
            PLATFORM = Platform.Solaris; // untested
        } else if (os.indexOf("bsd") >= 0) {
            PLATFORM = Platform.Bsd; // untested
        } else {
            PLATFORM = Platform.Unknown;
        }
    }

    ////////////////////////////// Class Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    //////////////////////////////// Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    private final Object lock = new Object();

    private final Map<String, byte[]> virtualAddresses;

    // Flag to permit likely VM addresses as real.
    private volatile boolean permitVMAddresses = false;

    /////////////////////////////// Constructors \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    public DefaultMacAddressProvider() {
        virtualAddresses = new ConcurrentHashMap<String, byte[]>();
        for (Entry<String, byte[]> entry : DEFAULT_VIRTUAL_ADDRESSES.entrySet()) {
            byte[] addr = new byte[entry.getValue().length];
            for (int x = 0; x < entry.getValue().length; x++)
                addr[x] = entry.getValue()[x];
            virtualAddresses.put(entry.getKey(), addr);
        }

    }

    ////////////////////////////////// Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    //------------------------ Implements: MacAddressProvider

    public Set<String> getSystemMacAddresses() {
        synchronized (lock) {
            return getMACAddresses();
        }
    }

    public String getName() {
        return this.getClass().getName();
    }

    public String getVersion() {
        return VersionInfo.getVersionString();
    }

    //------------------------ Overrides:

    //---------------------------- Abstract Methods -----------------------------

    //---------------------------- Utility Methods ------------------------------

    private boolean isVirtualAddress(byte[] address) {

        // Loop through the virtual mac database to determine if this is a physical
        // interface address.
        for (Entry<String, byte[]> entry : virtualAddresses.entrySet()) {
            String name = entry.getKey();
            byte[] data = entry.getValue();

            int matchCount = 0;

            for (int x = 0; x < Math.min(data.length, address.length); x++) {
                if (data[x] == address[x]) {
                    matchCount++;
                }
            }

            if (matchCount == data.length) {
                logger.log(Level.FINE, "Address {0} matches {1}, ignoring",
                        new Object[] { new String(Hex.encodeHex(address)), name });
                return true;
            }
        }

        return false;
    }

    private Set<String> getMACAddresses() {
        Set<String> addresses = new HashSet<String>();

        if (!FORCE_MACADDR_SHELL) {
            try {
                Method method = NetworkInterface.class.getMethod("getHardwareAddress", (Class[]) null);

                Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces();
                if (ifs != null) {
                    while (ifs.hasMoreElements()) {
                        NetworkInterface iface = ifs.nextElement();
                        byte[] hardware = (byte[]) method.invoke(iface);
                        if (hardware != null && hardware.length == 6 && hardware[1] != (byte) 0xff) {
                            if (permitVMAddresses || !isVirtualAddress(hardware)) {
                                addresses.add(new String(Hex.encodeHex(hardware)));
                            }
                        }
                    }
                }
            } catch (Exception ex) {
                logger.log(Level.FINE, null, ex);
            }

            // may not have found any
            if (!addresses.isEmpty()) {
                return addresses;
            }
        }

        // otherwise default to JDK1.5 way
        return useShellToFindAddresses();
    }

    private Set<String> useShellToFindAddresses() {
        logger.fine("Platform: " + PLATFORM);

        if (PLATFORM == Platform.Unknown) {
            logger.fine("Could not determine host platform, no Network Interfaces found.");
            return null;
        }
        BufferedReader reader = null;
        try {
            Process conf = Runtime.getRuntime().exec(PLATFORM.command);
            reader = new BufferedReader(new InputStreamReader(conf.getInputStream()));

            Set<String> macs = new HashSet<String>(4);
            String regex = "[a-f0-9]{2}([:-][a-f0-9]{2}){5}";
            Pattern pattern = Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE);
            String line = reader.readLine();

            do {
                logger.finer("Found line: " + line);
                Matcher matcher = pattern.matcher(line);
                while (matcher.find()) {
                    String address = matcher.group(0);
                    address = address.trim().replaceAll("[:-]", "");
                    logger.fine("Found address: " + address);
                    if (address.length() == 11)
                        address = "0" + address;
                    if (address.length() == 12) {
                        byte[] hex = Hex.decodeHex(address.toCharArray());
                        if (permitVMAddresses || !isVirtualAddress(hex)) {
                            macs.add(address);
                        } else
                            logger.fine("Found blacklisted address: " + address);
                    } else
                        logger.fine("Found bogus address: " + address);
                }
                line = reader.readLine();
            } while (line != null);
            return Collections.unmodifiableSet(macs);
        } catch (Throwable t) {
            logger.log(Level.FINE, null, t);
            return Collections.emptySet();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.log(Level.FINE, null, e);
                }
            }
        }
    }

    //---------------------------- Property Methods -----------------------------

    /**
     * Enable or disable the use of known VM MAC addresses.
     * 
     * @param permit If true, addresses that match the VM regexes will be
     * counted as a hardware MAC.
     */
    public void setPermitVMAddresses(boolean permit) {
        permitVMAddresses = permit;
    }

    /**
     * Returns the status of the permitVMAddressesflag.
     * @return True if VM addresses are permitted as valid hardware addresses,
     * or false if not.
     */
    public boolean permitVMAddresses() {
        return permitVMAddresses;
    }

    /**
     * Get the underlying Map used to store the VM address table.  The key of the
     * Map is a string description of the value (aka "VirtualBox default").  The
     * byte array is a variable length set of values that match an address group.
     * For example {0x00, 0x50, 0x56} is the default array value for a VMWare
     * host MAC address.
     * @return
     */
    public Map<String, byte[]> getVirtualAddressesMap() {
        return virtualAddresses;
    }
}